Skip to content
This repository was archived by the owner on Jan 24, 2023. It is now read-only.

Commit 64372f3

Browse files
committed
Upgrade webext-domain-permission-toggle.js and webext-dynamic-content-scripts.js
1 parent 4d75163 commit 64372f3

File tree

2 files changed

+289
-234
lines changed

2 files changed

+289
-234
lines changed

webext-domain-permission-toggle.js

Lines changed: 214 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,221 @@
1-
/* https://github.com/fregante/webext-domain-permission-toggle @ v1.0.1 */
1+
/* https://github.com/fregante/webext-domain-permission-toggle @ v2.1.0 */
22

33
var addDomainPermissionToggle = (function () {
4-
'use strict';
4+
'use strict';
55

6-
async function getManifestPermissions() {
7-
return getManifestPermissionsSync();
8-
}
9-
function getManifestPermissionsSync() {
10-
var _a, _b;
11-
const manifest = chrome.runtime.getManifest();
12-
const manifestPermissions = {
13-
origins: [],
14-
permissions: []
15-
};
16-
const list = new Set([
17-
...((_a = manifest.permissions) !== null && _a !== void 0 ? _a : []),
18-
...((_b = manifest.content_scripts) !== null && _b !== void 0 ? _b : []).flatMap(config => { var _a; return (_a = config.matches) !== null && _a !== void 0 ? _a : []; })
19-
]);
20-
for (const permission of list) {
21-
if (permission.includes('://')) {
22-
manifestPermissions.origins.push(permission);
23-
}
24-
else {
25-
manifestPermissions.permissions.push(permission);
26-
}
27-
}
28-
return manifestPermissions;
29-
}
6+
function NestedProxy(target) {
7+
return new Proxy(target, {
8+
get(target, prop) {
9+
if (typeof target[prop] !== 'function') {
10+
return new NestedProxy(target[prop]);
11+
}
12+
return (...arguments_) =>
13+
new Promise((resolve, reject) => {
14+
target[prop](...arguments_, result => {
15+
if (chrome.runtime.lastError) {
16+
reject(new Error(chrome.runtime.lastError.message));
17+
} else {
18+
resolve(result);
19+
}
20+
});
21+
});
22+
}
23+
});
24+
}
25+
const chromeP =
26+
typeof window === 'object' &&
27+
(window.browser || new NestedProxy(window.chrome));
3028

31-
const contextMenuId = 'webext-domain-permission-toggle:add-permission';
32-
let currentTabId;
33-
let globalOptions;
34-
async function p(fn, ...args) {
35-
return new Promise((resolve, reject) => {
36-
fn(...args, result => {
37-
if (chrome.runtime.lastError) {
38-
reject(chrome.runtime.lastError);
39-
}
40-
else {
41-
resolve(result);
42-
}
43-
});
44-
});
45-
}
46-
async function isOriginPermanentlyAllowed(origin) {
47-
return p(chrome.permissions.contains, {
48-
origins: [
49-
origin + '/*'
50-
]
51-
});
52-
}
53-
function createMenu() {
54-
chrome.contextMenus.remove(contextMenuId, () => chrome.runtime.lastError);
55-
chrome.contextMenus.create({
56-
id: contextMenuId,
57-
type: 'checkbox',
58-
checked: false,
59-
title: globalOptions.title,
60-
contexts: [
61-
'page_action',
62-
'browser_action'
63-
],
64-
documentUrlPatterns: [
65-
'http://*/*',
66-
'https://*/*'
67-
]
68-
});
69-
}
70-
function updateItem({ tabId }) {
71-
chrome.tabs.executeScript(tabId, {
72-
code: 'location.origin'
73-
}, async ([origin] = []) => {
74-
const settings = {
75-
checked: false,
76-
enabled: true
77-
};
78-
if (!chrome.runtime.lastError && origin) {
79-
const manifestPermissions = await getManifestPermissions();
80-
const isDefault = manifestPermissions.origins.some(permission => permission.startsWith(origin));
81-
settings.enabled = !isDefault;
82-
settings.checked = isDefault || await isOriginPermanentlyAllowed(origin);
83-
}
84-
chrome.contextMenus.update(contextMenuId, settings);
85-
});
86-
}
87-
async function handleClick({ wasChecked, menuItemId }, tab) {
88-
if (menuItemId !== contextMenuId || !tab) {
89-
return;
90-
}
91-
try {
92-
const successful = await p(wasChecked ? chrome.permissions.remove : chrome.permissions.request, {
93-
origins: [
94-
new URL(tab.url).origin + '/*'
95-
]
96-
});
97-
if (wasChecked && successful) {
98-
chrome.contextMenus.update(contextMenuId, {
99-
checked: false
100-
});
101-
}
102-
if (!wasChecked && successful && globalOptions.reloadOnSuccess) {
103-
chrome.tabs.executeScript({
104-
code: `confirm(${JSON.stringify(globalOptions.reloadOnSuccess)}) && location.reload()`
105-
});
106-
}
107-
}
108-
catch (error) {
109-
console.error(error.message);
110-
alert(`Error: ${error.message}`);
111-
updateItem({ tabId: tab.id });
112-
}
113-
}
114-
function addDomainPermissionToggle(options) {
115-
if (globalOptions) {
116-
throw new Error('webext-domain-permission-toggle can only be initialized once');
117-
}
118-
const { name } = chrome.runtime.getManifest();
119-
globalOptions = { title: `Enable ${name} on this domain`,
120-
reloadOnSuccess: `Do you want to reload this page to apply ${name}?`, ...options };
121-
chrome.contextMenus.onClicked.addListener(handleClick);
122-
chrome.tabs.onActivated.addListener(updateItem);
123-
chrome.tabs.onUpdated.addListener((tabId, { status }) => {
124-
if (currentTabId === tabId && status === 'complete') {
125-
updateItem({ tabId });
126-
}
127-
});
128-
createMenu();
129-
}
29+
const patternValidationRegex = /^(https?|wss?|file|ftp|\*):\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^file:\/\/\/.*$|^resource:\/\/(\*|\*\.[^*/]+|[^*/]+)\/.*$|^about:/;
30+
const isFirefox = typeof navigator === 'object' && navigator.userAgent.includes('Firefox/');
31+
function getRawRegex(matchPattern) {
32+
if (!patternValidationRegex.test(matchPattern)) {
33+
throw new Error(matchPattern + ' is an invalid pattern, it must match ' + String(patternValidationRegex));
34+
}
35+
let [, protocol, host, pathname] = matchPattern.split(/(^[^:]+:[/][/])([^/]+)?/);
36+
protocol = protocol
37+
.replace('*', isFirefox ? '(https?|wss?)' : 'https?')
38+
.replace(/[/]/g, '[/]');
39+
host = (host !== null && host !== void 0 ? host : '')
40+
.replace(/^[*][.]/, '([^/]+.)*')
41+
.replace(/^[*]$/, '[^/]+')
42+
.replace(/[.]/g, '[.]')
43+
.replace(/[*]$/g, '[^.]+');
44+
pathname = pathname
45+
.replace(/[/]/g, '[/]')
46+
.replace(/[.]/g, '[.]')
47+
.replace(/[*]/g, '.*');
48+
return '^' + protocol + host + '(' + pathname + ')?$';
49+
}
50+
function patternToRegex(...matchPatterns) {
51+
if (matchPatterns.includes('<all_urls>')) {
52+
return /^(https?|file|ftp):[/]+/;
53+
}
54+
return new RegExp(matchPatterns.map(getRawRegex).join('|'));
55+
}
13056

131-
return addDomainPermissionToggle;
57+
const isExtensionContext = typeof chrome === 'object' && chrome && typeof chrome.extension === 'object';
58+
const globalWindow = typeof window === 'object' ? window : undefined;
59+
const isWeb = typeof location === 'object' && location.protocol.startsWith('http');
60+
function isBackgroundPage() {
61+
var _a, _b;
62+
return isExtensionContext && (location.pathname === '/_generated_background_page.html' ||
63+
((_b = (_a = chrome.extension) === null || _a === void 0 ? void 0 : _a.getBackgroundPage) === null || _b === void 0 ? void 0 : _b.call(_a)) === globalWindow);
64+
}
65+
66+
function getManifestPermissionsSync() {
67+
return _getManifestPermissionsSync(chrome.runtime.getManifest());
68+
}
69+
function _getManifestPermissionsSync(manifest) {
70+
var _a, _b;
71+
const manifestPermissions = {
72+
origins: [],
73+
permissions: []
74+
};
75+
const list = new Set([
76+
...((_a = manifest.permissions) !== null && _a !== void 0 ? _a : []),
77+
...((_b = manifest.content_scripts) !== null && _b !== void 0 ? _b : []).flatMap(config => { var _a; return (_a = config.matches) !== null && _a !== void 0 ? _a : []; })
78+
]);
79+
for (const permission of list) {
80+
if (permission.includes('://')) {
81+
manifestPermissions.origins.push(permission);
82+
}
83+
else {
84+
manifestPermissions.permissions.push(permission);
85+
}
86+
}
87+
return manifestPermissions;
88+
}
89+
90+
const isFirefox$1 = typeof navigator === 'object' && navigator.userAgent.includes('Firefox/');
91+
const contextMenuId = 'webext-domain-permission-toggle:add-permission';
92+
let globalOptions;
93+
async function executeCode(tabId, function_, ...args) {
94+
return chromeP.tabs.executeScript(tabId, {
95+
code: `(${function_.toString()})(...${JSON.stringify(args)})`
96+
});
97+
}
98+
async function isOriginPermanentlyAllowed(origin) {
99+
return chromeP.permissions.contains({
100+
origins: [origin + '/*']
101+
});
102+
}
103+
async function getTabUrl(tabId) {
104+
if (isFirefox$1) {
105+
const [url] = await executeCode(tabId, () => location.href);
106+
return url;
107+
}
108+
const tab = await chromeP.tabs.get(tabId);
109+
return tab.url;
110+
}
111+
async function updateItem(url) {
112+
const settings = {
113+
checked: false,
114+
enabled: true
115+
};
116+
if (url) {
117+
const origin = new URL(url).origin;
118+
const manifestPermissions = getManifestPermissionsSync();
119+
const isDefault = patternToRegex(...manifestPermissions.origins).test(origin);
120+
settings.enabled = !isDefault;
121+
settings.checked = isDefault || await isOriginPermanentlyAllowed(origin);
122+
}
123+
chrome.contextMenus.update(contextMenuId, settings);
124+
}
125+
async function togglePermission(tab, toggle) {
126+
const safariError = 'The browser didn\'t supply any information about the active tab.';
127+
if (!tab.url && toggle) {
128+
throw new Error(`Please try again. ${safariError}`);
129+
}
130+
if (!tab.url && !toggle) {
131+
throw new Error(`Couldn't disable the extension on the current tab. ${safariError}`);
132+
}
133+
const permissionData = {
134+
origins: [
135+
new URL(tab.url).origin + '/*'
136+
]
137+
};
138+
if (!toggle) {
139+
void chromeP.permissions.remove(permissionData);
140+
return;
141+
}
142+
const userAccepted = await chromeP.permissions.request(permissionData);
143+
if (!userAccepted) {
144+
chrome.contextMenus.update(contextMenuId, {
145+
checked: false
146+
});
147+
return;
148+
}
149+
if (globalOptions.reloadOnSuccess) {
150+
void executeCode(tab.id, (message) => {
151+
if (confirm(message)) {
152+
location.reload();
153+
}
154+
}, globalOptions.reloadOnSuccess);
155+
}
156+
}
157+
async function handleTabActivated({ tabId }) {
158+
void updateItem(await getTabUrl(tabId).catch(() => ''));
159+
}
160+
async function handleClick({ checked, menuItemId }, tab) {
161+
if (menuItemId !== contextMenuId) {
162+
return;
163+
}
164+
try {
165+
await togglePermission(tab, checked);
166+
}
167+
catch (error) {
168+
if (tab === null || tab === void 0 ? void 0 : tab.id) {
169+
try {
170+
await executeCode(tab.id, 'alert' ,
171+
String(error instanceof Error ? error : new Error(error.message)));
172+
}
173+
catch (_a) {
174+
alert(error);
175+
}
176+
void updateItem();
177+
}
178+
throw error;
179+
}
180+
}
181+
function addDomainPermissionToggle(options) {
182+
if (!isBackgroundPage()) {
183+
throw new Error('webext-domain-permission-toggle can only be called from a background page');
184+
}
185+
if (globalOptions) {
186+
throw new Error('webext-domain-permission-toggle can only be initialized once');
187+
}
188+
const { name, optional_permissions } = chrome.runtime.getManifest();
189+
globalOptions = {
190+
title: `Enable ${name} on this domain`,
191+
reloadOnSuccess: `Do you want to reload this page to apply ${name}?`,
192+
...options
193+
};
194+
if (!chrome.contextMenus) {
195+
throw new Error('webext-domain-permission-toggle requires the `contextMenu` permission');
196+
}
197+
const optionalHosts = optional_permissions === null || optional_permissions === void 0 ? void 0 : optional_permissions.filter(permission => /<all_urls>|\*/.test(permission));
198+
if (!optionalHosts || optionalHosts.length === 0) {
199+
throw new TypeError('webext-domain-permission-toggle some wildcard hosts to be specified in `optional_permissions`');
200+
}
201+
chrome.contextMenus.remove(contextMenuId, () => chrome.runtime.lastError);
202+
chrome.contextMenus.create({
203+
id: contextMenuId,
204+
type: 'checkbox',
205+
checked: false,
206+
title: globalOptions.title,
207+
contexts: ['page_action', 'browser_action'],
208+
documentUrlPatterns: optionalHosts
209+
});
210+
chrome.contextMenus.onClicked.addListener(handleClick);
211+
chrome.tabs.onActivated.addListener(handleTabActivated);
212+
chrome.tabs.onUpdated.addListener(async (tabId, { status }, { url, active }) => {
213+
if (active && status === 'complete') {
214+
void updateItem(url !== null && url !== void 0 ? url : await getTabUrl(tabId).catch(() => ''));
215+
}
216+
});
217+
}
218+
219+
return addDomainPermissionToggle;
132220

133221
}());

0 commit comments

Comments
 (0)