├── .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 | [](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 |
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 |
18 |
22 |
23 |
24 |
29 |
34 |
35 |
36 |
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
--------------------------------------------------------------------------------