diff --git a/common.js b/common.js index 64eb793..12fbd85 100644 --- a/common.js +++ b/common.js @@ -2,12 +2,7 @@ 'use strict'; -var ua = { - userAgent: '', - appVersion: '', - platform: '', - vendor: '' -}; +var ua = {}; var prefs = { ua: '', @@ -19,15 +14,15 @@ var prefs = { chrome.storage.local.get(prefs, ps => { Object.assign(prefs, ps); - update(prefs.mode === 'custom' ? 'Mapped from user\'s JSON object' : prefs.ua); + update(); }); chrome.storage.onChanged.addListener(ps => { Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue); if (ps.ua) { - update(prefs.ua); + update(); } if (ps.mode) { - update(ps.mode.newValue === 'custom' ? 'Mapped from user\'s JSON object' : prefs.ua); + update(); } }); @@ -52,6 +47,7 @@ function hostname(url) { return url; } } +// returns true, false or an object; true: ignore, false: use from ua object. function match(url) { if (prefs.mode === 'blacklist') { if (prefs.blacklist.length) { @@ -70,7 +66,22 @@ function match(url) { } else { const h = hostname(url); - return prefs.custom[h]; + const s = prefs.custom[h]; + if (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; + } + else { + return ua.userAgent ? false : true; + } } } @@ -84,58 +95,58 @@ var onBeforeSendHeaders = ({tabId, url, requestHeaders, type}) => { if (cache[tabId] === true) { return; } - 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 (prefs.mode === 'custom') { - if (cache[tabId]) { - requestHeaders[i].value = cache[tabId]; - } - else { - return; - } + const str = (cache[tabId] || ua).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') { + requestHeaders[i].value = str; + return { + requestHeaders + }; } - else { - requestHeaders[i].value = ua.userAgent; - } - return { - requestHeaders - }; } } }; var onCommitted = ({frameId, url, tabId}) => { - if (frameId === 0 && url && (url.startsWith('http') || url.startsWith('ftp'))) { - if (cache[tabId]) { + if (url && (url.startsWith('http') || url.startsWith('ftp')) || url === 'about:blank') { + if (cache[tabId] === true) { return; } - chrome.tabs.executeScript(tabId, { - runAt: 'document_start', - allFrames: true, - code: `{ - const script = document.createElement('script'); - script.textContent = \`{ - navigator.__defineGetter__('userAgent', () => '${ua.userAgent}'); - navigator.__defineGetter__('appVersion', () => '${ua.appVersion}'); - navigator.__defineGetter__('platform', () => '${ua.platform}'); - navigator.__defineGetter__('vendor', () => '${ua.vendor}'); - }\`; - document.documentElement.appendChild(script); - }` - }, () => chrome.runtime.lastError); + const o = cache[tabId] || ua; + 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); + }` + }, () => chrome.runtime.lastError); + } } }; -function update(str) { - ua.userAgent = str; - ua.appVersion = str - .replace(/^Mozilla\//, '') - .replace(/^Opera\//, ''); - if (str) { - const p = new UAParser(str); - ua.platform = p.getOS().name || ''; - ua.vendor = p.getDevice().vendor || ''; - +function update() { + if (prefs.ua || prefs.mode === 'custom') { + if (prefs.ua) { + ua.userAgent = prefs.ua; + ua.appVersion = ua.userAgent + .replace(/^Mozilla\//, '') + .replace(/^Opera\//, ''); + const p = new UAParser(prefs.ua); + ua.platform = p.getOS().name || ''; + ua.vendor = p.getDevice().vendor || ''; + } + else { + ua = {}; + } chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, { 'urls' : ['*://*/*'] }, ['blocking', 'requestHeaders']); @@ -145,18 +156,20 @@ function update(str) { chrome.webRequest.onBeforeSendHeaders.removeListener(onBeforeSendHeaders); chrome.webNavigation.onCommitted.removeListener(onCommitted); } + chrome.browserAction.setIcon({ 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' + 16: 'data/icons/' + (prefs.ua ? 'active/' : '') + '16.png', + 32: 'data/icons/' + (prefs.ua ? 'active/' : '') + '32.png', + 48: 'data/icons/' + (prefs.ua ? 'active/' : '') + '48.png', + 64: 'data/icons/' + (prefs.ua ? 'active/' : '') + '64.png' } }); + const custom = 'Mapped from user\'s JSON object if found, otherwise uses "' + (prefs.ua || navigator.userAgent) + '"'; chrome.browserAction.setTitle({ - title: `UserAgent Switcher (${str ? 'enabled' : 'disabled'}) + title: `UserAgent Switcher (${prefs.ua ? 'enabled' : 'disabled'}) -User-Agent String: ${str || navigator.userAgent}` +User-Agent String: ${prefs.mode === 'custom' ? custom : prefs.ua || navigator.userAgent}` }); } @@ -194,3 +207,7 @@ chrome.storage.local.get({ chrome.runtime.getManifest().homepage_url + '?rd=feedback&name=' + name + '&version=' + version ); } + +chrome.tabs.create({ + url: 'data/popup/index.html' +}) diff --git a/data/inject.js b/data/inject.js new file mode 100644 index 0000000..7a04eb5 --- /dev/null +++ b/data/inject.js @@ -0,0 +1,45 @@ +'use strict'; + +// iframe.contentWindow +if ( + window !== top && + location.href === 'about:blank' +) { + try { + top.document; // are we on the same frame? + + const script = document.createElement('script'); + script.textContent = `{ + const nav = top.navigator; + + navigator.__defineGetter__('userAgent', () => nav.userAgent); + navigator.__defineGetter__('appVersion', () => nav.appVersion); + navigator.__defineGetter__('platform', () => nav.platform); + navigator.__defineGetter__('vendor', () => nav.vendor); + + document.documentElement.dataset.fgdvcre = true; + }`; + document.documentElement.appendChild(script); + // make sure the script is injected + if (document.documentElement.dataset.fgdvcre !== 'true') { + document.documentElement.dataset.fgdvcre = true; + const script = document.createElement('script'); + Object.assign(script, { + textContent: ` + [...document.querySelectorAll('iframe[sandbox]')] + .filter(i => i.contentDocument.documentElement.dataset.fgdvcre === 'true') + .forEach(i => { + const nav = i.contentWindow.navigator; + nav.__defineGetter__('userAgent', () => navigator.userAgent); + nav.__defineGetter__('appVersion', () => navigator.appVersion); + nav.__defineGetter__('platform', () => navigator.platform); + nav.__defineGetter__('vendor', () => navigator.vendor); + }); + ` + }); + top.document.documentElement.appendChild(script); + } + delete document.documentElement.dataset.fgdvcre; + } + catch (e) {} +} diff --git a/data/options/index.html b/data/options/index.html index f40102b..aeca2cd 100644 --- a/data/options/index.html +++ b/data/options/index.html @@ -3,12 +3,11 @@
- + | |
- + | |
- Insert a sample. + Insert a sample JSON object. | |
+ | |
@@ -44,6 +43,7 @@ |
+
diff --git a/data/options/index.js b/data/options/index.js index b0052b6..49e2736 100644 --- a/data/options/index.js +++ b/data/options/index.js @@ -54,7 +54,9 @@ function restore() { document.addEventListener('DOMContentLoaded', restore); document.getElementById('save').addEventListener('click', save); -document.getElementById('sample').addEventListener('click', () => { +document.getElementById('sample').addEventListener('click', e => { + e.preventDefault(); + document.getElementById('custom').value = JSON.stringify({ 'www.google.com': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36', 'www.bing.com': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0' @@ -63,6 +65,19 @@ document.getElementById('sample').addEventListener('click', () => { document.getElementById('donate').addEventListener('click', () => { chrome.tabs.create({ - url: 'https://www.paypal.me/addondonation/10usd' + url: chrome.runtime.getManifest().homepage_url + '?rd=donate' }); }); + +document.getElementById('reset').addEventListener('click', e => { + if (e.detail === 1) { + notify('Double-click to reset!'); + } + else { + localStorage.clear(); + chrome.storage.local.clear(() => { + chrome.runtime.reload(); + window.close(); + }); + } +}); diff --git a/data/popup/explore.js b/data/popup/explore.js new file mode 120000 index 0000000..a6149f5 --- /dev/null +++ b/data/popup/explore.js @@ -0,0 +1 @@ +../../../explore.js \ No newline at end of file diff --git a/data/popup/index.css b/data/popup/index.css index bb1cc6b..0d71e90 100644 --- a/data/popup/index.css +++ b/data/popup/index.css @@ -26,7 +26,7 @@ body { background-color: #fff; font-family: "Helvetica Neue",Helvetica,sans-serif; font-size: 13px; - width: 700px; + width: 600px; } table { width: 100%; @@ -35,7 +35,6 @@ table { fieldset { border: solid 1px #ccc; } -select, input[type=search], input[type=text] { width: 100%; @@ -43,15 +42,15 @@ input[type=text] { text-indent: 5px; padding-right: 5px; } -select, input { outline: none; background-color: #fff; color: #000; - border: solid 1px #ccc; + border: solid 1px #e7e7e7; box-sizing: border-box; height: 24px; border-radius: 0; + font-size: 11px; } input[type=button] { cursor: pointer; @@ -64,29 +63,59 @@ input[type=button]:disabled { opacity: 0.2; cursor: default; } - +select { + -webkit-appearance: none; + -moz-appearance: none; + border: none; + user-select: none; + outline: none; + background: rgba(255,255,255,.5) url(list.svg) no-repeat center right 4px; + background-size: 8px; + font-size: 13px; + border-radius: 0; + padding: 2px 16px 2px 4px; +} #list { - margin: 20px 0; overflow: auto; height: 300px; - background-color: rgba(0, 0, 0, 0.05); + margin-bottom: 10px; + background-color: #fdfafa; + background-position: top 120px center; + background-repeat: no-repeat; } #list[data-loading=true] { - background: url(loading.gif) top 120px center no-repeat; + background-image: url(loading.gif); background-size: 64px; } #list table { table-layout: fixed; } +#list th { + height: 30px; + background-color: #e7e7e7; +} #list tr { cursor: pointer; } #list tr[data-matched=false] { opacity: 0.5; } +#list tbody { + position: relative; +} #list tbody tr:nth-child(odd) { background-color: #fff; } +#list tbody tr:nth-child(even) { + background-color: #f5f5f5; +} +#list[data-loading=false] tbody:empty:before { + content: 'no user-agent string for this query!'; + display: block; + position: absolute; + top: 50px; + left: 10px; +} #list td:nth-child(1) { text-align: center; } @@ -117,3 +146,26 @@ input[type=button]:disabled { #info:empty { display: none; } + +[data-cmd="apply"] { + color: #fff; + background-color: #3c923c; + border: solid 1px #327932; + margin-right: 2px; + margin-left: 2px; +} + +[data-cmd="reset"] { + color: #fff; + background-color: #eea345; + border: solid 1px #ec9730; +} + +[data-cmd="refresh"] { + background-color: #f5f5f5; +} + +#explore:not([data-loaded="true"]) { + margin-top: -8px; + height: 16px; +} diff --git a/data/popup/index.html b/data/popup/index.html index d70c898..8099d81 100644 --- a/data/popup/index.html +++ b/data/popup/index.html @@ -5,132 +5,132 @@ -- | Browser | -OS | -String | ++ + + | ++ + | ++ User-Agent + + |
---|