talkdesk-useragent-switcher/extension/common.js

368 lines
11 KiB
JavaScript
Raw Normal View History

/* globals UAParser */
2017-09-13 03:18:12 -07:00
'use strict';
2019-07-31 23:53:48 -07:00
const cache = {};
const tabs = {};
2018-08-16 03:14:11 -07:00
chrome.tabs.onRemoved.addListener(id => delete cache[id]);
chrome.tabs.onCreated.addListener(tab => tabs[tab.id] = tab.windowId);
2017-09-13 03:18:12 -07:00
2019-07-31 23:53:48 -07:00
const prefs = {
ua: '',
blacklist: [],
whitelist: [],
custom: {},
2018-08-16 03:14:11 -07:00
mode: 'blacklist',
2019-07-31 23:53:48 -07:00
color: '#777',
2018-11-04 01:37:09 -08:00
cache: true,
2019-06-25 00:43:08 -07:00
exactMatch: false,
protected: ['google.com/recaptcha', 'gstatic.com/recaptcha']
};
chrome.storage.local.get(prefs, ps => {
Object.assign(prefs, ps);
2018-08-16 03:14:11 -07:00
chrome.tabs.query({}, ts => {
ts.forEach(t => tabs[t.id] = t.windowId);
ua.update();
});
if (chrome.browserAction.setBadgeBackgroundColor) { // FF for Android
chrome.browserAction.setBadgeBackgroundColor({
color: prefs.color
});
}
// context menu
chrome.contextMenus.create({
id: 'blacklist',
title: 'Switch to "black-list" mode',
contexts: ['browser_action'],
type: 'radio',
checked: prefs.mode === 'blacklist'
2019-03-13 01:27:20 -07:00
}, () => chrome.runtime.lastError);
chrome.contextMenus.create({
id: 'whitelist',
title: 'Switch to "white-list" mode',
contexts: ['browser_action'],
type: 'radio',
checked: prefs.mode === 'whitelist'
2019-03-13 01:27:20 -07:00
}, () => chrome.runtime.lastError);
chrome.contextMenus.create({
id: 'custom',
title: 'Switch to "custom" mode',
contexts: ['browser_action'],
type: 'radio',
checked: prefs.mode === 'custom'
2019-03-13 01:27:20 -07:00
}, () => chrome.runtime.lastError);
});
chrome.storage.onChanged.addListener(ps => {
Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue);
2018-08-15 07:30:29 -07:00
if (ps.ua || ps.mode) {
2018-08-16 03:14:11 -07:00
ua.update();
}
});
2019-07-31 23:53:48 -07:00
const ua = {
2018-08-16 03:14:11 -07:00
_obj: {
'global': {}
},
diff(tabId) { // returns true if there is per window object
const windowId = tabs[tabId];
return windowId in this._obj;
},
get windows() {
return Object.keys(this._obj).filter(id => id !== 'global').map(s => Number(s));
},
parse: s => {
const o = {};
o.userAgent = s;
o.appVersion = s
.replace(/^Mozilla\//, '')
.replace(/^Opera\//, '');
const p = (new UAParser(s)).getResult();
o.platform = p.os.name || '';
o.vendor = p.device.vendor || '';
o.product = p.engine.name || '';
o.oscpu = ((p.os.name || '') + ' ' + (p.os.version || '')).trim();
2018-08-16 03:14:11 -07:00
return o;
},
object(tabId, windowId) {
windowId = windowId || (tabId ? tabs[tabId] : 'global');
return this._obj[windowId] || this._obj.global;
},
string(str, windowId) {
if (str) {
this._obj[windowId] = this.parse(str);
}
else {
this._obj[windowId] = {};
}
},
2019-07-31 23:53:48 -07:00
tooltip(title, tabId) {
chrome.browserAction.setTitle({
title,
tabId
});
},
icon(mode, tabId) {
chrome.browserAction.setIcon({
tabId,
2018-08-16 03:14:11 -07:00
path: {
2019-07-31 23:53:48 -07:00
'16': 'data/icons/' + (mode ? mode + '/' : '') + '16.png',
'18': 'data/icons/' + (mode ? mode + '/' : '') + '18.png',
'19': 'data/icons/' + (mode ? mode + '/' : '') + '19.png',
'32': 'data/icons/' + (mode ? mode + '/' : '') + '32.png',
'36': 'data/icons/' + (mode ? mode + '/' : '') + '36.png',
'38': 'data/icons/' + (mode ? mode + '/' : '') + '38.png',
'48': 'data/icons/' + (mode ? mode + '/' : '') + '48.png'
2018-08-16 03:14:11 -07:00
}
2019-07-31 23:53:48 -07:00
});
},
toolbar: ({windowId, tabId, str = ua.object(tabId, windowId).userAgent}) => {
2018-08-16 03:14:11 -07:00
if (windowId) {
chrome.tabs.query({
windowId
}, tabs => tabs.forEach(tab => {
const tabId = tab.id;
chrome.browserAction.setBadgeText({
tabId,
text: ua.object(null, windowId).platform.substr(0, 3)
});
}));
}
else if (tabId) {
chrome.browserAction.setBadgeText({
tabId,
text: ua.object(tabId).platform.substr(0, 3)
});
}
},
update(str = prefs.ua, windowId = 'global') {
chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders);
chrome.webNavigation.onCommitted.removeListener(onCommitted);
if (str || prefs.mode === 'custom' || this.windows.length) {
ua.string(str, windowId);
chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {
'urls': ['*://*/*']
}, ['blocking', 'requestHeaders']);
chrome.webNavigation.onCommitted.addListener(onCommitted);
2019-07-31 23:53:48 -07:00
ua.tooltip('[Default] ' + navigator.userAgent);
ua.icon('ignored');
2018-08-16 03:14:11 -07:00
}
2019-07-31 23:53:48 -07:00
else {
ua.icon('');
ua.tooltip('[Disabled] to enable, use the popup window');
}
2018-08-16 03:14:11 -07:00
if (windowId === 'global') {
this.toolbar({str});
}
// update per window
else {
this.windows.forEach(windowId => this.toolbar({windowId}));
}
}
};
2019-07-31 23:53:48 -07:00
window.ua = ua; // using from popup
2018-08-16 03:14:11 -07:00
// make sure to clean on window removal
if (chrome.windows) { // FF on Android
chrome.windows.onRemoved.addListener(windowId => delete ua._obj[windowId]);
}
2018-08-16 03:14:11 -07:00
function hostname(url) {
const s = url.indexOf('//') + 2;
if (s > 1) {
let o = url.indexOf('/', s);
if (o > 0) {
return url.substring(s, o);
}
else {
o = url.indexOf('?', s);
if (o > 0) {
return url.substring(s, o);
}
else {
return url.substring(s);
}
}
}
else {
return url;
}
}
2018-04-10 00:50:58 -07:00
// returns true, false or an object; true: ignore, false: use from ua object.
2018-08-16 03:14:11 -07:00
function match({url, tabId}) {
if (prefs.mode === 'blacklist') {
if (prefs.blacklist.length) {
const h = hostname(url);
2019-03-13 02:24:03 -07:00
return prefs.blacklist.some(s => {
2018-11-04 01:37:09 -08:00
if (s === h) {
return true;
}
else if (prefs.exactMatch === false) {
2019-03-13 02:29:37 -07:00
return s.endsWith('.' + h) || h.endsWith('.' + s);
2018-11-04 01:37:09 -08:00
}
});
}
}
else if (prefs.mode === 'whitelist') {
if (prefs.whitelist.length) {
const h = hostname(url);
2018-11-04 01:37:09 -08:00
return prefs.whitelist.some(s => {
if (s === h) {
return true;
}
else if (prefs.exactMatch === false) {
2019-03-13 02:29:37 -07:00
return s.endsWith('.' + h) || h.endsWith('.' + s);
2018-11-04 01:37:09 -08:00
}
}) === false;
}
else {
return true;
}
}
else {
const h = hostname(url);
const key = Object.keys(prefs.custom).filter(s => {
if (s === h) {
return true;
}
else if (prefs.exactMatch === false) {
2019-03-13 02:29:37 -07:00
return s.endsWith('.' + h) || h.endsWith('.' + s);
}
}).shift();
let s = prefs.custom[key] || prefs.custom['*'];
2018-05-15 00:54:51 -07:00
// if s is an array select a random string
if (Array.isArray(s)) {
s = s[Math.floor(Math.random() * s.length)];
2019-05-11 01:57:29 -07:00
// set session mode if key is either on _[key] or _['*'] lists
if (prefs.custom._ && Array.isArray(prefs.custom._)) {
if (prefs.custom._.indexOf(key) !== -1) {
prefs.custom[key] = s;
}
else if (prefs.custom._.indexOf('*') !== -1) {
2019-05-11 02:09:41 -07:00
if (prefs.custom[key]) {
prefs.custom[key] = s;
}
2019-05-15 22:18:58 -07:00
else if (prefs.custom['*']) {
2019-05-11 02:09:41 -07:00
prefs.custom['*'] = s;
}
2019-05-11 01:57:29 -07:00
}
}
2018-05-15 00:54:51 -07:00
}
2018-04-10 00:50:58 -07:00
if (s) {
2018-08-16 03:14:11 -07:00
return ua.parse(s);
2018-04-10 00:50:58 -07:00
}
else {
2018-08-16 03:14:11 -07:00
return !ua.object(tabId).userAgent;
2018-04-10 00:50:58 -07:00
}
}
}
2019-07-31 23:53:48 -07:00
const onBeforeSendHeaders = ({tabId, url, requestHeaders, type}) => {
2018-08-20 04:41:47 -07:00
if (type === 'main_frame' || prefs.cache === false) {
2018-08-16 03:14:11 -07:00
cache[tabId] = match({url, tabId});
}
if (cache[tabId] === true) {
2019-07-31 23:53:48 -07:00
return {};
}
2019-06-25 00:43:08 -07:00
if (prefs.protected.some(s => url.indexOf(s) !== -1)) {
return {};
}
2018-08-16 03:14:11 -07:00
const str = (cache[tabId] || ua.object(tabId)).userAgent;
2018-04-10 00:50:58 -07:00
if (str) {
for (let i = 0, name = requestHeaders[0].name; i < requestHeaders.length; i += 1, name = requestHeaders[i].name) {
if (name === 'User-Agent' || name === 'user-agent') {
requestHeaders[i].value = str === 'empty' ? '' : str;
2018-04-10 00:50:58 -07:00
return {
requestHeaders
};
}
2017-09-13 03:18:12 -07:00
}
}
};
2019-07-31 23:53:48 -07:00
const onCommitted = ({frameId, url, tabId}) => {
2018-04-10 00:50:58 -07:00
if (url && (url.startsWith('http') || url.startsWith('ftp')) || url === 'about:blank') {
if (cache[tabId] === true) {
return;
}
2018-08-16 03:14:11 -07:00
const o = cache[tabId] || ua.object(tabId);
2018-04-10 00:50:58 -07:00
if (o.userAgent) {
let {userAgent, appVersion, platform, vendor, product, oscpu} = o;
if (o.userAgent === 'empty') {
userAgent = appVersion = platform = vendor = product = '';
}
2018-04-10 00:50:58 -07:00
chrome.tabs.executeScript(tabId, {
runAt: 'document_start',
frameId,
code: `{
const script = document.createElement('script');
script.textContent = \`{
2019-03-14 00:14:11 -07:00
const userAgent = "${encodeURIComponent(userAgent)}";
const appVersion = "${encodeURIComponent(appVersion)}";
const platform = "${encodeURIComponent(platform)}";
const vendor = "${encodeURIComponent(vendor)}";
const product = "${encodeURIComponent(product)}";
const oscpu = "${encodeURIComponent(oscpu)}";
2019-03-14 00:14:11 -07:00
navigator.__defineGetter__('userAgent', () => decodeURIComponent(userAgent));
navigator.__defineGetter__('appVersion', () => decodeURIComponent(appVersion));
navigator.__defineGetter__('platform', () => decodeURIComponent(platform));
navigator.__defineGetter__('vendor', () => decodeURIComponent(vendor));
navigator.__defineGetter__('product', () => decodeURIComponent(product));
navigator.__defineGetter__('oscpu', () => decodeURIComponent(oscpu));
navigator.__defineGetter__('productSub', () => '');
2019-03-14 00:14:11 -07:00
}\`;
2018-04-10 00:50:58 -07:00
document.documentElement.appendChild(script);
2018-08-16 03:14:11 -07:00
script.remove();
2018-04-10 00:50:58 -07:00
}`
2019-07-31 23:53:48 -07:00
}, () => {
if (chrome.runtime.lastError) {
if (frameId === 0) {
ua.tooltip('[Default] ' + navigator.userAgent);
ua.icon('ignored', tabId);
}
}
else if (frameId === 0) {
ua.tooltip('[Custom] ' + userAgent);
ua.icon('active', tabId);
}
});
2018-04-10 00:50:58 -07:00
}
2017-09-13 03:18:12 -07:00
}
2018-08-16 03:14:11 -07:00
// change the toolbar icon if there is a per window UA setting
if (frameId === 0 && ua.diff(tabId)) {
ua.toolbar({tabId});
2017-09-13 03:18:12 -07:00
}
2018-08-16 03:14:11 -07:00
};
// context menu
chrome.contextMenus.onClicked.addListener(info => chrome.storage.local.set({
mode: info.menuItemId
}));
2017-09-13 03:18:12 -07:00
2017-09-14 05:50:53 -07:00
// FAQs & Feedback
{
const {onInstalled, setUninstallURL, getManifest} = chrome.runtime;
const {name, version} = getManifest();
const page = getManifest().homepage_url;
onInstalled.addListener(({reason, previousVersion}) => {
chrome.storage.local.get({
'faqs': true,
'last-update': 0
}, prefs => {
if (reason === 'install' || (prefs.faqs && reason === 'update')) {
const doUpdate = (Date.now() - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45;
if (doUpdate && previousVersion !== version) {
chrome.tabs.create({
url: page + '?version=' + version +
(previousVersion ? '&p=' + previousVersion : '') +
'&type=' + reason,
active: reason === 'install'
});
chrome.storage.local.set({'last-update': Date.now()});
}
}
2017-09-14 05:50:53 -07:00
});
});
setUninstallURL(page + '?rd=feedback&name=' + encodeURIComponent(name) + '&version=' + version);
2017-09-14 05:50:53 -07:00
}