diff --git a/extension/ReadMe.txt b/extension/ReadMe.txt index 0e3c1cd..635db5b 100644 --- a/extension/ReadMe.txt +++ b/extension/ReadMe.txt @@ -1,2 +1,2 @@ ua-parser.min.js: - https://github.com/faisalman/ua-parser-js/releases/tag/0.7.19 + https://github.com/faisalman/ua-parser-js/releases/tag/0.7.20 diff --git a/extension/common.js b/extension/common.js index c405a38..d8d0430 100644 --- a/extension/common.js +++ b/extension/common.js @@ -2,6 +2,7 @@ 'use strict'; + const cache = {}; const tabs = {}; chrome.tabs.onRemoved.addListener(id => delete cache[id]); @@ -17,10 +18,15 @@ const prefs = { cache: true, exactMatch: false, protected: ['google.com/recaptcha', 'gstatic.com/recaptcha'], - parser: {} // maps ua string to a ua object + parser: {}, // maps ua string to a ua object, + log: false }; + +const log = (...args) => prefs.log && console.log(...args); + // exand comma-separated keys of prefs.custom const expand = () => { + log('expanding custom rules'); expand.rules = {}; for (const key of Object.keys(prefs.custom)) { for (const k of key.split(/\s*,\s*/)) { @@ -82,14 +88,18 @@ const ua = { 'global': {} }, diff(tabId) { // returns true if there is per window object + log('ua.diff is called', tabId); const windowId = tabs[tabId]; return windowId in this._obj; }, get windows() { + log('ua.windows is called'); return Object.keys(this._obj).filter(id => id !== 'global').map(s => Number(s)); }, parse: s => { + log('ua.parse is called', s); if (prefs.parser[s]) { + log('ua.parse is resolved using parser'); return Object.assign({ userAgent: s }, prefs.parser[s]); @@ -140,10 +150,12 @@ const ua = { return o; }, object(tabId, windowId) { + log('ua.object is called', tabId, windowId); windowId = windowId || (tabId ? tabs[tabId] : 'global'); return this._obj[windowId] || this._obj.global; }, string(str, windowId) { + log('ua.string is called', str, windowId); if (str) { this._obj[windowId] = this.parse(str); } @@ -152,12 +164,14 @@ const ua = { } }, tooltip(title, tabId) { + log('ua.tooltip is called', title, tabId); chrome.browserAction.setTitle({ title, tabId }); }, icon(mode, tabId) { + log('ua.icon is called', mode, tabId); chrome.browserAction.setIcon({ tabId, path: { @@ -172,6 +186,7 @@ const ua = { }); }, toolbar: ({windowId, tabId}) => { + log('ua.toolbar is called', windowId, tabId); if (windowId) { chrome.tabs.query({ windowId @@ -191,9 +206,13 @@ const ua = { } }, update(str = prefs.ua, windowId = 'global') { + log('ua.update is called', str, windowId); + // clear caching + Object.keys(cache).forEach(key => delete cache[key]); + // remove old listeners 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); chrome.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, { @@ -224,6 +243,7 @@ if (chrome.windows) { // FF on Android } function hostname(url) { + log('hostname', url); const s = url.indexOf('//') + 2; if (s > 1) { let o = url.indexOf('/', s); @@ -246,6 +266,7 @@ function hostname(url) { } // returns true, false or an object; true: ignore, false: use from ua object. function match({url, tabId}) { + log('match', url, tabId); const h = hostname(url); if (prefs.mode === 'blacklist') { @@ -364,10 +385,16 @@ const onCommitted = ({frameId, url, tabId}) => { }\`; document.documentElement.appendChild(script); script.remove(); + navigator.userAgent }` - }, () => { - if (chrome.runtime.lastError) { + }, r => { + const lastError = chrome.runtime.lastError; + if (lastError && + lastError.message !== 'No matching message handler' && // Firefox on Windows + lastError.message !== 'document.documentElement is null' // Firefox on Windows + ) { if (frameId === 0) { + console.log(lastError); ua.tooltip('[Default] ' + navigator.userAgent, tabId); ua.icon('ignored', tabId); } diff --git a/extension/data/options/index.html b/extension/data/options/index.html index ad31549..5920b03 100644 --- a/extension/data/options/index.html +++ b/extension/data/options/index.html @@ -24,7 +24,7 @@ @@ -32,7 +32,7 @@ @@ -40,20 +40,12 @@ - - - - - - @@ -64,11 +56,22 @@ - + + + + + + + + + +
- +
- +
- Press here to insert a sample JSON object. + Press here to insert a sample JSON object.
- Press here to insert a sample JSON object. -
A comma-separated list of keywords that the extension should not spoof the user-agent header. Use this list to protect URLs that contain these protected keywords. Each keyword need to be at least 5 char long.
Disable Spoofing A comma-separated list of keywords that the extension should not spoof the user-agent header. Use this list to protect URLs that contain these protected keywords. Each keyword need to be at least 5 char long.
+ Custom User-Agent Parsing: A JSON object to bypass the internal user-agent string parsing method. The keys are the actual user-agent strings and the value of each key is an object of the keys that need to be set for the "navigator" object. You can use the "[delete]" keyword if you want a key in the "navigator" object to get deleted. Press here to insert a sample JSON object. +

