2017-11-19 01:32:13 -08:00
|
|
|
/* globals UAParser*/
|
|
|
|
|
2017-09-13 03:18:12 -07:00
|
|
|
'use strict';
|
|
|
|
|
2018-08-16 03:14:11 -07:00
|
|
|
var cache = {};
|
|
|
|
var tabs = {};
|
|
|
|
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
|
|
|
|
2017-11-22 04:35:22 -08:00
|
|
|
var prefs = {
|
|
|
|
ua: '',
|
|
|
|
blacklist: [],
|
|
|
|
whitelist: [],
|
2017-12-13 04:45:54 -08:00
|
|
|
custom: {},
|
2018-08-16 03:14:11 -07:00
|
|
|
mode: 'blacklist',
|
|
|
|
color: '#ffa643'
|
2017-11-22 04:35:22 -08:00
|
|
|
};
|
|
|
|
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();
|
|
|
|
});
|
2018-08-16 23:16:10 -07:00
|
|
|
if (chrome.browserAction.setBadgeBackgroundColor) { // FF for Android
|
|
|
|
chrome.browserAction.setBadgeBackgroundColor({
|
|
|
|
color: prefs.color
|
|
|
|
});
|
|
|
|
}
|
2017-11-22 04:35:22 -08:00
|
|
|
});
|
|
|
|
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();
|
2017-12-13 04:45:54 -08:00
|
|
|
}
|
2017-11-22 04:35:22 -08:00
|
|
|
});
|
|
|
|
|
2018-08-16 03:14:11 -07:00
|
|
|
var ua = {
|
|
|
|
_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);
|
|
|
|
o.platform = p.getOS().name || '';
|
|
|
|
o.vendor = p.getDevice().vendor || '';
|
|
|
|
|
|
|
|
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] = {};
|
|
|
|
}
|
|
|
|
},
|
|
|
|
toolbar: ({windowId, tabId, str = ua.object(tabId, windowId).userAgent}) => {
|
|
|
|
const icon = {
|
|
|
|
path: {
|
|
|
|
16: 'data/icons/' + (str ? 'active/' : '') + '16.png',
|
|
|
|
32: 'data/icons/' + (str ? 'active/' : '') + '32.png',
|
|
|
|
48: 'data/icons/' + (str ? 'active/' : '') + '48.png',
|
|
|
|
64: 'data/icons/' + (str ? 'active/' : '') + '64.png'
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const custom = 'Mapped from user\'s JSON object if found, otherwise uses "' + (str || navigator.userAgent) + '"';
|
|
|
|
const title = {
|
|
|
|
title: `UserAgent Switcher (${str ? 'enabled' : 'disabled'})
|
|
|
|
|
|
|
|
User-Agent String: ${prefs.mode === 'custom' ? custom : str || navigator.userAgent}`
|
|
|
|
};
|
|
|
|
if (windowId) {
|
|
|
|
chrome.tabs.query({
|
|
|
|
windowId
|
|
|
|
}, tabs => tabs.forEach(tab => {
|
|
|
|
const tabId = tab.id;
|
|
|
|
chrome.browserAction.setTitle(Object.assign({tabId}, title));
|
|
|
|
chrome.browserAction.setBadgeText({
|
|
|
|
tabId,
|
|
|
|
text: ua.object(null, windowId).platform.substr(0, 3)
|
|
|
|
});
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
else if (tabId) {
|
|
|
|
chrome.browserAction.setTitle(Object.assign({tabId}, title));
|
|
|
|
chrome.browserAction.setBadgeText({
|
|
|
|
tabId,
|
|
|
|
text: ua.object(tabId).platform.substr(0, 3)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
chrome.browserAction.setIcon(icon);
|
|
|
|
chrome.browserAction.setTitle(title);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
if (windowId === 'global') {
|
|
|
|
this.toolbar({str});
|
|
|
|
}
|
|
|
|
// update per window
|
|
|
|
else {
|
|
|
|
this.windows.forEach(windowId => this.toolbar({windowId}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// make sure to clean on window removal
|
2018-08-16 23:16:10 -07:00
|
|
|
if (chrome.windows) { // FF on Android
|
|
|
|
chrome.windows.onRemoved.addListener(windowId => delete ua._obj[windowId]);
|
|
|
|
}
|
2018-08-16 03:14:11 -07:00
|
|
|
|
2017-11-22 04:35:22 -08: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}) {
|
2017-11-22 04:35:22 -08:00
|
|
|
if (prefs.mode === 'blacklist') {
|
|
|
|
if (prefs.blacklist.length) {
|
|
|
|
const h = hostname(url);
|
|
|
|
return prefs.blacklist.some(s => s === h);
|
|
|
|
}
|
|
|
|
}
|
2017-12-13 04:45:54 -08:00
|
|
|
else if (prefs.mode === 'whitelist') {
|
2017-12-13 00:45:52 -08:00
|
|
|
if (prefs.whitelist.length) {
|
2017-11-22 04:35:22 -08:00
|
|
|
const h = hostname(url);
|
|
|
|
return prefs.whitelist.some(s => s === h) === false;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2017-12-13 04:45:54 -08:00
|
|
|
else {
|
|
|
|
const h = hostname(url);
|
2018-05-15 00:54:51 -07:00
|
|
|
let s = prefs.custom[h] || prefs.custom['*'];
|
|
|
|
// if s is an array select a random string
|
|
|
|
if (Array.isArray(s)) {
|
|
|
|
s = s[Math.floor(Math.random() * s.length)];
|
|
|
|
}
|
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
|
|
|
}
|
2017-12-13 04:45:54 -08:00
|
|
|
}
|
2017-11-22 04:35:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
var onBeforeSendHeaders = ({tabId, url, requestHeaders, type}) => {
|
|
|
|
if (type === 'main_frame') {
|
2018-08-16 03:14:11 -07:00
|
|
|
cache[tabId] = match({url, tabId});
|
2017-11-22 04:35:22 -08:00
|
|
|
}
|
2017-12-13 04:45:54 -08:00
|
|
|
if (cache[tabId] === true) {
|
2017-11-22 04:35:22 -08:00
|
|
|
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;
|
|
|
|
return {
|
|
|
|
requestHeaders
|
|
|
|
};
|
2017-12-13 04:45:54 -08:00
|
|
|
}
|
2017-09-13 03:18:12 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var 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) {
|
2017-11-22 04:35:22 -08:00
|
|
|
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) {
|
|
|
|
chrome.tabs.executeScript(tabId, {
|
|
|
|
runAt: 'document_start',
|
|
|
|
frameId,
|
|
|
|
code: `{
|
|
|
|
const script = document.createElement('script');
|
|
|
|
script.textContent = \`{
|
|
|
|
navigator.__defineGetter__('userAgent', () => '${o.userAgent}');
|
|
|
|
navigator.__defineGetter__('appVersion', () => '${o.appVersion}');
|
|
|
|
navigator.__defineGetter__('platform', () => '${o.platform}');
|
|
|
|
navigator.__defineGetter__('vendor', () => '${o.vendor}');
|
|
|
|
}\`;
|
|
|
|
document.documentElement.appendChild(script);
|
2018-08-16 03:14:11 -07:00
|
|
|
script.remove();
|
2018-04-10 00:50:58 -07:00
|
|
|
}`
|
|
|
|
}, () => chrome.runtime.lastError);
|
|
|
|
}
|
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
|
|
|
};
|
2017-09-13 03:18:12 -07:00
|
|
|
|
2017-09-14 05:50:53 -07:00
|
|
|
// FAQs & Feedback
|
|
|
|
chrome.storage.local.get({
|
|
|
|
'version': null,
|
2018-08-15 07:30:29 -07:00
|
|
|
'faqs': false,
|
2018-08-15 05:01:50 -07:00
|
|
|
'last-update': 0
|
2017-09-14 05:50:53 -07:00
|
|
|
}, prefs => {
|
|
|
|
const version = chrome.runtime.getManifest().version;
|
|
|
|
|
|
|
|
if (prefs.version ? (prefs.faqs && prefs.version !== version) : true) {
|
2017-12-13 04:45:54 -08:00
|
|
|
const now = Date.now();
|
2018-08-15 05:01:50 -07:00
|
|
|
const doUpdate = (now - prefs['last-update']) / 1000 / 60 / 60 / 24 > 45;
|
2017-12-13 04:45:54 -08:00
|
|
|
chrome.storage.local.set({
|
|
|
|
version,
|
|
|
|
'last-update': doUpdate ? Date.now() : prefs['last-update']
|
|
|
|
}, () => {
|
2018-08-15 05:01:50 -07:00
|
|
|
// do not display the FAQs page if last-update occurred less than 45 days ago.
|
2017-12-13 04:45:54 -08:00
|
|
|
if (doUpdate) {
|
|
|
|
const p = Boolean(prefs.version);
|
|
|
|
chrome.tabs.create({
|
|
|
|
url: chrome.runtime.getManifest().homepage_url + '?version=' + version +
|
|
|
|
'&type=' + (p ? ('upgrade&p=' + prefs.version) : 'install'),
|
|
|
|
active: p === false
|
|
|
|
});
|
|
|
|
}
|
2017-09-14 05:50:53 -07:00
|
|
|
});
|
|
|
|
}
|
2017-09-13 03:18:12 -07:00
|
|
|
});
|
2017-09-14 05:50:53 -07:00
|
|
|
|
|
|
|
{
|
|
|
|
const {name, version} = chrome.runtime.getManifest();
|
2017-12-13 04:45:54 -08:00
|
|
|
chrome.runtime.setUninstallURL(
|
|
|
|
chrome.runtime.getManifest().homepage_url + '?rd=feedback&name=' + name + '&version=' + version
|
|
|
|
);
|
2017-09-14 05:50:53 -07:00
|
|
|
}
|