supporting custom ua string per container
This commit is contained in:
parent
bf16cb694a
commit
2a317e5aec
5 changed files with 112 additions and 67 deletions
|
@ -5,8 +5,16 @@
|
||||||
|
|
||||||
const cache = {};
|
const cache = {};
|
||||||
const tabs = {};
|
const tabs = {};
|
||||||
chrome.tabs.onRemoved.addListener(id => delete cache[id]);
|
const cookieStoreIds = {};
|
||||||
chrome.tabs.onCreated.addListener(tab => tabs[tab.id] = tab.windowId);
|
chrome.tabs.onRemoved.addListener(id => {
|
||||||
|
delete cache[id];
|
||||||
|
delete tabs[id];
|
||||||
|
delete cookieStoreIds[id];
|
||||||
|
});
|
||||||
|
chrome.tabs.onCreated.addListener(tab => {
|
||||||
|
tabs[tab.id] = tab.windowId;
|
||||||
|
cookieStoreIds[tab.id] = tab.cookieStoreId;
|
||||||
|
});
|
||||||
|
|
||||||
const prefs = {
|
const prefs = {
|
||||||
ua: '',
|
ua: '',
|
||||||
|
@ -50,7 +58,10 @@ chrome.storage.local.get(prefs, ps => {
|
||||||
}
|
}
|
||||||
expand();
|
expand();
|
||||||
chrome.tabs.query({}, ts => {
|
chrome.tabs.query({}, ts => {
|
||||||
ts.forEach(t => tabs[t.id] = t.windowId);
|
ts.forEach(t => {
|
||||||
|
tabs[t.id] = t.windowId;
|
||||||
|
cookieStoreIds[t.id] = t.cookieStoreId;
|
||||||
|
});
|
||||||
ua.update();
|
ua.update();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -84,7 +95,6 @@ chrome.storage.local.get(prefs, ps => {
|
||||||
}, () => chrome.runtime.lastError);
|
}, () => chrome.runtime.lastError);
|
||||||
});
|
});
|
||||||
chrome.storage.onChanged.addListener(ps => {
|
chrome.storage.onChanged.addListener(ps => {
|
||||||
console.log(ps);
|
|
||||||
Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue);
|
Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue);
|
||||||
if (ps.ua || ps.mode) {
|
if (ps.ua || ps.mode) {
|
||||||
ua.update();
|
ua.update();
|
||||||
|
@ -95,17 +105,19 @@ chrome.storage.onChanged.addListener(ps => {
|
||||||
});
|
});
|
||||||
|
|
||||||
const ua = {
|
const ua = {
|
||||||
_obj: {
|
_obj: {},
|
||||||
'global': {}
|
diff(tabId, cookieStoreId = 'default-container') { // returns true if there is per window object
|
||||||
},
|
log('ua.diff is called', tabId, cookieStoreId);
|
||||||
diff(tabId) { // returns true if there is per window object
|
|
||||||
log('ua.diff is called', tabId);
|
|
||||||
const windowId = tabs[tabId];
|
const windowId = tabs[tabId];
|
||||||
return windowId in this._obj;
|
return windowId in (this._obj[cookieStoreId] || this._obj['default-container'] || {});
|
||||||
},
|
},
|
||||||
get windows() {
|
get windows() {
|
||||||
log('ua.windows is called');
|
log('ua.windows is called');
|
||||||
return Object.keys(this._obj).filter(id => id !== 'global').map(s => Number(s));
|
const ids = [];
|
||||||
|
for (const cookieStoreId of Object.keys(this._obj)) {
|
||||||
|
ids.push(...Object.keys(this._obj[cookieStoreId]).filter(id => id !== 'global').map(s => Number(s)));
|
||||||
|
}
|
||||||
|
return ids.filter((n, i, l) => n && l.indexOf(n) === i);
|
||||||
},
|
},
|
||||||
parse: s => {
|
parse: s => {
|
||||||
log('ua.parse is called', s);
|
log('ua.parse is called', s);
|
||||||
|
@ -159,18 +171,23 @@ const ua = {
|
||||||
}
|
}
|
||||||
return o;
|
return o;
|
||||||
},
|
},
|
||||||
object(tabId, windowId) {
|
object(tabId, windowId, cookieStoreId = 'default-container') {
|
||||||
log('ua.object is called', tabId, windowId);
|
|
||||||
windowId = windowId || (tabId ? tabs[tabId] : 'global');
|
windowId = windowId || (tabId ? tabs[tabId] : 'global');
|
||||||
return this._obj[windowId] || this._obj.global;
|
log('ua.object is called', tabId, windowId, cookieStoreId);
|
||||||
|
|
||||||
|
if (this._obj[cookieStoreId]) {
|
||||||
|
return this._obj[cookieStoreId][windowId] || this._obj[cookieStoreId].global || this._obj['default-container'].global;
|
||||||
|
}
|
||||||
|
return (this._obj['default-container'] || {}).global;
|
||||||
},
|
},
|
||||||
string(str, windowId) {
|
string(str, windowId, cookieStoreId) {
|
||||||
log('ua.string is called', str, windowId);
|
log('ua.string is called', str, windowId);
|
||||||
|
this._obj[cookieStoreId] = this._obj[cookieStoreId] || {};
|
||||||
if (str) {
|
if (str) {
|
||||||
this._obj[windowId] = this.parse(str);
|
this._obj[cookieStoreId][windowId] = this.parse(str);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._obj[windowId] = {};
|
this._obj[cookieStoreId][windowId] = {};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
tooltip(title, tabId) {
|
tooltip(title, tabId) {
|
||||||
|
@ -195,7 +212,7 @@ const ua = {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
toolbar: ({windowId, tabId}) => {
|
toolbar: ({windowId, tabId, cookieStoreId}) => {
|
||||||
log('ua.toolbar is called', windowId, tabId);
|
log('ua.toolbar is called', windowId, tabId);
|
||||||
if (windowId) {
|
if (windowId) {
|
||||||
chrome.tabs.query({
|
chrome.tabs.query({
|
||||||
|
@ -204,20 +221,19 @@ const ua = {
|
||||||
const tabId = tab.id;
|
const tabId = tab.id;
|
||||||
chrome.browserAction.setBadgeText({
|
chrome.browserAction.setBadgeText({
|
||||||
tabId,
|
tabId,
|
||||||
text: ua.object(null, windowId).platform.substr(0, 3)
|
text: ua.object(null, windowId, tab.cookieStoreId).platform.substr(0, 3)
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
else if (tabId) {
|
else if (tabId) {
|
||||||
chrome.browserAction.setBadgeText({
|
chrome.browserAction.setBadgeText({
|
||||||
tabId,
|
tabId,
|
||||||
text: ua.object(tabId).platform.substr(0, 3)
|
text: ua.object(tabId, undefined, cookieStoreId).platform.substr(0, 3)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update(str = prefs.ua, windowId = 'global') {
|
update(str = prefs.ua, windowId = 'global', cookieStoreId = 'default-container') {
|
||||||
console.log(str);
|
log('ua.update is called', str, windowId, cookieStoreId);
|
||||||
log('ua.update is called', str, windowId);
|
|
||||||
// clear caching
|
// clear caching
|
||||||
Object.keys(cache).forEach(key => delete cache[key]);
|
Object.keys(cache).forEach(key => delete cache[key]);
|
||||||
// remove old listeners
|
// remove old listeners
|
||||||
|
@ -225,7 +241,7 @@ const ua = {
|
||||||
chrome.webNavigation.onCommitted.removeListener(onCommitted);
|
chrome.webNavigation.onCommitted.removeListener(onCommitted);
|
||||||
// apply new ones
|
// apply new ones
|
||||||
if (str || prefs.mode === 'custom' || this.windows.length) {
|
if (str || prefs.mode === 'custom' || this.windows.length) {
|
||||||
ua.string(str, windowId);
|
ua.string(str, windowId, cookieStoreId);
|
||||||
chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {
|
chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {
|
||||||
'urls': ['*://*/*']
|
'urls': ['*://*/*']
|
||||||
}, ['blocking', 'requestHeaders']);
|
}, ['blocking', 'requestHeaders']);
|
||||||
|
@ -250,7 +266,11 @@ const ua = {
|
||||||
window.ua = ua; // using from popup
|
window.ua = ua; // using from popup
|
||||||
// make sure to clean on window removal
|
// make sure to clean on window removal
|
||||||
if (chrome.windows) { // FF on Android
|
if (chrome.windows) { // FF on Android
|
||||||
chrome.windows.onRemoved.addListener(windowId => delete ua._obj[windowId]);
|
chrome.windows.onRemoved.addListener(windowId => {
|
||||||
|
Object.keys(ua._obj).forEach(cookieStoreId => {
|
||||||
|
delete ua._obj[cookieStoreId][windowId];
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hostname(url) {
|
function hostname(url) {
|
||||||
|
@ -276,8 +296,8 @@ function hostname(url) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// returns true, false or an object; true: ignore, false: use from ua object.
|
// returns true, false or an object; true: ignore, false: use from ua object.
|
||||||
function match({url, tabId}) {
|
function match({url, tabId, cookieStoreId = 'default-container'}) {
|
||||||
log('match', url, tabId);
|
log('match', url, tabId, cookieStoreId);
|
||||||
const h = hostname(url);
|
const h = hostname(url);
|
||||||
|
|
||||||
if (prefs.mode === 'blacklist') {
|
if (prefs.mode === 'blacklist') {
|
||||||
|
@ -307,55 +327,57 @@ function match({url, tabId}) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
const [hh] = h.split(':');
|
||||||
const [hh] = h.split(':');
|
const key = Object.keys(expand.rules).filter(s => {
|
||||||
const key = Object.keys(expand.rules).filter(s => {
|
if (s === h || s === hh) {
|
||||||
if (s === h || s === hh) {
|
return true;
|
||||||
return true;
|
}
|
||||||
|
else if (prefs.exactMatch === false) {
|
||||||
|
return s.endsWith('.' + h) || h.endsWith('.' + s) || s.endsWith('.' + hh) || hh.endsWith('.' + s);
|
||||||
|
}
|
||||||
|
}).shift();
|
||||||
|
let s = expand.rules[key] || expand.rules['*'];
|
||||||
|
// if s is an array select a random string
|
||||||
|
if (Array.isArray(s)) {
|
||||||
|
s = s[Math.floor(Math.random() * s.length)];
|
||||||
|
// set session mode if key is either on _[key] or _['*'] lists
|
||||||
|
if (expand.rules._ && Array.isArray(expand.rules._)) {
|
||||||
|
if (expand.rules._.indexOf(key) !== -1) {
|
||||||
|
expand.rules[key] = s;
|
||||||
}
|
}
|
||||||
else if (prefs.exactMatch === false) {
|
else if (expand.rules._.indexOf('*') !== -1) {
|
||||||
return s.endsWith('.' + h) || h.endsWith('.' + s) || s.endsWith('.' + hh) || hh.endsWith('.' + s);
|
if (expand.rules[key]) {
|
||||||
}
|
|
||||||
}).shift();
|
|
||||||
let s = expand.rules[key] || expand.rules['*'];
|
|
||||||
// if s is an array select a random string
|
|
||||||
if (Array.isArray(s)) {
|
|
||||||
s = s[Math.floor(Math.random() * s.length)];
|
|
||||||
// set session mode if key is either on _[key] or _['*'] lists
|
|
||||||
if (expand.rules._ && Array.isArray(expand.rules._)) {
|
|
||||||
if (expand.rules._.indexOf(key) !== -1) {
|
|
||||||
expand.rules[key] = s;
|
expand.rules[key] = s;
|
||||||
}
|
}
|
||||||
else if (expand.rules._.indexOf('*') !== -1) {
|
else if (expand.rules['*']) {
|
||||||
if (expand.rules[key]) {
|
expand.rules['*'] = s;
|
||||||
expand.rules[key] = s;
|
|
||||||
}
|
|
||||||
else if (expand.rules['*']) {
|
|
||||||
expand.rules['*'] = s;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s) {
|
}
|
||||||
return ua.parse(s);
|
if (s) {
|
||||||
}
|
return ua.parse(s);
|
||||||
else {
|
}
|
||||||
return !ua.object(tabId).userAgent;
|
else {
|
||||||
}
|
const o = ua.object(tabId, undefined, cookieStoreId);
|
||||||
|
return o ? !o.userAgent : true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onBeforeSendHeaders = ({tabId, url, requestHeaders, type}) => {
|
const onBeforeSendHeaders = d => {
|
||||||
|
const {tabId, url, requestHeaders, type, cookieStoreId} = d;
|
||||||
if (type === 'main_frame' || prefs.cache === false) {
|
if (type === 'main_frame' || prefs.cache === false) {
|
||||||
cache[tabId] = match({url, tabId});
|
cache[tabId] = match({url, tabId, cookieStoreId});
|
||||||
}
|
}
|
||||||
|
console.log(cache[tabId]);
|
||||||
|
|
||||||
if (cache[tabId] === true) {
|
if (cache[tabId] === true) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (prefs.protected.some(s => url.indexOf(s) !== -1)) {
|
if (prefs.protected.some(s => url.indexOf(s) !== -1)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
const str = (cache[tabId] || ua.object(tabId)).userAgent;
|
const str = (cache[tabId] || ua.object(tabId, undefined, d.cookieStoreId)).userAgent;
|
||||||
if (str) {
|
if (str) {
|
||||||
for (let i = 0, name = requestHeaders[0].name; i < requestHeaders.length; i += 1, name = (requestHeaders[i] || {}).name) {
|
for (let i = 0, name = requestHeaders[0].name; i < requestHeaders.length; i += 1, name = (requestHeaders[i] || {}).name) {
|
||||||
if (name === 'User-Agent' || name === 'user-agent') {
|
if (name === 'User-Agent' || name === 'user-agent') {
|
||||||
|
@ -368,12 +390,15 @@ const onBeforeSendHeaders = ({tabId, url, requestHeaders, type}) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCommitted = ({frameId, url, tabId}) => {
|
const onCommitted = d => {
|
||||||
|
const {frameId, url, tabId} = d;
|
||||||
|
const cookieStoreId = cookieStoreIds[tabId] || 'default-container';
|
||||||
|
|
||||||
if (url && (url.startsWith('http') || url.startsWith('ftp')) || url === 'about:blank') {
|
if (url && (url.startsWith('http') || url.startsWith('ftp')) || url === 'about:blank') {
|
||||||
if (cache[tabId] === true) {
|
if (cache[tabId] === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const o = cache[tabId] || ua.object(tabId);
|
const o = cache[tabId] || ua.object(tabId, undefined, cookieStoreId);
|
||||||
if (o.userAgent) {
|
if (o.userAgent) {
|
||||||
chrome.tabs.executeScript(tabId, {
|
chrome.tabs.executeScript(tabId, {
|
||||||
runAt: 'document_start',
|
runAt: 'document_start',
|
||||||
|
@ -416,8 +441,8 @@ const onCommitted = ({frameId, url, tabId}) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// change the toolbar icon if there is a per window UA setting
|
// change the toolbar icon if there is a per window UA setting
|
||||||
if (frameId === 0 && ua.diff(tabId)) {
|
if (frameId === 0 && ua.diff(tabId, cookieStoreId)) {
|
||||||
ua.toolbar({tabId});
|
ua.toolbar({tabId, cookieStoreId});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// context menu
|
// context menu
|
||||||
|
|
|
@ -190,6 +190,7 @@ select {
|
||||||
background-color: #4d72b7;
|
background-color: #4d72b7;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
[data-cmd="container"],
|
||||||
[data-cmd="window"],
|
[data-cmd="window"],
|
||||||
[data-cmd="apply"] {
|
[data-cmd="apply"] {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
|
|
|
@ -127,7 +127,7 @@
|
||||||
<input type="button" value="Refresh Tab" title="Refresh the current page" data-cmd="refresh">
|
<input type="button" value="Refresh Tab" title="Refresh the current page" data-cmd="refresh">
|
||||||
<input type="button" value="Reset" title="Reset User-Agent string to the default one. This will not reset window-based UA strings. To reset them, use the 'Restart' button" data-cmd="reset">
|
<input type="button" value="Reset" title="Reset User-Agent string to the default one. This will not reset window-based UA strings. To reset them, use the 'Restart' button" data-cmd="reset">
|
||||||
<input type="button" value="Test" title="Test your user-agent string" data-cmd="test">
|
<input type="button" value="Test" title="Test your user-agent string" data-cmd="test">
|
||||||
<span></span>
|
<input type="button" value="Container" title="Set this string as this container's User-Agent string" data-cmd="container">
|
||||||
<input type="button" value="Window" title="Set this string as this window's User-Agent string" data-cmd="window">
|
<input type="button" value="Window" title="Set this string as this window's User-Agent string" data-cmd="window">
|
||||||
<input type="button" value="Apply" title="Set this string as the browser's User-Agent string" data-cmd="apply">
|
<input type="button" value="Apply" title="Set this string as the browser's User-Agent string" data-cmd="apply">
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -237,12 +237,27 @@ document.addEventListener('click', ({target}) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if (cmd === 'window') {
|
else if (cmd === 'window' || cmd === 'container') {
|
||||||
const value = document.getElementById('ua').value;
|
const value = document.getElementById('ua').value;
|
||||||
chrome.tabs.query({
|
const next = () => chrome.tabs.query({
|
||||||
active: true,
|
active: true,
|
||||||
currentWindow: true
|
currentWindow: true
|
||||||
}, ([tab]) => chrome.runtime.getBackgroundPage(bg => bg.ua.update(value, tab.windowId)));
|
}, ([tab]) => {
|
||||||
|
if (cmd === 'window') {
|
||||||
|
chrome.runtime.getBackgroundPage(bg => bg.ua.update(value, tab.windowId, tab.cookieStoreId));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chrome.runtime.getBackgroundPage(bg => bg.ua.update(value, undefined, tab.cookieStoreId));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (cmd === 'container') {
|
||||||
|
chrome.permissions.request({
|
||||||
|
permissions: ['cookies']
|
||||||
|
}, granted => granted && next());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (cmd === 'reset') {
|
else if (cmd === 'reset') {
|
||||||
const input = document.querySelector('#list :checked');
|
const input = document.querySelector('#list :checked');
|
||||||
|
|
|
@ -13,8 +13,12 @@
|
||||||
"webNavigation",
|
"webNavigation",
|
||||||
"webRequest",
|
"webRequest",
|
||||||
"webRequestBlocking",
|
"webRequestBlocking",
|
||||||
|
"contextualIdentities",
|
||||||
"contextMenus"
|
"contextMenus"
|
||||||
],
|
],
|
||||||
|
"optional_permissions": [
|
||||||
|
"cookies"
|
||||||
|
],
|
||||||
|
|
||||||
"icons": {
|
"icons": {
|
||||||
"16": "data/icons/active/16.png",
|
"16": "data/icons/active/16.png",
|
||||||
|
|
Loading…
Reference in a new issue