diff --git a/extension/common.js b/extension/common.js index 12bcda6..216441c 100644 --- a/extension/common.js +++ b/extension/common.js @@ -3,7 +3,7 @@ 'use strict'; -const cache = {}; +const cache = {}; // cache how a tab's request get handled (true, false, object) const tabs = {}; const cookieStoreIds = {}; chrome.tabs.onRemoved.addListener(id => { @@ -47,22 +47,35 @@ const expand = () => { }; expand.rules = {}; +const currentCookieStoreId = () => new Promise(resolve => chrome.tabs.query({ + active: true, + currentWindow: true +}, tbs => { + resolve((tbs.length ? tbs[0].cookieStoreId : '') || 'firefox-default'); +})); + chrome.storage.local.get(prefs, ps => { Object.assign(prefs, ps); - // update prefs.ua from the managed storage - chrome.storage.managed.get({ - ua: '' - }, rps => { - if (!chrome.runtime.lastError && rps.ua) { - prefs.ua = rps.ua; - } - expand(); - chrome.tabs.query({}, ts => { - ts.forEach(t => { - tabs[t.id] = t.windowId; - cookieStoreIds[t.id] = t.cookieStoreId; - }); - ua.update(); + expand(); + + chrome.tabs.query({}, ts => { + ts.forEach(t => { + tabs[t.id] = t.windowId; + cookieStoreIds[t.id] = t.cookieStoreId; + }); + + // update prefs.ua from the managed storage + chrome.storage.managed.get({ + ua: '' + }, rps => { + if (!chrome.runtime.lastError && rps.ua) { + chrome.storage.local.set({ + ua: rps.ua + }); + } + else { + ua.update(undefined, undefined, 'firefox-default'); + } }); }); @@ -96,8 +109,17 @@ chrome.storage.local.get(prefs, ps => { }); chrome.storage.onChanged.addListener(ps => { Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue); + if (ps.ua || ps.mode) { - ua.update(); + currentCookieStoreId().then(cookieStoreId => { + if (ps.ua) { + if (ps.ua.newValue === '') { + console.log('delete from onChanged', cookieStoreId); + delete ua._obj[cookieStoreId]; + } + } + ua.update(undefined, undefined, cookieStoreId); + }); } if (ps.custom) { expand(); @@ -176,9 +198,8 @@ const ua = { 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[cookieStoreId][windowId] || this._obj[cookieStoreId].global; } - return (this._obj['default-container'] || {}).global; }, string(str, windowId, cookieStoreId) { log('ua.string is called', str, windowId); @@ -219,20 +240,28 @@ const ua = { windowId }, tabs => tabs.forEach(tab => { const tabId = tab.id; + const o = ua.object(null, windowId, tab.cookieStoreId); chrome.browserAction.setBadgeText({ tabId, - text: ua.object(null, windowId, tab.cookieStoreId).platform.substr(0, 3) + text: o && o.platform ? o.platform.substr(0, 3) : '' }); })); } else if (tabId) { + const o = ua.object(tabId, undefined, cookieStoreId); chrome.browserAction.setBadgeText({ tabId, - text: ua.object(tabId, undefined, cookieStoreId).platform.substr(0, 3) + text: o.platform ? o.platform.substr(0, 3) : 'BOT' }); } }, update(str = prefs.ua, windowId = 'global', cookieStoreId = 'default-container') { + + console.log(new Error().stack, cookieStoreId); + console.log(str, prefs.mode === 'custom', this.windows.length, Object.keys(this._obj).length); + + + log('ua.update is called', str, windowId, cookieStoreId); // clear caching Object.keys(cache).forEach(key => delete cache[key]); @@ -240,8 +269,11 @@ const ua = { chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders); chrome.webNavigation.onCommitted.removeListener(onCommitted); // apply new ones - if (str || prefs.mode === 'custom' || this.windows.length) { - ua.string(str, windowId, cookieStoreId); + if (str || prefs.mode === 'custom' || this.windows.length || Object.keys(this._obj).length) { + console.log('reinstall'); + if (str) { + ua.string(str, windowId, cookieStoreId); + } chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, { 'urls': ['*://*/*'] }, ['blocking', 'requestHeaders']); @@ -266,10 +298,26 @@ const ua = { window.ua = ua; // using from popup // make sure to clean on window removal if (chrome.windows) { // FF on Android + console.log('windowId'); chrome.windows.onRemoved.addListener(windowId => { + console.log(windowId, Object.keys(ua._obj), ua._obj); + let update = false; Object.keys(ua._obj).forEach(cookieStoreId => { - delete ua._obj[cookieStoreId][windowId]; + if (windowId in ua._obj[cookieStoreId]) { + console.log('DELETE from windows', cookieStoreId); + delete ua._obj[cookieStoreId][windowId]; + // delete the entire object if it is empty + if (Object.keys(ua._obj[cookieStoreId]).length === 0) { + delete ua._obj[cookieStoreId]; + } + update = true; + } }); + console.log('upda', update); + // if nothing is left to monitor, disable the extension + if (update) { + currentCookieStoreId().then(cookieStoreId => ua.update(undefined, undefined, cookieStoreId)); + } }); } @@ -369,7 +417,6 @@ const onBeforeSendHeaders = d => { if (type === 'main_frame' || prefs.cache === false) { cache[tabId] = match({url, tabId, cookieStoreId}); } - console.log(cache[tabId]); if (cache[tabId] === true) { return {}; @@ -377,7 +424,8 @@ const onBeforeSendHeaders = d => { if (prefs.protected.some(s => url.indexOf(s) !== -1)) { return {}; } - const str = (cache[tabId] || ua.object(tabId, undefined, d.cookieStoreId)).userAgent; + const o = (cache[tabId] || ua.object(tabId, undefined, d.cookieStoreId)); + const str = o ? o.userAgent : ''; 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') { diff --git a/extension/data/popup/index.css b/extension/data/popup/index.css index 8c2a0b9..fb08204 100644 --- a/extension/data/popup/index.css +++ b/extension/data/popup/index.css @@ -190,11 +190,14 @@ select { background-color: #4d72b7; color: #fff; } -[data-cmd="container"], +[data-cmd="container"] { + background-color: #92773c; + color: #fff; +} [data-cmd="window"], [data-cmd="apply"] { - color: #fff; background-color: #3c923c; + color: #fff; } [data-cmd="reset"] { @@ -242,3 +245,7 @@ body[data-android="true"] [data-cmd="window"] { #toast:empty { display: none; } + +.hide { + visibility: hidden; +} diff --git a/extension/data/popup/index.html b/extension/data/popup/index.html index 50e0e54..1d95a62 100644 --- a/extension/data/popup/index.html +++ b/extension/data/popup/index.html @@ -78,7 +78,7 @@ userAgent
- +
@@ -125,11 +125,11 @@ - + - - - + + + diff --git a/extension/data/popup/index.js b/extension/data/popup/index.js index d4a6baf..1b142d3 100644 --- a/extension/data/popup/index.js +++ b/extension/data/popup/index.js @@ -2,6 +2,30 @@ document.body.dataset.android = navigator.userAgent.indexOf('Android') !== -1; +let tab = {}; + +chrome.tabs.query({ + active: true, + currentWindow: true +}, tbs => { + if (tbs.length) { + tab = tbs[0]; + if ('cookieStoreId' in tab) { + const apply = document.querySelector('[data-cmd="apply"]'); + apply.value += ' (CNT)'; + apply.title = 'Set this user-agent string as the current container\'s User-Agent string'; + + const w = document.querySelector('[data-cmd="window"]'); + w.value += ' (CNT)'; + w.title = 'Set this user-agent string for all tabs inside the current window\'s container'; + + const reset = document.querySelector('[data-cmd="reset"]'); + reset.value += ' (CNT)'; + reset.title = 'Reset the container\'s user-agent string to the default one. This will not reset window-based UA strings. To reset them, use the \'Restart\' button'; + } + } +}); + const map = {}; function sort(arr) { @@ -227,37 +251,15 @@ document.addEventListener('click', ({target}) => { else { msg('User-Agent is Set'); } - chrome.storage.local.set({ - ua: '' // since we set from managed storage, the value might already be this one and hence onChanged is not being called - }, () => { - if (value !== navigator.userAgent) { - chrome.storage.local.set({ - ua: value - }); - } - }); + if (value !== navigator.userAgent) { + chrome.storage.local.set({ + ua: value + }); + } } - else if (cmd === 'window' || cmd === 'container') { + else if (cmd === 'window') { const value = document.getElementById('ua').value; - const next = () => chrome.tabs.query({ - active: true, - currentWindow: true - }, ([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(); - } + chrome.runtime.getBackgroundPage(bg => bg.ua.update(value, tab.windowId, tab.cookieStoreId)); } else if (cmd === 'reset') { const input = document.querySelector('#list :checked'); @@ -267,7 +269,7 @@ document.addEventListener('click', ({target}) => { chrome.storage.local.set({ ua: '' }); - msg('Reset to Default'); + msg('Disabled. Uses the default user-agent string'); } else if (cmd === 'refresh') { chrome.tabs.query({ @@ -314,3 +316,28 @@ document.getElementById('ua').addEventListener('input', e => { }); } }); +document.getElementById('ua').addEventListener('keyup', e => { + if (e.key === 'Enter') { + document.querySelector('[data-cmd="apply"]').click(); + } +}); + +/* container support */ +document.querySelector('[data-cmd="container"]').addEventListener('click', e => { + chrome.permissions.request({ + permissions: ['cookies'] + }, granted => { + if (granted) { + e.target.classList.add('hide'); + } + }); +}); +if (/Firefox/.test(navigator.userAgent)) { + chrome.permissions.contains({ + permissions: ['cookies'] + }, granted => { + if (granted === false) { + document.querySelector('[data-cmd="container"]').classList.remove('hide'); + } + }); +}