diff --git a/extension/data/options/index.js b/extension/data/options/index.js index 3edb623..6fb3020 100644 --- a/extension/data/options/index.js +++ b/extension/data/options/index.js @@ -43,6 +43,7 @@ function save() { chrome.storage.local.set({ exactMatch: document.getElementById('exactMatch').checked, faqs: document.getElementById('faqs').checked, + log: document.getElementById('log').checked, cache: document.getElementById('cache').checked, blacklist: prepare(document.getElementById('blacklist').value), whitelist: prepare(document.getElementById('whitelist').value), @@ -63,6 +64,7 @@ function restore() { chrome.storage.local.get({ exactMatch: false, faqs: true, + log: false, cache: true, mode: 'blacklist', whitelist: [], @@ -73,6 +75,7 @@ function restore() { }, prefs => { document.getElementById('exactMatch').checked = prefs.exactMatch; document.getElementById('faqs').checked = prefs.faqs; + document.getElementById('log').checked = prefs.log; document.getElementById('cache').checked = prefs.cache; document.querySelector(`[name="mode"][value="${prefs.mode}"`).checked = true; document.getElementById('blacklist').value = prefs.blacklist.join(', '); diff --git a/extension/data/popup/index.css b/extension/data/popup/index.css index 6eaaff2..f028f8e 100644 --- a/extension/data/popup/index.css +++ b/extension/data/popup/index.css @@ -26,7 +26,7 @@ body { background-color: #fff; font-family: "Helvetica Neue", Helvetica, sans-serif; font-size: 13px; - width: 650px; + min-width: 650px; margin: 0; } table { @@ -89,10 +89,10 @@ select { #list { overflow: auto; scroll-behavior: smooth; - height: 200px; + height: 268px; margin-bottom: 16px; color: #000; - background-position: top 88px center; + background-position: top 120px center; background-repeat: no-repeat; font-size: 11px; } @@ -210,6 +210,7 @@ body[data-android="true"] [data-cmd="window"] { } #view { background-color: #f5f5f5; + padding: 5px 0; } #view td { text-align: right; diff --git a/extension/data/popup/index.js b/extension/data/popup/index.js index c33792d..1f43f68 100644 --- a/extension/data/popup/index.js +++ b/extension/data/popup/index.js @@ -85,7 +85,6 @@ function update(ua) { } document.addEventListener('change', ({target}) => { - console.log(target); if (target.closest('#filter')) { localStorage.setItem(target.id, target.value); chrome.storage.local.get({ diff --git a/extension/extension.zip b/extension/extension.zip new file mode 100644 index 0000000..d162087 Binary files /dev/null and b/extension/extension.zip differ diff --git a/extension/manifest.json b/extension/manifest.json index 7f275ad..f81b535 100755 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "User-Agent Switcher and Manager", "short_name": "useragent-switcher", - "version": "0.3.2", + "version": "0.3.3", "description": "A highly customizable extension to spoof the User-Agent string of your browser with a new one globally, randomly or per hostname", diff --git a/extension/ua-parser.min.js b/extension/ua-parser.min.js old mode 100755 new mode 100644 index c12f2ef..6e8acb3 --- a/extension/ua-parser.min.js +++ b/extension/ua-parser.min.js @@ -1,9 +1,9 @@ /*! - * UAParser.js v0.7.19 + * UAParser.js v0.7.20 * Lightweight JavaScript-based User-Agent string parser * https://github.com/faisalman/ua-parser-js * - * Copyright © 2012-2016 Faisal Salman - * Dual licensed under GPLv2 or MIT + * Copyright © 2012-2019 Faisal Salman + * Licensed under MIT License */ -(function(window,undefined){"use strict";var LIBVERSION="0.7.19",EMPTY="",UNKNOWN="?",FUNC_TYPE="function",UNDEF_TYPE="undefined",OBJ_TYPE="object",STR_TYPE="string",MAJOR="major",MODEL="model",NAME="name",TYPE="type",VENDOR="vendor",VERSION="version",ARCHITECTURE="architecture",CONSOLE="console",MOBILE="mobile",TABLET="tablet",SMARTTV="smarttv",WEARABLE="wearable",EMBEDDED="embedded";var util={extend:function(regexes,extensions){var margedRegexes={};for(var i in regexes){if(extensions[i]&&extensions[i].length%2===0){margedRegexes[i]=extensions[i].concat(regexes[i])}else{margedRegexes[i]=regexes[i]}}return margedRegexes},has:function(str1,str2){if(typeof str1==="string"){return str2.toLowerCase().indexOf(str1.toLowerCase())!==-1}else{return false}},lowerize:function(str){return str.toLowerCase()},major:function(version){return typeof version===STR_TYPE?version.replace(/[^\d\.]/g,"").split(".")[0]:undefined},trim:function(str){return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"")}};var mapper={rgx:function(ua,arrays){var i=0,j,k,p,q,matches,match;while(i0){if(q.length==2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length==3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},str:function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j0){if(q.length==2){if(typeof q[1]==FUNC_TYPE){this[q[0]]=q[1].call(this,match)}else{this[q[0]]=q[1]}}else if(q.length==3){if(typeof q[1]===FUNC_TYPE&&!(q[1].exec&&q[1].test)){this[q[0]]=match?q[1].call(this,match,q[2]):undefined}else{this[q[0]]=match?match.replace(q[1],q[2]):undefined}}else if(q.length==4){this[q[0]]=match?q[3].call(this,match.replace(q[1],q[2])):undefined}}else{this[q]=match?match:undefined}}}}i+=2}},str:function(str,map){for(var i in map){if(typeof map[i]===OBJ_TYPE&&map[i].length>0){for(var j=0;j