├── .eslintrc ├── .gitignore ├── .travis.yml ├── README.md ├── assets └── user_agent_overrider-logo.svg ├── docs ├── Build.md ├── Preference.md └── README.md ├── gulpfile.js ├── gulpfiles ├── .eslintrc ├── basic.js ├── commander.js ├── common.js ├── develop.js ├── gconfig.js └── product.js ├── package.json └── src ├── _includes ├── .eslintrc ├── assets │ ├── builtin_user_agents.txt │ ├── developer_note.js │ └── license_banner.js ├── bootstrap.js ├── jslib │ ├── BrowserManager.js │ ├── Pref.js │ ├── StyleManager.js │ ├── ToolbarManager.js │ ├── UAManager.js │ ├── Utils.js │ ├── logging.js │ └── xulComp.js └── options.js ├── bootstrap.js.nj ├── chrome.manifest.nj ├── content ├── options.js.nj └── options.xul ├── icon.png ├── install.rdf.nj ├── locale ├── bg │ ├── global.dtd │ └── global.properties ├── de │ ├── global.dtd │ └── global.properties ├── en-US │ ├── global.dtd │ └── global.properties ├── es-ES │ ├── global.dtd │ └── global.properties ├── es │ ├── global.dtd │ └── global.properties ├── fr │ ├── global.dtd │ └── global.properties ├── it │ ├── global.dtd │ └── global.properties ├── nl_NL │ ├── global.dtd │ └── global.properties ├── ro │ ├── global.dtd │ └── global.properties ├── ru │ ├── global.dtd │ └── global.properties ├── tr_TR │ ├── global.dtd │ └── global.properties ├── zh-CN │ ├── global.dtd │ └── global.properties └── zh-TW │ ├── global.dtd │ └── global.properties └── skin ├── browser.css ├── icon16.png ├── icon24.png └── icon32.png /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "es6": true 5 | }, 6 | "rules": { 7 | "comma-dangle": [2, "always-multiline"], 8 | "eol-last": 2, 9 | "eqeqeq": 2, 10 | "key-spacing": [2, {"afterColon": true}], 11 | "keyword-spacing": 2, 12 | "no-dupe-args": 2, 13 | "no-dupe-keys": 2, 14 | "no-eval": 2, 15 | "no-extend-native": 2, 16 | "no-extra-bind": 2, 17 | "no-extra-semi": 2, 18 | "no-mixed-spaces-and-tabs": 2, 19 | "no-spaced-func": 2, 20 | "no-trailing-spaces": 2, 21 | "no-undef": 2, 22 | "no-underscore-dangle": 0, 23 | "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], 24 | "quotes": [2, "single", "avoid-escape"], 25 | "semi": [2, "always"], 26 | "space-before-blocks": 2, 27 | "space-infix-ops": 2, 28 | "space-in-parens": 2, 29 | "strict": [2, "global"] 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | notifications: 4 | email: false 5 | 6 | matrix: 7 | include: 8 | - os: linux 9 | dist: trusty 10 | sudo: false 11 | node_js: '7' 12 | 13 | install: 14 | - npm install 15 | 16 | script: 17 | - npm run build 18 | - ls dist/xpi 19 | 20 | deploy: 21 | provider: releases 22 | api_key: 23 | secure: ZbxaezVd3paGnCZFWoY1peo0LkIUooD9mdjDNC2AwNAopCh/YPgCD0IdGddLNxzuQ6HPfrf8Xy49toLxwAyLn/7FrFMDLyMsDSg8yofFfLbpLx9FQ+K6dXzeDxnjnC4dfSXux4pcNyrr2pveZWcVoX7fVplkKSIcF+Kgu7Yj2pJCvvjWD1ng9UZ18XJUUsbtQtX4ktpDDgcgUBrEExEiLgLIxoudjf2+wcmXNhYS0P28koKKjR8EYYVTkh+VlUugOVU6GeCIzLmZcs4hJQ9kcnhn54IKQ8SAP5+uQ99vHy+smXE1MH/n/n/xHUbckUkdvuMSGww5utq3Q29XoA8PWPOPy/fW5aKP9aQBtUGYsnOfgyDtA4WDRTxWhIvyj1aiInwu72Z/cFPADyH7LXfBVElY14zprOzpGYftYKr+l4X6NsvDuHMm8+PyAWOojhis8KqlM4tyoCKmPR5PMHPwcG5u6Y6m1l2g9VdKrl45eeA4/KtBZclrQykhIwIlgFhjgIR1Sa8JEM0zSYYnQyi6eTaHZMlEdLt7/omo3qqxMkzMmS2WFdmFRKj5oNTiTgf3fbs88ZyrdFrnSttVgWEWI8tv6tE5TndC3iHphMUHxusw+GK2FrLZpXdPKWMjioTxnEBgWEg+bTJeINZg6gQ/4WmAm/AqW8tH86EI3wU/sRo= 24 | file_glob: true 25 | file: dist/xpi/*.xpi 26 | overwrite: true 27 | skip_cleanup: true 28 | on: 29 | tags: true 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | User Agent Overrider 2 | ==================== 3 | 4 | [![Build Status](https://travis-ci.org/muzuiget/user_agent_overrider.svg?branch=master)](https://travis-ci.org/muzuiget/user_agent_overrider) 5 | 6 | **Note:** Because Firefox 57 will [drop support legacy add-ons](https://blog.mozilla.org/addons/2017/02/16/the-road-to-firefox-57-compatibility-milestones/), and I don't had any time to migrate it to WebExtensions yet, this project is in maintain mode, bugfix only, no new feature. For Firefox 57+ users, I recommend to use [User Agent Switcher](https://addons.mozilla.org/firefox/addon/uaswitcher/) instead. 7 | 8 | ## Introduction 9 | 10 | User Agent Overrider is a Firefox extension to override the [user-agent](https://en.wikipedia.org/wiki/User_agent) string. 11 | 12 | ## Installation 13 | 14 | * [Github Release Page](https://github.com/muzuiget/user_agent_overrider/releases), pre-release, unsigned xpi file. 15 | 16 | * [Mozilla Addons Page](https://addons.mozilla.org/firefox/addon/user-agent-overrider/), formal release, signed xpi file, reviewed by AMO. 17 | 18 | ## Docs 19 | 20 | * [docs](docs/) 21 | 22 | ## License 23 | 24 | [MPLv2](http://www.mozilla.org/MPL/2.0/) 25 | 26 | -------------------------------------------------------------------------------- /assets/user_agent_overrider-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 40 | 42 | 44 | 48 | 52 | 53 | 61 | 65 | 69 | 70 | 73 | 76 | 77 | 79 | 83 | 87 | 88 | 90 | 99 | 100 | 108 | 118 | 128 | 130 | 134 | 138 | 139 | 147 | 149 | 153 | 157 | 158 | 160 | 164 | 168 | 169 | 171 | 175 | 179 | 183 | 184 | 187 | 190 | 191 | 193 | 197 | 201 | 202 | 209 | 212 | 213 | 221 | 225 | 229 | 230 | 239 | 248 | 257 | 266 | 275 | 285 | 295 | 304 | 314 | 316 | 320 | 324 | 325 | 333 | 335 | 339 | 343 | 347 | 351 | 352 | 361 | 370 | 379 | 388 | 397 | 399 | 403 | 407 | 408 | 417 | 427 | 437 | 447 | 457 | 466 | 474 | 478 | 482 | 483 | 492 | 503 | 514 | 523 | 534 | 544 | 555 | 564 | 566 | 571 | 572 | 579 | 582 | 583 | 592 | 594 | 598 | 602 | 603 | 612 | 614 | 618 | 622 | 623 | 632 | 634 | 638 | 642 | 643 | 653 | 663 | 673 | 683 | 693 | 702 | 711 | 722 | 733 | 742 | 753 | 764 | 774 | 784 | 794 | 804 | 814 | 824 | 835 | 846 | 856 | 866 | 876 | 886 | 887 | 889 | 890 | 892 | image/svg+xml 893 | 895 | 896 | 897 | 898 | 899 | 912 | 917 | 920 | 923 | 929 | 935 | 941 | 951 | 961 | 962 | 971 | 976 | 981 | 986 | 989 | 999 | 1000 | 1003 | 1005 | 1010 | 1015 | 1021 | 1027 | 1033 | 1039 | 1045 | 1051 | 1056 | 1061 | 1067 | 1068 | 1069 | 1072 | 1079 | 1084 | 1089 | 1094 | 1099 | 1104 | 1109 | 1114 | 1115 | 1116 | 1117 | -------------------------------------------------------------------------------- /docs/Build.md: -------------------------------------------------------------------------------- 1 | Build 2 | ===== 3 | 4 | ## system dependencies 5 | 6 | The project build scripts base on NodeJS, you need NodeJS 6+ and npm tools in your system. 7 | 8 | ## how to build 9 | 10 | Get the source code 11 | 12 | git clone https://github.com/muzuiget/user_agent_overrider.git 13 | cd user_agent_overrider 14 | 15 | Install NodeJS modules, a "node_modules" folder will be create in project fodler; 16 | 17 | npm install 18 | 19 | Run build task 20 | 21 | $ npm run build 22 | 23 | > user_agent_overrider@0.4.1 build ~/user_agent_overrider 24 | > gulp build 25 | 26 | [15:04:22] Using gulpfile ~/user_agent_overrider/gulpfile.js 27 | [15:04:22] Starting 'build'... 28 | [15:04:22] Starting 'remake'... 29 | [15:04:22] Starting 'clean'... 30 | [15:04:22] Finished 'clean' after 20 ms 31 | [15:04:22] Starting 'make'... 32 | [15:04:22] Starting 'copy'... 33 | [15:04:22] Starting 'metainfo'... 34 | [15:04:22] Starting 'manifest'... 35 | [15:04:22] Starting 'script'... 36 | [15:04:22] Finished 'metainfo' after 67 ms 37 | [15:04:22] Finished 'manifest' after 63 ms 38 | [15:04:22] Finished 'script' after 64 ms 39 | [15:04:22] Finished 'copy' after 87 ms 40 | [15:04:22] Finished 'make' after 87 ms 41 | [15:04:22] Finished 'remake' after 108 ms 42 | [15:04:22] Starting 'product'... 43 | [15:04:22] Starting 'create-xpi'... 44 | [15:04:22] Finished 'create-xpi' after 39 ms 45 | [15:04:22] Finished 'product' after 39 ms 46 | [15:04:22] Finished 'build' after 147 ms 47 | 48 | The xpi will be place in dist/xpi 49 | 50 | $ ls dist/xpi/user_agent_overrider-0.4.1.xpi 51 | dist/xpi/user_agent_overrider-0.4.1.xpi 52 | 53 | -------------------------------------------------------------------------------- /docs/Preference.md: -------------------------------------------------------------------------------- 1 | Preference 2 | ========== 3 | 4 | We reference "User Agent Overrider" as "UAO". 5 | 6 | ## Format 7 | 8 | You can custom the toolbar button menu item in preference panel. 9 | 10 | One line one menu item and user-agent string pair. format as below: 11 | 12 | 13 | # line starts with `#` is comment 14 | menu_item_label: user_agent_string_for_this_menu_item_label 15 | # ^ 16 | # the first colon `:` character as separator 17 | 18 | 19 | Empty line, comment and other invalid format line will be ignored. 20 | 21 | If you remove or rename the checked menuitem's label, the extension will deactivate automatically. 22 | 23 | ## Example 24 | 25 | Here is the [built-in user-agents file](https://github.com/muzuiget/user_agent_overrider/blob/master/src/_includes/assets/builtin_user_agents.txt) 26 | 27 | Here is legacy Microsoft IE list which does not ship in latest version 28 | 29 | # IE 30 | Windows / IE 6: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) 31 | Windows / IE 7: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1) 32 | Windows / IE 8: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0) 33 | Windows / IE 9: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0) 34 | Windows / IE 10: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0) 35 | 36 | ## Check your user-agent 37 | 38 | ### Method 1 39 | 40 | Open `about:config`, find `general.useragent.override` item. 41 | 42 | This what and the only thing UAO actually modifty Firefox configure. 43 | 44 | The value should be **the same as** your choice in UAO, if not, there is a bug. 45 | 46 | ### Method 2 47 | 48 | Open Firefox DevTools, switch to "Network" tab, visit any url, select a request item on left panel, you will see a "User-Agent" below "Requeset headers" section. 49 | 50 | This user-agent Firefox finally send out, **maybe not the same** your choice in UAO, because other Firefox extension may change it again, is not the UAO bug. 51 | 52 | ### Method 3 53 | 54 | Visit url http://httpbin.org/user-agent 55 | 56 | This page will echo what user-agent reach the server, also **maybe not the same** your choice in UAO, because firewall or proxy in your network will change it again, is not the UAO bug. 57 | 58 | ## Useful sites to get user agent: 59 | 60 | * http://www.useragentstring.com/ 61 | * http://www.user-agents.org/ 62 | * http://whatsmyuseragent.com/ 63 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | * [Build](Build.md) 5 | * [Preference](Preference.md) 6 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | /* eslint strict: [2, "global"] */ 3 | 4 | 'use strict'; 5 | 6 | let gulp = require('gulp'); 7 | let runSeq = require('run-sequence'); 8 | 9 | require('./gulpfiles/commander'); 10 | require('./gulpfiles/basic'); 11 | require('./gulpfiles/common'); 12 | require('./gulpfiles/develop'); 13 | require('./gulpfiles/product'); 14 | 15 | gulp.task('make', function(callback) { 16 | runSeq(['copy', 'metainfo', 'manifest', 'script'], callback); 17 | }); 18 | 19 | gulp.task('remake', function(callback) { 20 | runSeq('clean', 'make', callback); 21 | }); 22 | 23 | gulp.task('build', function(callback) { 24 | runSeq('remake', 'product', callback); 25 | }); 26 | 27 | gulp.task('default', function(callback) { 28 | runSeq('remake', ['process', 'livereload', 'watch'], callback); 29 | }); 30 | -------------------------------------------------------------------------------- /gulpfiles/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /gulpfiles/basic.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let del = require('del'); 4 | let gulp = require('gulp'); 5 | let eslint = require('gulp-eslint'); 6 | 7 | gulp.task('clean', function() { 8 | return del(['dist']); 9 | }); 10 | 11 | gulp.task('eslint', function() { 12 | let files = [ 13 | 'gulpfile.js', 14 | 'gulpfiles/**/*.js', 15 | 'src/**/*.js', 16 | ]; 17 | return gulp.src(files) 18 | .pipe(eslint()) 19 | .pipe(eslint.format()) 20 | .pipe(eslint.failOnError()); 21 | }); 22 | -------------------------------------------------------------------------------- /gulpfiles/commander.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let commander = require('commander'); 4 | 5 | commander 6 | .option('-p, --profile-folder ', 'develop with with a specified profile folder') 7 | .option('-v, --metainfo-version ', 'make with specified release version') 8 | .option('-u, --metainfo-unpack ', 'mark xpi file need to unpack when install') 9 | .parse(process.argv); 10 | -------------------------------------------------------------------------------- /gulpfiles/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let fs = require('fs'); 4 | let gulp = require('gulp'); 5 | let lazypipe = require('lazypipe'); 6 | let nunjucks = require('gulp-nunjucks'); 7 | let propertiesReader = require('properties-reader'); 8 | let rename = require('gulp-rename'); 9 | 10 | let gconfig = require('./gconfig'); 11 | let packageJSON = require('../package.json'); 12 | 13 | let idname = packageJSON.name.replace(/_/g, ''); 14 | 15 | let tasks = [ 16 | 17 | { 18 | name: 'copy', 19 | files: [ 20 | 'src/**/*', 21 | '!src/**/*.nj', 22 | '!src/_includes/', 23 | '!src/_includes/**/*', 24 | ], 25 | base: 'src', 26 | pipes: function() { 27 | let pipes = lazypipe() 28 | .pipe(gulp.dest, 'dist/nonpack/'); 29 | return pipes(); 30 | }, 31 | }, 32 | 33 | { 34 | name: 'metainfo', 35 | files: [ 36 | 'src/install.rdf.nj', 37 | ], 38 | base: 'src', 39 | pipes: function() { 40 | let defaultPath = 'src/locale/en-US/global.properties'; 41 | let defaultProp = propertiesReader(defaultPath); 42 | let defauleLocaleName = defaultProp.get('extensionName'); 43 | let defauleLocaleDesc = defaultProp.get('extensionDesc'); 44 | 45 | let langs = fs.readdirSync('src/locale'); 46 | let locales = []; 47 | for (let lang of langs) { 48 | if (lang === 'en-US') { 49 | continue; 50 | } 51 | 52 | let path = `src/locale/${lang}/global.properties`; 53 | let prop = propertiesReader(path); 54 | let name = prop.get('extensionName'); 55 | let desc = prop.get('extensionDescription'); 56 | let locale = { 57 | lang: lang, 58 | name: name || defauleLocaleName, 59 | desc: desc || defauleLocaleDesc, 60 | }; 61 | locales.push(locale); 62 | } 63 | 64 | let nunjucksData = { 65 | version: gconfig.metainfoVersion, 66 | unpack: gconfig.metainfoUnpack, 67 | locales: locales, 68 | }; 69 | 70 | let pipes = lazypipe() 71 | .pipe(nunjucks.compile, nunjucksData) 72 | .pipe(rename, {extname: ''}) 73 | .pipe(gulp.dest, 'dist/nonpack/'); 74 | return pipes(); 75 | }, 76 | }, 77 | 78 | { 79 | name: 'manifest', 80 | files: [ 81 | 'src/chrome.manifest.nj', 82 | ], 83 | base: 'src', 84 | pipes: function() { 85 | let langs = fs.readdirSync('src/locale'); 86 | let pipes = lazypipe() 87 | .pipe(nunjucks.compile, {idname, langs}) 88 | .pipe(rename, {extname: ''}) 89 | .pipe(gulp.dest, 'dist/nonpack/'); 90 | return pipes(); 91 | }, 92 | }, 93 | 94 | { 95 | name: 'script', 96 | files: [ 97 | 'src/**/*.js.nj', 98 | ], 99 | base: 'src', 100 | entirety: true, 101 | pipes: function() { 102 | let pipes = lazypipe() 103 | .pipe(nunjucks.compile) 104 | .pipe(rename, {extname: ''}) 105 | .pipe(gulp.dest, 'dist/nonpack/'); 106 | return pipes(); 107 | }, 108 | }, 109 | 110 | ]; 111 | 112 | tasks.forEach(function(task) { 113 | gulp.task(task.name, function() { 114 | return gulp.src(task.files, {base: task.base}) 115 | .pipe(task.pipes()); 116 | }); 117 | }); 118 | 119 | module.exports = {tasks}; 120 | -------------------------------------------------------------------------------- /gulpfiles/develop.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let gulp = require('gulp'); 4 | let gutil = require('gulp-util'); 5 | let gulpProcess = require('gulp-process'); 6 | 7 | let gconfig = require('./gconfig'); 8 | let commonTasks = require('./common').tasks; 9 | 10 | gulp.task('watch', function() { 11 | let onChange = function(task, event) { 12 | let files = task.entirety ? task.files : event.path; 13 | gulp.src(files, {base: task.base}) 14 | .pipe(task.pipes()) 15 | .on('error', (error) => { 16 | gutil.log(error.message); 17 | }); 18 | }; 19 | commonTasks.forEach(function(task) { 20 | gulp.watch(task.files).on('change', (e) => onChange(task, e)); 21 | }); 22 | }); 23 | 24 | gulp.task('process', function(callback) { 25 | let folder = gconfig.profileFolder; 26 | if (!folder) { 27 | callback(); 28 | return; 29 | } 30 | gutil.log(`Launching Firefix with profile ${folder}`); 31 | let args = ['--no-remote', '-profile', folder]; 32 | gulpProcess.start('firefoxProcess', 'firefox', args); 33 | }); 34 | 35 | gulp.task('livereload', function() { 36 | let files = [ 37 | 'dist/**/*', 38 | ]; 39 | gulp.watch(files).on('change', () => { 40 | gulpProcess.restart('firefoxProcess'); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /gulpfiles/gconfig.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let commander = require('commander'); 4 | let packageJSON = require('../package.json'); 5 | 6 | let isBuildTask = commander.args.includes('build'); 7 | 8 | let metainfoVersion = packageJSON.version; 9 | if (commander.metainfoVersion) { 10 | metainfoVersion = commander.metainfoVersion; 11 | } 12 | 13 | let metainfoUnpack = !isBuildTask; 14 | if (commander.metainfoUnpack === 'true') { 15 | metainfoUnpack = true; 16 | } 17 | 18 | let gconfig = { 19 | profileFolder: commander.profileFolder, 20 | metainfoVersion: metainfoVersion, 21 | metainfoUnpack: metainfoUnpack, 22 | }; 23 | 24 | module.exports = gconfig; 25 | -------------------------------------------------------------------------------- /gulpfiles/product.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let gulp = require('gulp'); 4 | let runSeq = require('run-sequence'); 5 | let zip = require('gulp-zip'); 6 | 7 | let gconfig = require('./gconfig'); 8 | 9 | gulp.task('create-xpi', function() { 10 | let version = gconfig.metainfoVersion; 11 | let files = [ 12 | 'dist/nonpack/*', 13 | 'dist/nonpack/*/**', 14 | ]; 15 | return gulp.src(files) 16 | .pipe(zip(`user_agent_overrider-${version}.xpi`)) 17 | .pipe(gulp.dest('dist/xpi/')); 18 | }); 19 | 20 | gulp.task('product', function(callback) { 21 | runSeq('create-xpi', callback); 22 | }); 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "user_agent_overrider", 3 | "version": "0.5.2", 4 | "private": true, 5 | "devDependencies": { 6 | "commander": "2.9.0", 7 | "del": "2.2.2", 8 | "gulp": "3.9.1", 9 | "gulp-eslint": "3.0.1", 10 | "gulp-nunjucks": "3.0.0", 11 | "gulp-process": "0.1.2", 12 | "gulp-rename": "1.2.2", 13 | "gulp-util": "3.0.8", 14 | "gulp-zip": "4.0.0", 15 | "lazypipe": "1.0.1", 16 | "properties-reader": "0.0.15", 17 | "run-sequence": "1.2.2" 18 | }, 19 | "scripts": { 20 | "build": "gulp build" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/_includes/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "strict": [2, "never"] 4 | }, 5 | "globals": { 6 | "Components": true, 7 | "Iterator": true, 8 | "dump": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/_includes/assets/builtin_user_agents.txt: -------------------------------------------------------------------------------- 1 | Linux / Firefox 54: Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0 2 | Mac OS X/ Safari: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30 3 | Windows / IE 11: Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko 4 | Windows / Edge: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063 5 | Windows / Chrome: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.104 Safari/537.36 6 | 7 | Android / Chrome 40: Mozilla/5.0 (Linux; Android 5.1.1; Nexus 4 Build/LMY48T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.89 Mobile Safari/537.36 8 | iOS / Safari 10: Mozilla/5.0 (iPhone; CPU iPhone OS 10_3_1 like Mac OS X) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.0 Mobile/14E304 Safari/602.1 9 | 10 | Google Bot: Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html) 11 | PS4: Mozilla/5.0 (PlayStation 4 3.15) AppleWebKit/537.73 (KHTML, like Gecko) 12 | Curl: curl/7.51.0 13 | -------------------------------------------------------------------------------- /src/_includes/assets/developer_note.js: -------------------------------------------------------------------------------- 1 | /* DEVELOPER NOTE: 2 | * This file is generated by project's build-system, it maybe has redundance code and not easy to read. 3 | * If you are interesting hacking or reviewing, consider viewing the project's orignal source code. 4 | * https://github.com/muzuiget/user_agent_overrider 5 | */ 6 | -------------------------------------------------------------------------------- /src/_includes/assets/license_banner.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | -------------------------------------------------------------------------------- /src/_includes/bootstrap.js: -------------------------------------------------------------------------------- 1 | /* global trace */ 2 | /* global NS_XUL */ 3 | /* global Utils */ 4 | /* global ToolbarManager */ 5 | /* global BrowserManager */ 6 | /* global StyleManager */ 7 | /* global UAManager */ 8 | /* global Pref */ 9 | /* global ASSET_BUILTIN_UA_TEXT */ 10 | 11 | var _ = null; 12 | var loadLocalization = function() { 13 | _ = Utils.localization('useragentoverrider', 'global'); 14 | }; 15 | 16 | var uaEntriesConverter = function(text) { 17 | let entries = []; 18 | let lines = text.split('\n'); 19 | for (let line of lines) { 20 | line = line.trim(); 21 | if (!line || line.startsWith('#')) { 22 | continue; 23 | } 24 | let delimiter = line.indexOf(':'); 25 | let label = line.slice(0, delimiter).trim(); 26 | let url = line.slice(delimiter + 1).trim(); 27 | if (label && url) { 28 | entries.push([label, url]); 29 | } 30 | } 31 | return entries; 32 | }; 33 | 34 | var UserAgentOverrider = function() { 35 | 36 | const EXTENSION_NAME = 'User Agent Overrider'; 37 | const BUTTON_ID = 'useragentoverrider-button'; 38 | const STYLE_URI = 'chrome://useragentoverrider/skin/browser.css'; 39 | const PREF_BRANCH = 'extensions.useragentoverrider.'; 40 | 41 | const ACTIVATED_TOOLTIPTEXT = EXTENSION_NAME + '\n' + 42 | _('activatedTooltip'); 43 | const DEACTIVATED_TOOLTIPTEXT = EXTENSION_NAME + '\n' + 44 | _('deactivatedTooltip'); 45 | 46 | const DEFAULT_ENTRIES_STRING = ASSET_BUILTIN_UA_TEXT.trim() + '\n'; 47 | 48 | let config = { 49 | firstRun: true, 50 | activated: false, 51 | entries: [], 52 | currentLabel: '', // label in entries 53 | }; 54 | let pref = Pref(PREF_BRANCH); 55 | 56 | let prefObserver; 57 | let uaHandler; 58 | let toolbarButtons; 59 | 60 | prefObserver = { 61 | 62 | observe: function() { 63 | this.reloadConfig(); 64 | uaHandler.refresh(); 65 | toolbarButtons.refresh(); 66 | }, 67 | 68 | start: function() { 69 | pref.addObserver(this); 70 | }, 71 | stop: function() { 72 | pref.removeObserver(this); 73 | }, 74 | 75 | initBool: function(name) { 76 | let value = pref.getBool(name); 77 | if (value === null) { 78 | pref.setBool(name, config[name]); 79 | } else { 80 | config[name] = value; 81 | } 82 | }, 83 | initString: function(name) { 84 | let value = pref.getString(name); 85 | if (value === null) { 86 | pref.setString(name, config[name]); 87 | } else { 88 | config[name] = value; 89 | } 90 | }, 91 | initComplex: function(name, converter, defaultValue) { 92 | let text = pref.getString(name); 93 | if (text === null) { 94 | pref.setString(name, defaultValue); 95 | config[name] = converter(defaultValue); 96 | } else { 97 | config[name] = converter(text); 98 | } 99 | }, 100 | 101 | loadBool: function(name) { 102 | let value = pref.getBool(name); 103 | if (value !== null) { 104 | config[name] = value; 105 | } 106 | }, 107 | loadString: function(name) { 108 | let value = pref.getString(name); 109 | if (value !== null) { 110 | config[name] = value; 111 | } 112 | }, 113 | loadComplex: function(name, converter) { 114 | let text = pref.getString(name); 115 | if (text !== null) { 116 | config[name] = converter(text); 117 | } 118 | }, 119 | 120 | initConfig: function() { 121 | let {initBool, initString, initComplex} = this; 122 | initBool('firstRun'); 123 | initBool('activated'); 124 | initComplex('entries', uaEntriesConverter, DEFAULT_ENTRIES_STRING); 125 | initString('currentLabel'); 126 | }, 127 | reloadConfig: function() { 128 | let {loadBool, loadString, loadComplex} = this; 129 | loadBool('firstRun'); 130 | loadBool('activated'); 131 | loadComplex('entries', uaEntriesConverter); 132 | loadString('currentLabel'); 133 | }, 134 | saveConfig: function() { 135 | this.stop(); // avoid recursion 136 | 137 | pref.setBool('firstRun', false); 138 | pref.setBool('activated', config.activated); 139 | pref.setString('currentLabel', config.currentLabel); 140 | 141 | this.start(); 142 | }, 143 | }; 144 | 145 | uaHandler = { 146 | revert: function() { 147 | UAManager.revert(); 148 | }, 149 | refresh: function() { 150 | let {activated, entries, currentLabel} = config; 151 | if (!activated || !currentLabel) { 152 | UAManager.revert(); 153 | return; 154 | } 155 | 156 | // change to the newsest uastring 157 | for (let [label, uastring] of entries) { 158 | if (label === currentLabel) { 159 | UAManager.change(uastring); 160 | return; 161 | } 162 | } 163 | 164 | // the last current label has removed, revert default 165 | config.activated = false; 166 | config.currentLabel = ''; 167 | prefObserver.saveConfig(); 168 | UAManager.revert(); 169 | }, 170 | }; 171 | 172 | toolbarButtons = { 173 | 174 | refresh: function() { 175 | this.refreshMenu(); 176 | this.refreshStatus(); 177 | }, 178 | 179 | refreshMenu: function() { 180 | let that = this; 181 | BrowserManager.run(function(window) { 182 | let document = window.document; 183 | let button = document.getElementById(BUTTON_ID); 184 | let uaMenuitems = that.createUAMenuitems(document); 185 | that.refreshMenuFor(button, uaMenuitems); 186 | }); 187 | }, 188 | refreshMenuFor: function(button, uaMenuitems) { 189 | let menupopup = button.getElementsByTagName('menupopup')[0]; 190 | let prefMenuitem = button.getElementsByClassName('pref')[0]; 191 | let menusep = button.getElementsByTagName('menuseparator')[0]; 192 | 193 | menupopup.textContent = ''; 194 | menupopup.appendChild(prefMenuitem); 195 | menupopup.appendChild(menusep); 196 | for (let menuitem of uaMenuitems) { 197 | menuitem.addEventListener('command', this.onUAMenuitemCommand.bind(this)); 198 | menupopup.appendChild(menuitem); 199 | } 200 | }, 201 | 202 | refreshStatus: function() { 203 | let that = this; 204 | BrowserManager.run(function(window) { 205 | let document = window.document; 206 | let button = document.getElementById(BUTTON_ID); 207 | that.refreshStatusFor(button); 208 | }); 209 | }, 210 | refreshStatusFor: function(button) { 211 | let {activated, entries, currentLabel} = config; 212 | let uaMenuitems = button.getElementsByClassName('ua'); 213 | let menusep = button.getElementsByTagName('menuseparator')[0]; 214 | 215 | // hide menuseparator if no uaMenuitems 216 | if (entries.length === 0) { 217 | menusep.setAttribute('hidden', true); 218 | } else { 219 | menusep.removeAttribute('hidden'); 220 | } 221 | 222 | // always deactivate button if is not check an uaMenuitem 223 | if (!currentLabel) { 224 | button.setAttribute('disabled', 'yes'); 225 | button.setAttribute('tooltiptext', DEACTIVATED_TOOLTIPTEXT); 226 | uaMenuitems[0].setAttribute('checked', true); 227 | return; 228 | } 229 | 230 | // update button and menuitems status 231 | if (activated) { 232 | button.removeAttribute('disabled'); 233 | button.setAttribute('tooltiptext', ACTIVATED_TOOLTIPTEXT); 234 | } else { 235 | button.setAttribute('disabled', 'yes'); 236 | button.setAttribute('tooltiptext', DEACTIVATED_TOOLTIPTEXT); 237 | } 238 | 239 | for (let menuitem of uaMenuitems) { 240 | let label = menuitem.getAttribute('label'); 241 | menuitem.setAttribute('checked', label === currentLabel); 242 | } 243 | 244 | }, 245 | 246 | toggle: function(activated) { 247 | if (activated === undefined) { 248 | activated = !config.activated; 249 | } 250 | config.activated = activated; 251 | prefObserver.saveConfig(); 252 | uaHandler.refresh(); 253 | this.refreshStatus(); 254 | }, 255 | 256 | onPrefMenuitemCommand: function(event) { 257 | let dialog = Utils.getMostRecentWindow( 258 | 'UserAgentOverrider:Preferences'); 259 | if (dialog) { 260 | dialog.focus(); 261 | } else { 262 | let window = event.target.ownerDocument.defaultView; 263 | window.openDialog( 264 | 'chrome://useragentoverrider/content/options.xul', '', 265 | 'chrome,titlebar,toolbar,centerscreen,dialog=no'); 266 | } 267 | }, 268 | onUAMenuitemCommand: function(event) { 269 | let value = event.target.getAttribute('value'); 270 | 271 | // zero is the default menuitem 272 | if (value === '0') { 273 | config.currentLabel = ''; 274 | this.toggle(false); 275 | return; 276 | } 277 | 278 | config.currentLabel = event.target.getAttribute('label'); 279 | this.toggle(true); 280 | }, 281 | 282 | createUAMenuitems: function(document) { 283 | let defaultEntry = [_('default'), UAManager.defaultUa]; 284 | 285 | let entries = []; 286 | entries.push(defaultEntry); 287 | entries.push.apply(entries, config.entries); 288 | 289 | let menuitems = []; 290 | for (let i = 0; i < entries.length; i += 1) { 291 | let [label, uastring] = entries[i]; 292 | let attrs = { 293 | 'class': 'ua', 294 | label: label, 295 | value: i, 296 | tooltiptext: uastring, 297 | name: 'useragentoverrider-ua', 298 | type: 'radio', 299 | }; 300 | let menuitem = document.createElementNS(NS_XUL, 'menuitem'); 301 | Utils.setAttrs(menuitem, attrs); 302 | menuitems.push(menuitem); 303 | } 304 | return menuitems; 305 | }, 306 | 307 | createInstance: function(window) { 308 | let document = window.document; 309 | 310 | let button = (function() { 311 | let attrs = { 312 | id: BUTTON_ID, 313 | 'class': 'toolbarbutton-1 chromeclass-toolbar-additional', 314 | type: 'menu', 315 | removable: true, 316 | label: EXTENSION_NAME, 317 | tooltiptext: EXTENSION_NAME, 318 | }; 319 | if (config.activated) { 320 | attrs.tooltiptext = ACTIVATED_TOOLTIPTEXT; 321 | } else { 322 | attrs.disabled = 'yes'; 323 | attrs.tooltiptext = DEACTIVATED_TOOLTIPTEXT; 324 | } 325 | let button = document.createElementNS(NS_XUL, 'toolbarbutton'); 326 | Utils.setAttrs(button, attrs); 327 | return button; 328 | })(); 329 | 330 | let prefMenuitem = (function() { 331 | let menuitem = document.createElementNS(NS_XUL, 'menuitem'); 332 | menuitem.setAttribute('class', 'pref'); 333 | menuitem.setAttribute('label', _('openPreferences')); 334 | return menuitem; 335 | })(); 336 | prefMenuitem.addEventListener('command', 337 | this.onPrefMenuitemCommand); 338 | 339 | let menusep = document.createElementNS(NS_XUL, 'menuseparator'); 340 | let uaMenuitems = this.createUAMenuitems(document); 341 | 342 | let menupopup = document.createElementNS(NS_XUL, 'menupopup'); 343 | menupopup.appendChild(prefMenuitem); 344 | menupopup.appendChild(menusep); 345 | for (let menuitem of uaMenuitems) { 346 | menuitem.addEventListener('command', this.onUAMenuitemCommand.bind(this)); 347 | menupopup.appendChild(menuitem); 348 | } 349 | 350 | button.appendChild(menupopup); 351 | this.refreshStatusFor(button); 352 | return button; 353 | }, 354 | }; 355 | 356 | let insertToolbarButton = function(window) { 357 | let button = toolbarButtons.createInstance(window); 358 | try { 359 | ToolbarManager.addWidget(window, button, config.firstRun); 360 | } catch (error) { 361 | trace(error); 362 | } 363 | }; 364 | let removeToolbarButton = function(window) { 365 | try { 366 | ToolbarManager.removeWidget(window, BUTTON_ID); 367 | } catch (error) { 368 | trace(error); 369 | } 370 | }; 371 | 372 | let initialize = function() { 373 | prefObserver.initConfig(); 374 | prefObserver.start(); 375 | uaHandler.refresh(); 376 | 377 | BrowserManager.run(insertToolbarButton); 378 | BrowserManager.addListener(insertToolbarButton); 379 | StyleManager.load(STYLE_URI); 380 | }; 381 | let destory = function() { 382 | prefObserver.saveConfig(); 383 | prefObserver.stop(); 384 | uaHandler.revert(); 385 | 386 | BrowserManager.run(removeToolbarButton); 387 | BrowserManager.destory(); 388 | StyleManager.destory(); 389 | }; 390 | 391 | let exports = { 392 | initialize: initialize, 393 | destory: destory, 394 | }; 395 | return exports; 396 | 397 | }; 398 | 399 | /* bootstrap entry points */ 400 | 401 | var userAgentOverrider; 402 | 403 | var install = function() {}; 404 | var uninstall = function() {}; 405 | 406 | var startup = function() { 407 | loadLocalization(); 408 | userAgentOverrider = UserAgentOverrider(); 409 | userAgentOverrider.initialize(); 410 | }; 411 | 412 | var shutdown = function() { 413 | userAgentOverrider.destory(); 414 | }; 415 | 416 | /* exported install uninstall startup shutdown */ 417 | -------------------------------------------------------------------------------- /src/_includes/jslib/BrowserManager.js: -------------------------------------------------------------------------------- 1 | var BrowserManager = (function() { 2 | /* global trace Cc Ci */ 3 | 4 | const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1'] 5 | .getService(Ci.nsIWindowWatcher); 6 | 7 | const BROWSER_URI = 'chrome://browser/content/browser.xul'; 8 | 9 | let listeners = []; 10 | 11 | let onload = function(event) { 12 | for (let listener of listeners) { 13 | let window = event.currentTarget; 14 | window.removeEventListener('load', onload); 15 | if (window.location.href !== BROWSER_URI) { 16 | return; 17 | } 18 | try { 19 | listener(window); 20 | } catch (error) { 21 | trace(error); 22 | } 23 | } 24 | }; 25 | 26 | let observer = { 27 | observe: function(window, topic) { 28 | if (topic !== 'domwindowopened') { 29 | return; 30 | } 31 | window.addEventListener('load', onload); 32 | }, 33 | }; 34 | 35 | let run = function(func) { 36 | let enumerator = windowWatcher.getWindowEnumerator(); 37 | while (enumerator.hasMoreElements()) { 38 | let window = enumerator.getNext(); 39 | if (window.location.href !== BROWSER_URI) { 40 | continue; 41 | } 42 | 43 | try { 44 | func(window); 45 | } catch (error) { 46 | trace(error); 47 | } 48 | } 49 | }; 50 | 51 | let addListener = function(listener) { 52 | listeners.push(listener); 53 | }; 54 | 55 | let removeListener = function(listener) { 56 | let start = listeners.indexOf(listener); 57 | if (start !== -1) { 58 | listeners.splice(start, 1); 59 | } 60 | }; 61 | 62 | let initialize = function() { 63 | windowWatcher.registerNotification(observer); 64 | }; 65 | 66 | let destory = function() { 67 | windowWatcher.unregisterNotification(observer); 68 | listeners = null; 69 | }; 70 | 71 | initialize(); 72 | 73 | let exports = { 74 | run: run, 75 | addListener: addListener, 76 | removeListener: removeListener, 77 | destory: destory, 78 | }; 79 | return exports; 80 | })(); 81 | 82 | /* exported BrowserManager */ 83 | -------------------------------------------------------------------------------- /src/_includes/jslib/Pref.js: -------------------------------------------------------------------------------- 1 | var Pref = function(branchRoot) { 2 | /* global trace Cc Ci */ 3 | 4 | const supportsStringClass = Cc['@mozilla.org/supports-string;1']; 5 | const prefService = Cc['@mozilla.org/preferences-service;1'] 6 | .getService(Ci.nsIPrefService); 7 | 8 | const new_nsiSupportsString = function(data) { 9 | let string = supportsStringClass.createInstance(Ci.nsISupportsString); 10 | string.data = data; 11 | return string; 12 | }; 13 | 14 | let branch = prefService.getBranch(branchRoot); 15 | 16 | let setBool = function(key, value) { 17 | try { 18 | branch.setBoolPref(key, value); 19 | } catch (error) { 20 | branch.clearUserPref(key); 21 | branch.setBoolPref(key, value); 22 | } 23 | }; 24 | let getBool = function(key, defaultValue) { 25 | let value; 26 | try { 27 | value = branch.getBoolPref(key); 28 | } catch (error) { 29 | value = defaultValue || null; 30 | } 31 | return value; 32 | }; 33 | 34 | let setInt = function(key, value) { 35 | try { 36 | branch.setIntPref(key, value); 37 | } catch (error) { 38 | branch.clearUserPref(key); 39 | branch.setIntPref(key, value); 40 | } 41 | }; 42 | let getInt = function(key, defaultValue) { 43 | let value; 44 | try { 45 | value = branch.getIntPref(key); 46 | } catch (error) { 47 | value = defaultValue || null; 48 | } 49 | return value; 50 | }; 51 | 52 | let setString = function(key, value) { 53 | try { 54 | branch.setComplexValue(key, Ci.nsISupportsString, 55 | new_nsiSupportsString(value)); 56 | } catch (error) { 57 | branch.clearUserPref(key); 58 | branch.setComplexValue(key, Ci.nsISupportsString, 59 | new_nsiSupportsString(value)); 60 | } 61 | }; 62 | let getString = function(key, defaultValue) { 63 | let value; 64 | try { 65 | value = branch.getComplexValue(key, Ci.nsISupportsString).data; 66 | } catch (error) { 67 | value = defaultValue || null; 68 | } 69 | return value; 70 | }; 71 | 72 | let reset = function(key) { 73 | branch.clearUserPref(key); 74 | }; 75 | 76 | let addObserver = function(observer) { 77 | try { 78 | branch.addObserver('', observer, false); 79 | } catch (error) { 80 | trace(error); 81 | } 82 | }; 83 | let removeObserver = function(observer) { 84 | try { 85 | branch.removeObserver('', observer, false); 86 | } catch (error) { 87 | trace(error); 88 | } 89 | }; 90 | 91 | let exports = { 92 | setBool: setBool, 93 | getBool: getBool, 94 | setInt: setInt, 95 | getInt: getInt, 96 | setString: setString, 97 | getString: getString, 98 | reset: reset, 99 | addObserver: addObserver, 100 | removeObserver: removeObserver, 101 | }; 102 | return exports; 103 | }; 104 | 105 | /* exported Pref */ 106 | -------------------------------------------------------------------------------- /src/_includes/jslib/StyleManager.js: -------------------------------------------------------------------------------- 1 | var StyleManager = (function() { 2 | /* global Cc Ci */ 3 | 4 | const styleService = Cc['@mozilla.org/content/style-sheet-service;1'] 5 | .getService(Ci.nsIStyleSheetService); 6 | const ioService = Cc['@mozilla.org/network/io-service;1'] 7 | .getService(Ci.nsIIOService); 8 | 9 | const STYLE_TYPE = styleService.USER_SHEET; 10 | 11 | const new_nsiURI = function(uri) { 12 | return ioService.newURI(uri, null, null); 13 | }; 14 | 15 | let uris = []; 16 | 17 | let load = function(uri) { 18 | let nsiURI = new_nsiURI(uri); 19 | if (styleService.sheetRegistered(nsiURI, STYLE_TYPE)) { 20 | return; 21 | } 22 | styleService.loadAndRegisterSheet(nsiURI, STYLE_TYPE); 23 | uris.push(uri); 24 | }; 25 | 26 | let unload = function(uri) { 27 | let nsiURI = new_nsiURI(uri); 28 | if (!styleService.sheetRegistered(nsiURI, STYLE_TYPE)) { 29 | return; 30 | } 31 | styleService.unregisterSheet(nsiURI, STYLE_TYPE); 32 | let start = uris.indexOf(uri); 33 | uris.splice(start, 1); 34 | }; 35 | 36 | let destory = function() { 37 | for (let uri of uris.slice(0)) { 38 | unload(uri); 39 | } 40 | uris = null; 41 | }; 42 | 43 | let exports = { 44 | load: load, 45 | unload: unload, 46 | destory: destory, 47 | }; 48 | return exports; 49 | 50 | })(); 51 | 52 | /* exported StyleManager */ 53 | -------------------------------------------------------------------------------- /src/_includes/jslib/ToolbarManager.js: -------------------------------------------------------------------------------- 1 | var ToolbarManager = (function() { 2 | /* global trace */ 3 | 4 | /** 5 | * Remember the button position. 6 | * This function Modity from addon-sdk file lib/sdk/widget.js, and 7 | * function BrowserWindow.prototype._insertNodeInToolbar 8 | */ 9 | let layoutWidget = function(document, button, isFirstRun) { 10 | 11 | // Add to the customization palette 12 | let toolbox = document.getElementById('navigator-toolbox'); 13 | toolbox.palette.appendChild(button); 14 | 15 | // Search for widget toolbar by reading toolbar's currentset attribute 16 | let container = null; 17 | let toolbars = document.getElementsByTagName('toolbar'); 18 | let id = button.getAttribute('id'); 19 | for (let i = 0; i < toolbars.length; i += 1) { 20 | let toolbar = toolbars[i]; 21 | if (toolbar.getAttribute('currentset').indexOf(id) !== -1) { 22 | container = toolbar; 23 | } 24 | } 25 | 26 | // if widget isn't in any toolbar, default add it next to searchbar 27 | if (!container) { 28 | if (isFirstRun) { 29 | container = document.getElementById('nav-bar'); 30 | } else { 31 | return; 32 | } 33 | } 34 | 35 | // Now retrieve a reference to the next toolbar item 36 | // by reading currentset attribute on the toolbar 37 | let nextNode = null; 38 | let currentSet = container.getAttribute('currentset'); 39 | let ids = (currentSet === '__empty') ? [] : currentSet.split(','); 40 | let idx = ids.indexOf(id); 41 | if (idx !== -1) { 42 | for (let i = idx; i < ids.length; i += 1) { 43 | nextNode = document.getElementById(ids[i]); 44 | if (nextNode) { 45 | break; 46 | } 47 | } 48 | } 49 | 50 | // Finally insert our widget in the right toolbar and in the right position 51 | container.insertItem(id, nextNode, null, false); 52 | 53 | // Update DOM in order to save position 54 | // in this toolbar. But only do this the first time we add it to the toolbar 55 | if (ids.indexOf(id) === -1) { 56 | container.setAttribute('currentset', container.currentSet); 57 | document.persist(container.id, 'currentset'); 58 | } 59 | }; 60 | 61 | let addWidget = function(window, widget, isFirstRun) { 62 | try { 63 | layoutWidget(window.document, widget, isFirstRun); 64 | } catch (error) { 65 | trace(error); 66 | } 67 | }; 68 | 69 | let removeWidget = function(window, widgetId) { 70 | try { 71 | let widget = window.document.getElementById(widgetId); 72 | widget.parentNode.removeChild(widget); 73 | } catch (error) { 74 | trace(error); 75 | } 76 | }; 77 | 78 | let exports = { 79 | addWidget: addWidget, 80 | removeWidget: removeWidget, 81 | }; 82 | return exports; 83 | })(); 84 | 85 | /* exported ToolbarManager */ 86 | -------------------------------------------------------------------------------- /src/_includes/jslib/UAManager.js: -------------------------------------------------------------------------------- 1 | var UAManager = (function() { 2 | /* global Cc Ci Pref */ 3 | 4 | const DEFAULT_UA = Cc['@mozilla.org/network/protocol;1?name=http'] 5 | .getService(Ci.nsIHttpProtocolHandler) 6 | .userAgent; 7 | 8 | let pref = Pref('general.useragent.'); 9 | 10 | let revert = function() { 11 | pref.reset('override'); 12 | }; 13 | 14 | let change = function(uastring) { 15 | pref.setString('override', uastring); 16 | }; 17 | 18 | let exports = { 19 | revert: revert, 20 | change: change, 21 | defaultUa: DEFAULT_UA, 22 | }; 23 | return exports; 24 | 25 | })(); 26 | 27 | /* exported UAManager */ 28 | -------------------------------------------------------------------------------- /src/_includes/jslib/Utils.js: -------------------------------------------------------------------------------- 1 | var Utils = (function() { 2 | /* global Cc Ci */ 3 | 4 | const sbService = Cc['@mozilla.org/intl/stringbundle;1'] 5 | .getService(Ci.nsIStringBundleService); 6 | const windowMediator = Cc['@mozilla.org/appshell/window-mediator;1'] 7 | .getService(Ci.nsIWindowMediator); 8 | 9 | let localization = function(id, name) { 10 | let uri = 'chrome://' + id + '/locale/' + name + '.properties'; 11 | return sbService.createBundle(uri).GetStringFromName; 12 | }; 13 | 14 | let setAttrs = function(widget, attrs) { 15 | for (let [key, value] in Iterator(attrs)) { 16 | widget.setAttribute(key, value); 17 | } 18 | }; 19 | 20 | let getMostRecentWindow = function(winType) { 21 | return windowMediator.getMostRecentWindow(winType); 22 | }; 23 | 24 | let exports = { 25 | localization: localization, 26 | setAttrs: setAttrs, 27 | getMostRecentWindow: getMostRecentWindow, 28 | }; 29 | return exports; 30 | 31 | })(); 32 | 33 | /* exported Utils */ 34 | -------------------------------------------------------------------------------- /src/_includes/jslib/logging.js: -------------------------------------------------------------------------------- 1 | var log = function() { dump(Array.slice(arguments).join(' ') + '\n'); }; 2 | var trace = function(error) { log(error); log(error.stack); }; 3 | var dirobj = function(obj) { for (let i in obj) { log(i, ':', obj[i]); } }; 4 | 5 | /* exported log trace dirobj */ 6 | -------------------------------------------------------------------------------- /src/_includes/jslib/xulComp.js: -------------------------------------------------------------------------------- 1 | var {classes: Cc, interfaces: Ci, utils: Cu} = Components; 2 | var NS_XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; 3 | 4 | /* exported Cc Ci Cu NS_XUL */ 5 | -------------------------------------------------------------------------------- /src/_includes/options.js: -------------------------------------------------------------------------------- 1 | /* eslint-env browser */ 2 | /* global Utils */ 3 | /* global ASSET_BUILTIN_UA_TEXT */ 4 | 5 | var _ = null; 6 | var loadLocalization = function() { 7 | let stringbundle = document.getElementById('useragentoverrider-strings'); 8 | _ = (name) => stringbundle.getString(name); 9 | }; 10 | 11 | var onHelpLinkClick = function() { 12 | let helpUrl = 'https://github.com/muzuiget/user_agent_overrider/blob/master/docs/Preference.md'; 13 | let browserWindow = Utils.getMostRecentWindow('navigator:browser'); 14 | if (browserWindow) { 15 | let gBrowser = browserWindow.gBrowser; 16 | gBrowser.selectedTab = gBrowser.addTab(helpUrl); 17 | } else { 18 | window.open(helpUrl); 19 | } 20 | return false; 21 | }; 22 | 23 | var onResetLinkClick = function() { 24 | let response = confirm(_('areYouSure')); 25 | if (!response) { 26 | return; 27 | } 28 | let textbox = document.getElementById('textbox'); 29 | textbox.value = ASSET_BUILTIN_UA_TEXT.trim() + '\n'; 30 | document.getElementById('main').userChangedValue(textbox); 31 | }; 32 | 33 | var onDocumentLoad = function() { 34 | loadLocalization(); 35 | }; 36 | 37 | /* exported onHelpLinkClick */ 38 | /* exported onResetLinkClick */ 39 | /* exported onDocumentLoad */ 40 | -------------------------------------------------------------------------------- /src/bootstrap.js.nj: -------------------------------------------------------------------------------- 1 | {% include "./_includes/assets/license_banner.js" %} 2 | {% include "./_includes/assets/developer_note.js" %} 3 | 4 | 'use strict'; 5 | 6 | {% include "./_includes/jslib/logging.js" %} 7 | {% include "./_includes/jslib/xulComp.js" %} 8 | {% include "./_includes/jslib/Utils.js" %} 9 | {% include "./_includes/jslib/StyleManager.js" %} 10 | {% include "./_includes/jslib/BrowserManager.js" %} 11 | {% include "./_includes/jslib/ToolbarManager.js" %} 12 | {% include "./_includes/jslib/Pref.js" %} 13 | {% include "./_includes/jslib/UAManager.js" %} 14 | 15 | var ASSET_BUILTIN_UA_TEXT = ` 16 | {% include "./_includes/assets/builtin_user_agents.txt" %} 17 | `; 18 | 19 | {% include "./_includes/bootstrap.js" %} 20 | -------------------------------------------------------------------------------- /src/chrome.manifest.nj: -------------------------------------------------------------------------------- 1 | content {{ idname }} content/ 2 | skin {{ idname }} classic/1.0 skin/ 3 | {% for lang in langs -%} 4 | locale {{ idname }} {{ lang }} locale/{{ lang }}/ 5 | {% endfor %} 6 | -------------------------------------------------------------------------------- /src/content/options.js.nj: -------------------------------------------------------------------------------- 1 | {% include "./_includes/assets/license_banner.js" %} 2 | {% include "./_includes/assets/developer_note.js" %} 3 | 4 | 'use strict'; 5 | 6 | {% include "./_includes/jslib/logging.js" %} 7 | {% include "./_includes/jslib/xulComp.js" %} 8 | {% include "./_includes/jslib/Utils.js" %} 9 | 10 | var ASSET_BUILTIN_UA_TEXT = ` 11 | {% include "./_includes/assets/builtin_user_agents.txt" %} 12 | `; 13 | 14 | {% include "./_includes/options.js" %} 15 | -------------------------------------------------------------------------------- /src/content/options.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 37 | 38 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muzuiget/user_agent_overrider/561bce5ebf0eb12b891978d50b74907049b68c9a/src/icon.png -------------------------------------------------------------------------------- /src/install.rdf.nj: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | useragentoverrider@qixinglu.com 7 | {{ version }} 8 | 2 9 | true 10 | {{ unpack }} 11 | chrome://useragentoverrider/content/options.xul 12 | true 13 | 14 | 15 | User Agent Overrider 16 | Override browser User-Agent string 17 | muzuiget 18 | 19 | 20 | 21 | 22 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 23 | 52.0 24 | 55.* 25 | 26 | 27 | 28 | 29 | 30 | {% for locale in locales %} 31 | 32 | {{ locale.lang }} 33 | {{ locale.name }} 34 | {{ locale.desc }} 35 | 36 | {% endfor %} 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/locale/bg/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/bg/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Подмяна на User-Agent на браузъра 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=е включен 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=е изключен 7 | default=Default 8 | openPreferences=Предпочитания... 9 | -------------------------------------------------------------------------------- /src/locale/de/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/de/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Überschreibe User-Agent des Browsers 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=eingeschaltet 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=ausgeschaltet 7 | default=Default 8 | openPreferences=Einstellungen 9 | -------------------------------------------------------------------------------- /src/locale/en-US/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/en-US/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Override browser User-Agent string 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=working now 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=not working now 7 | default=Default 8 | openPreferences=Preferences... 9 | -------------------------------------------------------------------------------- /src/locale/es-ES/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/es-ES/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Te permite sobrescribir el User-Agent del navegador 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=Activado 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=Desactivado 7 | default=Default 8 | openPreferences=Preferencias... 9 | -------------------------------------------------------------------------------- /src/locale/es/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/es/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Sustituir al agente de usuario del navegador 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=ahora operativo 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=ahora no operativo 7 | default=Default 8 | openPreferences=Preferencias... 9 | -------------------------------------------------------------------------------- /src/locale/fr/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/fr/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Outre passer la chaîne User-Agent du navigateur 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=Activé 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=Désactivé 7 | default=Default 8 | openPreferences=Préférences 9 | -------------------------------------------------------------------------------- /src/locale/it/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/it/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Modifica la stringa User-Agent del browser 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=in funzione 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=non in funzione 7 | default=Default 8 | openPreferences=Preferenze... 9 | -------------------------------------------------------------------------------- /src/locale/nl_NL/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/nl_NL/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Gebruikersagent-string van browser overschrijven 2 | extensionName=Gebruikersagent-wisselaar 3 | 4 | activatedTooltip=werkt nu 5 | areYouSure=Weet u het zeker? 6 | deactivatedTooltip=werkt nu niet 7 | default=Standaard 8 | openPreferences=Voorkeuren... 9 | -------------------------------------------------------------------------------- /src/locale/ro/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/ro/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Înlocuiește șirul pentru Agent-Utilizator al navigatorului 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=acum funcționează 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=acum nu funcționează 7 | default=Default 8 | openPreferences=Preferințe... 9 | -------------------------------------------------------------------------------- /src/locale/ru/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/ru/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Переопределить строку User-Agent в браузере 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=сейчас включено 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=сейчас выключено 7 | default=Default 8 | openPreferences=Настройки... 9 | -------------------------------------------------------------------------------- /src/locale/tr_TR/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/tr_TR/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=Taraycı User-Agent yazısını etkisiz kıl 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=şuan çalışıyor 5 | areYouSure=Are you sure? 6 | deactivatedTooltip=şuan çalışmıyor 7 | default=Default 8 | openPreferences=Özellikler... 9 | -------------------------------------------------------------------------------- /src/locale/zh-CN/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/zh-CN/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=修改浏览器的 User-Agent 字符串 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=正在工作中 5 | areYouSure=你确定这样做吗? 6 | deactivatedTooltip=不在工作中 7 | default=默认 8 | openPreferences=首选项... 9 | -------------------------------------------------------------------------------- /src/locale/zh-TW/global.dtd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/locale/zh-TW/global.properties: -------------------------------------------------------------------------------- 1 | extensionDescription=修改瀏覽器的 User-Agent 字符串 2 | extensionName=User Agent Overrider 3 | 4 | activatedTooltip=正在工作中 5 | areYouSure=你確定這樣做嗎? 6 | deactivatedTooltip=不在工作中 7 | default=預設 8 | openPreferences=偏好設置... 9 | -------------------------------------------------------------------------------- /src/skin/browser.css: -------------------------------------------------------------------------------- 1 | /* About the logo 2 | * 3 | * The logo icon is modified from tiheum's excellent work: Faenza icon set. 4 | * License is GPL, more information see it's home page 5 | * http://gnome-look.org/content/show.php?content=128143 6 | * 7 | */ 8 | 9 | /* in browser window toolbar */ 10 | 11 | toolbar #useragentoverrider-button { 12 | list-style-image: url("chrome://useragentoverrider/skin/icon24.png"); 13 | } 14 | 15 | toolbar #useragentoverrider-button .toolbarbutton-menu-dropmarker { 16 | display: none; 17 | } 18 | 19 | toolbar[iconsize="small"] #useragentoverrider-button { 20 | list-style-image: url("chrome://useragentoverrider/skin/icon16.png") !important; 21 | } 22 | 23 | toolbar #useragentoverrider-button[disabled="yes"] { 24 | filter: grayscale(100%); 25 | } 26 | 27 | /* in browser window toolbar palette (Australis theme since Firefox 29) */ 28 | 29 | #useragentoverrider-button[cui-areatype="menu-panel"], 30 | toolbarpaletteitem[place="palette"] > #useragentoverrider-button { 31 | list-style-image: url("chrome://useragentoverrider/skin/icon32.png"); 32 | } 33 | 34 | #useragentoverrider-button[cui-areatype="menu-panel"][disabled="yes"] { 35 | filter: grayscale(100%); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/skin/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muzuiget/user_agent_overrider/561bce5ebf0eb12b891978d50b74907049b68c9a/src/skin/icon16.png -------------------------------------------------------------------------------- /src/skin/icon24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muzuiget/user_agent_overrider/561bce5ebf0eb12b891978d50b74907049b68c9a/src/skin/icon24.png -------------------------------------------------------------------------------- /src/skin/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/muzuiget/user_agent_overrider/561bce5ebf0eb12b891978d50b74907049b68c9a/src/skin/icon32.png --------------------------------------------------------------------------------