├── .DS_Store
├── JPM
├── data
│ ├── icons
│ │ ├── 128.png
│ │ ├── 16.png
│ │ ├── 20.png
│ │ ├── 24.png
│ │ ├── 256.png
│ │ ├── 32.png
│ │ ├── 48.png
│ │ ├── 64.png
│ │ └── mac
│ │ │ ├── 128.png
│ │ │ ├── 16.png
│ │ │ ├── 20.png
│ │ │ ├── 24.png
│ │ │ ├── 256.png
│ │ │ ├── 32.png
│ │ │ ├── 48.png
│ │ │ └── 64.png
│ ├── popover
│ │ ├── fontello.css
│ │ ├── fontello.woff
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.js
│ └── settings
│ │ ├── index.css
│ │ ├── index.html
│ │ └── index.js
├── icon.png
├── icon64.png
├── install.rdf
├── lib
│ ├── main.js
│ └── policy.js
└── package.json
└── WebExtension
├── .DS_Store
├── background.js
├── data
├── .DS_Store
├── icons
│ ├── 128.png
│ ├── 16.png
│ ├── 20.png
│ ├── 24.png
│ ├── 256.png
│ ├── 32.png
│ ├── 48.png
│ └── 64.png
├── log
│ ├── index.html
│ └── index.js
├── options
│ ├── index.html
│ └── index.js
└── popup
│ ├── index.css
│ ├── index.html
│ └── index.js
└── manifest.json
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/.DS_Store
--------------------------------------------------------------------------------
/JPM/data/icons/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/128.png
--------------------------------------------------------------------------------
/JPM/data/icons/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/16.png
--------------------------------------------------------------------------------
/JPM/data/icons/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/20.png
--------------------------------------------------------------------------------
/JPM/data/icons/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/24.png
--------------------------------------------------------------------------------
/JPM/data/icons/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/256.png
--------------------------------------------------------------------------------
/JPM/data/icons/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/32.png
--------------------------------------------------------------------------------
/JPM/data/icons/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/48.png
--------------------------------------------------------------------------------
/JPM/data/icons/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/64.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/128.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/16.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/20.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/24.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/256.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/32.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/48.png
--------------------------------------------------------------------------------
/JPM/data/icons/mac/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/icons/mac/64.png
--------------------------------------------------------------------------------
/JPM/data/popover/fontello.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'fontello';
3 | src: url('fontello.woff') format('woff');
4 | font-weight: normal;
5 | font-style: normal;
6 | }
7 |
8 | [class^="icon-"]:before, [class*=" icon-"]:before {
9 | font-family: "fontello";
10 | font-style: normal;
11 | font-weight: normal;
12 | speak: none;
13 |
14 | display: inline-block;
15 | text-decoration: inherit;
16 | width: 1em;
17 | margin-right: .2em;
18 | text-align: center;
19 | /* opacity: .8; */
20 |
21 | /* For safety - reset parent styles, that can break glyph codes*/
22 | font-variant: normal;
23 | text-transform: none;
24 |
25 | /* fix buttons height, for twitter bootstrap */
26 | line-height: 1em;
27 |
28 | /* Animation center compensation - margins should be symmetric */
29 | /* remove if not needed */
30 | margin-left: .2em;
31 |
32 | /* you can be more comfortable with increased icons size */
33 | /* font-size: 120%; */
34 |
35 | /* Font smoothing. That was taken from TWBS */
36 | -webkit-font-smoothing: antialiased;
37 | -moz-osx-font-smoothing: grayscale;
38 |
39 | /* Uncomment for 3D effect */
40 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
41 | }
42 |
43 | .icon-toggle-off:before { content: '\e803'; } /* '' */
44 | .icon-toggle-on:before { content: '\e805'; } /* '' */
45 |
--------------------------------------------------------------------------------
/JPM/data/popover/fontello.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/data/popover/fontello.woff
--------------------------------------------------------------------------------
/JPM/data/popover/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial,sans-serif;
3 | font-size: 12px;
4 | padding: 10px;
5 | margin: 0;
6 | min-width: 400px;
7 | overflow: hidden;
8 | background-color: #fff;
9 | }
10 | h1 {
11 | font-size: 110%;
12 | font-weight: normal;
13 | }
14 | ul {
15 | list-style-type: none;
16 | padding-left: 15px;
17 | }
18 | li {
19 | position: relative;
20 | }
21 | li span:last-child {
22 | position: absolute;
23 | right: 0;
24 | text-align: right;
25 | width: 100px;
26 | }
27 | li:hover {
28 | background-color: #eee;
29 | }
30 | .button {
31 | cursor: pointer;
32 | -moz-user-select: none;
33 | }
34 | .button:before {
35 | padding-right: 5px;
36 | }
37 |
--------------------------------------------------------------------------------
/JPM/data/popover/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
35 | 1: By enabling a rule, websites will NOT be able to access that particular resource anymore. However, you can still allow access of some resources using
custom filtering rules .
36 |
37 | 2, 3: Disable these options to increase performance.
38 |
39 | 4: Instead of temporary disabling all rules, it is recommended to turn off policy rules permanently in the private browsing mode.
40 |
41 |
42 |
--------------------------------------------------------------------------------
/JPM/data/popover/index.js:
--------------------------------------------------------------------------------
1 | /* globals self */
2 | 'use strict';
3 |
4 | function size () {
5 | self.port.emit('size', {
6 | width: Math.max(document.body.scrollWidth, document.documentElement.scrollWidth, document.body.offsetWidth, document.documentElement.offsetWidth, document.body.clientWidth, document.documentElement.clientWidth),
7 | height: Math.max(document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight)
8 | });
9 | }
10 | size();
11 |
12 | var dom = {
13 | get policies () {
14 | return [].slice.call(document.querySelectorAll('[data-policy]'));
15 | },
16 | policy: function (name) {
17 | return document.querySelector('[data-policy=' + name + ']').querySelector('.button');
18 | },
19 | mod: function (name) {
20 | return document.querySelector('[data-policy=' + name + ']').querySelector('a');
21 | },
22 | get checkboxes () {
23 | return [].slice.call(document.querySelectorAll('input[type=checkbox]'));
24 | },
25 | cmd: function (name) {
26 | return document.querySelector('[data-cmd="' + name + '"]');
27 | }
28 | };
29 |
30 | self.port.on('set-preference', function (obj) {
31 | if (obj.name.indexOf('policy-') === 0) {
32 | var policy = dom.policy(obj.name.substr(7));
33 | if (policy) {
34 | if (obj.value) {
35 | policy.textContent = 'On';
36 | policy.classList.remove('icon-toggle-off');
37 | policy.classList.add('icon-toggle-on');
38 | }
39 | else {
40 | policy.textContent = 'Off';
41 | policy.classList.remove('icon-toggle-on');
42 | policy.classList.add('icon-toggle-off');
43 | }
44 | }
45 | }
46 | if (obj.name.indexOf('mod-') === 0) {
47 | var mod = dom.mod(obj.name.substr(4));
48 | if (mod) {
49 | mod.dataset.mod = obj.value;
50 | mod.textContent = obj.value === 'p' ? 'third-party' : 'all';
51 | }
52 | }
53 | if (obj.name.indexOf('log-') === 0 || obj.name === 'private') {
54 | var log = dom.cmd(obj.name);
55 | log.checked = obj.value;
56 | }
57 | });
58 |
59 | // init
60 | dom.policies.map(p => p.dataset.policy).forEach(function (name) {
61 | self.port.emit('get-preference', 'policy-' + name);
62 | self.port.emit('get-preference', 'mod-' + name);
63 | });
64 | dom.checkboxes.map(e => e.dataset.cmd).forEach(p => self.port.emit('get-preference', p));
65 | // click
66 | document.addEventListener('click', function (e) {
67 | var target = e.target;
68 | if (target.classList.contains('button')) {
69 | var pref = 'policy-' + target.parentNode.dataset.policy;
70 | self.port.emit('set-preference', {
71 | name: pref,
72 | value: target.classList.contains('icon-toggle-off')
73 | });
74 | }
75 | }, false);
76 | document.addEventListener('click', function (e) {
77 | var target = e.target;
78 | var mod = target.dataset.mod;
79 | var policy = target.parentNode.dataset.policy;
80 | if (mod && policy) {
81 | self.port.emit('set-preference', {
82 | name: 'mod-' + policy,
83 | value: mod === 'p' ? 'f' : 'p'
84 | });
85 | }
86 | }, false);
87 | document.addEventListener('click', function (e) {
88 | var target = e.target;
89 | var cmd = target.dataset.cmd;
90 | if (cmd === 'enable') {
91 | self.port.emit('command', {
92 | cmd: cmd,
93 | value: target.dataset.value,
94 | states: dom.policies.map(function (elem) {
95 | var policy = elem.dataset.policy;
96 | return {
97 | status: dom.policy(policy).classList.contains('icon-toggle-on'),
98 | name: 'policy-' + policy
99 | };
100 | })
101 | });
102 | }
103 | else if (cmd) {
104 | self.port.emit('command', {
105 | cmd: cmd,
106 | value: target.checked
107 | });
108 | }
109 | }, false);
110 |
111 | self.port.on('enabled', function (b) {
112 | var button = document.querySelector('[data-cmd=enable]');
113 | button.value = b ? 'Disable all filters temporarily' : 'Enable filters rules';
114 | button.dataset.value = b;
115 | });
116 |
--------------------------------------------------------------------------------
/JPM/data/settings/index.css:
--------------------------------------------------------------------------------
1 | body, table, input, select {
2 | font-family: arial,sans-serif;
3 | font-size: 12px;
4 | }
5 | h1 {
6 | font-size: 120%;
7 | }
8 | h1:after {
9 | content:' ';
10 | display:block;
11 | border:1px solid #d0d0d0;
12 | border-radius:4px;
13 | box-shadow:inset 0 1px 1px rgba(0, 0, 0, .05);
14 | }
15 | table {
16 | width: 100%;
17 | }
18 | table thead {
19 | text-align: center;
20 | font-size: 100%;
21 | font-weight: bold;
22 | }
23 | input,
24 | select {
25 | height: 22px;
26 | border: solid 1px black;
27 | margin: 2px 0;
28 | }
29 |
30 | .hidden {
31 | display: none;
32 | }
33 |
34 | #rules tbody tr:nth-child(even) {
35 | background-color: #ccc;
36 | }
37 | #rules td:nth-child(2),
38 | #rules td:nth-child(4),
39 | #rules td:nth-child(5),
40 | #rules td:nth-child(6) {
41 | white-space: nowrap;
42 | width: 1px;
43 | padding: 0 5px;
44 | }
45 | #rules td:nth-child(3) {
46 | width: 120px;
47 | }
48 |
49 | #add thead td:nth-child(2),
50 | #add thead td:nth-child(4),
51 | #add thead td:nth-child(5),
52 | #add thead td:nth-child(6) {
53 | white-space: nowrap;
54 | width: 1px;
55 | padding: 0 5px;
56 | }
57 | #add td:nth-child(3) {
58 | width: 150px;
59 | }
60 | #add input[type=text] {
61 | width: 100%;
62 | }
63 | #add input[type=checkbox] {
64 | width: 20px;
65 | height: 20px;
66 | -moz-appearance: none;
67 | }
68 |
--------------------------------------------------------------------------------
/JPM/data/settings/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Filter Settings - Policy Control
7 |
8 |
9 |
How to
10 |
By enabling a policy rule in the toolbar panel, all resources matching the policy rule will be blocked. This might be annoying as some website might not work properly after a policy rule is active. Here you can add exceptions to the global policy rules defined in the toolbar panel by allowing individual resource either per domain or globally (*). To do so, simply add the URL of the resource you would like the extension to allow access, then press "Add" button. Rules section contains all the currently allowed resources (provided they are marked as enabled).
11 |
Note: If you are not sure the URL of the resource you would like to allow, open the Error Console (Ctrl + Shift +J), then clear it. Now open the toolbar panel and check "Log blocked URLs in the Error Console". If you refresh the page, you will see the list of all blocked resources in the Console. Make sure to uncheck logging, after the resource is found.
12 |
You can find more about filtering rules in the extensions homepage .
13 |
Active Policy Rules
14 |
36 |
Add a new Policy Rule
37 |
79 |
80 | 1: WildCard only accepts one asterisk.
81 |
82 | 2: In WildCard matching, a domain name prefixed with an asterisk and dot matches any URL of that domain or a subdomain, using any of http, https, ftp (e.g: "*.example.com")
83 |
84 | 3: In WildCard matching, a URL followed by an asterisk matches that URL and any URL prefixed with the pattern (e.g: "https://foo.com/*")
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/JPM/data/settings/index.js:
--------------------------------------------------------------------------------
1 | /* globals self */
2 | 'use strict';
3 |
4 | var add = {
5 | url: document.getElementById('add').querySelector('input'),
6 | button: document.querySelector('[data-cmd=add]'),
7 | matching: document.getElementById('add').querySelector('select'),
8 | domain: document.getElementById('add').querySelector('input[data-type=domain]'),
9 | type: document.getElementById('add').querySelector('select[data-type=type]'),
10 | enabled: document.getElementById('add').querySelector('[type=checkbox]')
11 | };
12 |
13 | var rules = document.getElementById('rules');
14 |
15 | function validate () {
16 | if (add.matching.value === 'WildCard' && add.url.value.split('*').length > 2) {
17 | window.alert('WildCard only accepts one "*"');
18 | add.button.disabled = true;
19 | return;
20 | }
21 | add.button.disabled = !add.url.value;
22 | }
23 | document.addEventListener('input', validate);
24 |
25 | document.addEventListener('click', function (e) {
26 | var target = e.target;
27 | if (target.dataset.cmd === 'add') {
28 | self.port.emit('insert', {
29 | url: add.url.value,
30 | matching: add.matching.value,
31 | domain: add.domain.value || '*',
32 | type: add.type.value,
33 | enabled: add.enabled.checked
34 | });
35 | }
36 | });
37 | document.addEventListener('click', function (e) {
38 | var target = e.target;
39 | if (target.dataset.cmd === 'delete') {
40 | self.port.emit('delete', target.parentNode.parentNode.dataset.id);
41 | }
42 | });
43 | self.port.on('delete', function (id) {
44 | var tr = document.querySelector('[data-id="' + id + '"]');
45 | if (tr) {
46 | tr.parentNode.removeChild(tr);
47 | }
48 | });
49 |
50 | function insert (obj) {
51 | var tr = document.querySelector('[data-id="-1"]').cloneNode(true);
52 | var child = tr.children;
53 | child[0].textContent = obj.url;
54 | child[1].textContent = obj.matching;
55 | child[2].textContent = obj.domain;
56 | child[3].textContent = obj.type;
57 | child[4].textContent = obj.enabled;
58 | tr.dataset.id = obj.id;
59 | rules.querySelector('tbody').appendChild(tr);
60 | tr.classList.remove('hidden');
61 | }
62 | self.port.on('list', function (arr) {
63 | arr.forEach(insert);
64 | });
65 | self.port.on('insert', insert);
66 | self.port.emit('list');
67 |
--------------------------------------------------------------------------------
/JPM/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/icon.png
--------------------------------------------------------------------------------
/JPM/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/JPM/icon64.png
--------------------------------------------------------------------------------
/JPM/install.rdf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | jid1-gHwvGmJ8Ii9oOq@jetpack
5 | 2
6 | true
7 | false
8 | 0.2.1
9 | Policy Control (JavaScript, CSS, Media, ...)
10 | This extension helps you quickly disable and enable different types of resources such as JavaScript, CSS, and Media. The extension can be used to increase privacy as well as saving bandwidth by blocking unwanted contents.
11 | Jeremy Schomery
12 | data:text/xml,<placeholder/>
13 | 2
14 |
15 |
16 |
17 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384}
18 | 29.0
19 | 46.0
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/JPM/lib/main.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var panels = require('sdk/panel');
4 | var self = require('sdk/self');
5 | var sp = require('sdk/simple-prefs');
6 | var prefs = sp.prefs;
7 | var tabs = require('sdk/tabs');
8 | var timers = require('sdk/timers');
9 | var core = require('sdk/view/core');
10 | var unload = require('sdk/system/unload');
11 | var runtime = require('sdk/system/runtime');
12 | var pageMod = require('sdk/page-mod');
13 | var policy = require('./policy');
14 | var {ToggleButton} = require('sdk/ui/button/toggle');
15 | var {Cu} = require('chrome');
16 |
17 | var {devtools} = Cu.import('resource://gre/modules/devtools/Loader.jsm');
18 | var HUDService;
19 | try {
20 | HUDService = devtools.require('devtools/webconsole/hudservice');
21 | }
22 | catch (e) {
23 | HUDService = devtools.require('devtools/client/webconsole/hudservice');
24 | }
25 |
26 | var path = './icons/' + (runtime.OS === 'Darwin' ? 'mac/' : '');
27 |
28 | var filters = {
29 | read: function () {
30 | return JSON.parse(prefs.filters || '[]');
31 | },
32 | write: function (val) {
33 | var fs = this.read();
34 | fs.push(val);
35 | prefs.filters = JSON.stringify(fs);
36 | policy.filters(fs);
37 | },
38 | remove: function (id) {
39 | var fs = this.read();
40 | prefs.filters = JSON.stringify(fs.filter(o => o.id !== +id));
41 | policy.filters(this.read());
42 | }
43 | };
44 |
45 | var states = [];
46 |
47 | var button = new ToggleButton({
48 | id: 'policy-control',
49 | label: 'Policy Control',
50 | icon: {
51 | '16': path + '16.png',
52 | '32': path + '32.png',
53 | '64': path + '64.png',
54 | },
55 | onChange: function (state) {
56 | if (state.checked) {
57 | panel.show({
58 | position: button
59 | });
60 | }
61 | }
62 | });
63 | function state (obj) {
64 | button.state('window', Object.assign({
65 | badge: button.state('window').badge,
66 | checked: button.state('window').checked
67 | }, obj));
68 | }
69 |
70 | var panel = panels.Panel({
71 | contentScriptOptions: {
72 | font: sp.prefs.font
73 | },
74 | contentURL: self.data.url('popover/index.html'),
75 | contentScriptFile: self.data.url('popover/index.js'),
76 | onHide: () => state({checked: false})
77 | });
78 | core.getActiveView(panel).setAttribute('tooltip', 'aHTMLTooltip');
79 |
80 | panel.port.on('size', function (obj) {
81 | panel.width = obj.width;
82 | panel.height = obj.height;
83 | });
84 |
85 | panel.port.on('get-preference', function (name) {
86 | panel.port.emit('set-preference', {
87 | name,
88 | value: prefs[name]
89 | });
90 | });
91 | panel.port.on('set-preference', function (obj) {
92 | prefs[obj.name] = obj.value;
93 | });
94 | sp.on('*', function (name) {
95 | if (name) {
96 | panel.port.emit('set-preference', {
97 | name,
98 | value: prefs[name]
99 | });
100 | }
101 | });
102 | panel.port.on('command', function (obj) {
103 | if (obj.cmd === 'options') {
104 | options();
105 | panel.hide();
106 | }
107 | if (obj.cmd === 'console') {
108 | HUDService.openBrowserConsoleOrFocus();
109 | panel.hide();
110 | }
111 | if (obj.cmd.indexOf('log-') === 0 || obj.cmd === 'private') {
112 | prefs[obj.cmd] = obj.value;
113 | }
114 | if (obj.cmd === 'enable') {
115 | if (obj.value === 'true') {
116 | states = obj.states;
117 | states.forEach(function (obj) {
118 | prefs[obj.name] = false;
119 | });
120 | panel.port.emit('enabled', false);
121 | }
122 | else {
123 | states.forEach(function (obj) {
124 | prefs[obj.name] = obj.status;
125 | });
126 | panel.port.emit('enabled', true);
127 | }
128 | if (obj.enabled === 'false') {
129 | states = [];
130 | }
131 | }
132 | });
133 |
134 | unload.when(function () {
135 | if (states.length) {
136 | states.forEach(function (obj) {
137 | prefs[obj.name] = obj.status;
138 | });
139 | }
140 | });
141 |
142 | /* policy */
143 | policy.badge(count => state({badge: count || ''}));
144 | policy.filters(filters.read());
145 |
146 | /* settings page */
147 | function closeOptions () {
148 | for (let tab of tabs) {
149 | if (tab.url.indexOf(self.data.url('settings/index.html')) === 0) {
150 | tab.close();
151 | }
152 | }
153 | }
154 | function options () {
155 | closeOptions();
156 | tabs.open(self.data.url('settings/index.html'));
157 | }
158 | unload.when(closeOptions);
159 | pageMod.PageMod({
160 | include: self.data.url('settings/index.html'),
161 | contentScriptFile: self.data.url('settings/index.js'),
162 | contentScriptWhen: 'ready',
163 | onAttach: function (worker) {
164 | worker.port.on('list', function () {
165 | worker.port.emit('list', filters.read());
166 | });
167 | worker.port.on('delete', function (id) {
168 | filters.remove(id);
169 | worker.port.emit('delete', id);
170 | });
171 | worker.port.on('insert', function (obj) {
172 | var id = (prefs.id || 0) + 1;
173 | prefs.id = id;
174 | obj.id = id;
175 | filters.write(obj);
176 | worker.port.emit('insert', obj);
177 | });
178 | }
179 | });
180 | sp.on('options', options);
181 |
182 | /* welcome */
183 | exports.main = function (options) {
184 | if (options.loadReason === 'install' || options.loadReason === 'startup') {
185 | var version = sp.prefs.version;
186 | if (self.version !== version) {
187 | if (true) {
188 | timers.setTimeout(function () {
189 | tabs.open(
190 | 'http://firefox.add0n.com/policy-control.html?v=' + self.version +
191 | (version ? '&p=' + version + '&type=upgrade' : '&type=install')
192 | );
193 | }, 3000);
194 | }
195 | sp.prefs.version = self.version;
196 | }
197 | }
198 | };
199 |
--------------------------------------------------------------------------------
/JPM/lib/policy.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var sp = require('sdk/simple-prefs');
4 | var prefs = sp.prefs;
5 | var timers = require('sdk/timers');
6 | var utils = require('sdk/tabs/utils');
7 | var urls = require('sdk/url');
8 | var tabs = require('sdk/tabs');
9 | var utils = require('sdk/tabs/utils');
10 | var unload = require('sdk/system/unload');
11 | var {Unknown, Factory} = require('sdk/platform/xpcom');
12 | var {isPrivate} = require('sdk/private-browsing');
13 | var {Class} = require('sdk/core/heritage');
14 | var {Cc, Ci} = require('chrome');
15 | var {MatchPattern} = require('sdk/util/match-pattern');
16 | var {viewFor} = require('sdk/view/core');
17 |
18 | var categoryManager = Cc['@mozilla.org/categorymanager;1']
19 | .getService(Ci.nsICategoryManager);
20 | var pointers = {
21 | privates: new WeakMap(),
22 | tabs: new WeakMap(),
23 | counts: new WeakMap(),
24 | };
25 | var af = {};
26 | var id;
27 | var setBadge = function () {};
28 | var types = {
29 | 2: 'TYPE_SCRIPT',
30 | 3: 'TYPE_IMAGE',
31 | 4: 'TYPE_STYLESHEET',
32 | 5: 'TYPE_OBJECT',
33 | 7: 'TYPE_SUBDOCUMENT',
34 | 10: 'TYPE_PING',
35 | 11: 'TYPE_XMLHTTPREQUEST',
36 | 12: 'TYPE_OBJECT_SUBREQUEST',
37 | 14: 'TYPE_FONT',
38 | 15: 'TYPE_MEDIA',
39 | 16: 'TYPE_WEBSOCKET',
40 | 19: 'TYPE_BEACON'
41 | };
42 | var [modes, active] = (function (map, tmp1, tmp2) {
43 | for (let name in map) {
44 | tmp1[map[name]] = prefs['mod-' + name];
45 | tmp2[map[name]] = prefs['policy-' + name];
46 | }
47 | sp.on('*', function (pref) {
48 | if (pref.indexOf('mod-') === 0) {
49 | tmp1[map[pref.split('-')[1]]] = prefs[pref];
50 | }
51 | if (pref.indexOf('policy-') === 0) {
52 | tmp2[map[pref.split('-')[1]]] = prefs[pref];
53 | }
54 | });
55 | return [tmp1, tmp2];
56 | })({
57 | 'font': Ci.nsIContentPolicy.TYPE_FONT,
58 | 'image': Ci.nsIContentPolicy.TYPE_IMAGE,
59 | 'media': Ci.nsIContentPolicy.TYPE_MEDIA,
60 | 'object': Ci.nsIContentPolicy.TYPE_OBJECT,
61 | 'stylesheet': Ci.nsIContentPolicy.TYPE_STYLESHEET,
62 | 'script': Ci.nsIContentPolicy.TYPE_SCRIPT,
63 | 'subdomain': Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
64 | 'request': Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST,
65 | 'subrequest': Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST,
66 | 'websocket': Ci.nsIContentPolicy.TYPE_WEBSOCKET,
67 | 'ping': Ci.nsIContentPolicy.TYPE_PING,
68 | 'beacon': Ci.nsIContentPolicy.TYPE_BEACON
69 | }, {}, {});
70 |
71 | function getTopContext (context) {
72 | if (!(context instanceof Ci.nsIDOMWindow)) {
73 | if (context instanceof Ci.nsIDOMNode && context.ownerDocument) {
74 | context = context.ownerDocument;
75 | }
76 | if (context instanceof Ci.nsIDOMDocument) {
77 | context = context.defaultView;
78 | }
79 | else {
80 | context = null;
81 | }
82 | }
83 | return context && context.top ? context.top : context;
84 | }
85 |
86 | tabs.on('activate', function (tab) {
87 | let browser = utils.getBrowserForTab(viewFor(tab));
88 | let count = pointers.counts.get(browser);
89 | setBadge(count);
90 | });
91 |
92 | function valid (url, top, type) {
93 | let filters = af[type];
94 | if (!filters) {
95 | return true;
96 | }
97 | let partial = modes[type] === 'p';
98 | if (partial) {
99 | try {
100 | let host = urls.URL(url).host;
101 | if ((host && host.split(top)[1] === '') || url.indexOf('data:') === 0) {
102 | if (prefs['log-passed']) {
103 | console.error('[Passed]', 'Passed by third-party rule', url, 'domain:', top);
104 | }
105 | return false;
106 | }
107 | }
108 | catch (e) {
109 | console.error(e);
110 | }
111 | }
112 |
113 | for (let i = 0; i < filters.length; i++) {
114 | let filter = filters[i];
115 | if (filter.domain !== '*' && filter.domain !== top && filter.domain !== top) {
116 | continue;
117 | }
118 | try {
119 | if (filter.matching === 'RegExp') {
120 | let re = new RegExp(filter.url);
121 | if (re.test(url)) {
122 | if (prefs['log-passed']) {
123 | console.error('[Passed]', 'passed by RegExp matching rule', 'domain:', url);
124 | }
125 | return false;
126 | }
127 | }
128 | else {
129 | let pattern = new MatchPattern(filter.url);
130 | if (pattern.test(url)) {
131 | if (prefs['log-passed']) {
132 | console.error('[Passed]', 'passed by WildCard matching rule', 'domain:', url);
133 | }
134 | return false;
135 | }
136 | }
137 | }
138 | catch (e) {
139 | console.error(e);
140 | }
141 | }
142 | return true;
143 | }
144 |
145 | var policy2 = new Class({
146 | extends: Unknown,
147 | interfaces: ['nsISimpleContentPolicy'],
148 | shouldLoad: function (aContentType, aContentLocation, aRequestOrigin, aTopFrameElement, aIsTopLevel, aMimeTypeGuess, aExtra, aRequestPrincipal) {
149 | // do not block at document level
150 | if (aContentType === Ci.nsIContentPolicy.TYPE_DOCUMENT) {
151 | pointers.privates.set(aTopFrameElement, isPrivate(aTopFrameElement.contentWindow));
152 | pointers.counts.set(aTopFrameElement, 0);
153 | for (let t of tabs) {
154 | if (utils.getBrowserForTab(viewFor(t)) === aTopFrameElement) {
155 | pointers.tabs.set(aTopFrameElement, t);
156 | if (t === tabs.activeTab) {
157 | setBadge(0);
158 | }
159 | break;
160 | }
161 | }
162 | return Ci.nsIContentPolicy.ACCEPT;
163 | }
164 | // from internal
165 | if (!aTopFrameElement) {
166 | return Ci.nsIContentPolicy.ACCEPT;
167 | }
168 | // type is not supported
169 | if (!types[aContentType]) {
170 | return Ci.nsIContentPolicy.ACCEPT;
171 | }
172 | // white-listed resources
173 | if (!active[aContentType]) {
174 | return Ci.nsIContentPolicy.ACCEPT;
175 | }
176 | // do not blocked in the private mode (if activated)
177 | if (prefs.private && pointers.privates.get(aTopFrameElement)) {
178 | return Ci.nsIContentPolicy.ACCEPT;
179 | }
180 | if (aRequestOrigin && (aRequestOrigin.scheme === 'http' || aRequestOrigin.scheme === 'https')) {
181 | // validate
182 | let url = aContentLocation.spec;
183 | let top = aRequestPrincipal ? aRequestPrincipal.baseDomain : '';
184 |
185 | if (valid(url, top, aContentType)) {
186 | if (prefs['log-blocked']) {
187 | console.error('[Blocked] url:', url, 'domain:', top, 'type:', types[aContentType]);
188 | }
189 | let count = (pointers.counts.get(aTopFrameElement) || 0) + 1;
190 | pointers.counts.set(aTopFrameElement, count);
191 | if (count) {
192 | timers.clearTimeout(id);
193 | id = timers.setTimeout(function (context) {
194 | let tab = pointers.tabs.get(context);
195 | if (tab && tabs.activeTab === tab) {
196 | setBadge(pointers.counts.get(context));
197 | }
198 | } , 100, aTopFrameElement);
199 | }
200 | return Ci.nsIContentPolicy.REJECT_REQUEST;
201 | }
202 | }
203 | return Ci.nsIContentPolicy.ACCEPT;
204 | },
205 | shouldProcess: () => Ci.nsIContentPolicy.ACCEPT
206 | });
207 | // registering
208 | (function (factory) {
209 | categoryManager.addCategoryEntry('simple-content-policy', factory.contract, factory.contract, false, true);
210 | unload.when(() => categoryManager.deleteCategoryEntry('simple-content-policy', factory.contract, false));
211 | })(new Factory({
212 | Component: policy2,
213 | contract: '@add0n.com/simpletestpolicy;1',
214 | description: 'Blocking network resources'
215 | }));
216 |
217 | exports.badge = c => setBadge = c;
218 |
219 | exports.filters = function (filters) {
220 | af[Ci.nsIContentPolicy.TYPE_FONT] = filters.filter(o => o.enabled && o.type === 'TYPE_FONT');
221 | af[Ci.nsIContentPolicy.TYPE_IMAGE] = filters.filter(o => o.enabled && o.type === 'TYPE_IMAGE');
222 | af[Ci.nsIContentPolicy.TYPE_MEDIA] = filters.filter(o => o.enabled && o.type === 'TYPE_MEDIA');
223 | af[Ci.nsIContentPolicy.TYPE_OBJECT] = filters.filter(o => o.enabled && o.type === 'TYPE_OBJECT');
224 | af[Ci.nsIContentPolicy.TYPE_STYLESHEET] = filters.filter(o => o.enabled && o.type === 'TYPE_STYLESHEET');
225 | af[Ci.nsIContentPolicy.TYPE_SCRIPT] = filters.filter(o => o.enabled && o.type === 'TYPE_SCRIPT');
226 | af[Ci.nsIContentPolicy.TYPE_SUBDOCUMENT] = filters.filter(o => o.enabled && o.type === 'TYPE_SUBDOCUMENT');
227 | af[Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST] = filters.filter(o => o.enabled && o.type === 'TYPE_XMLHTTPREQUEST');
228 | af[Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST] = filters.filter(o => o.enabled && o.type === 'TYPE_OBJECT_SUBREQUEST');
229 | af[Ci.nsIContentPolicy.TYPE_WEBSOCKET] = filters.filter(o => o.enabled && o.type === 'TYPE_WEBSOCKET');
230 | af[Ci.nsIContentPolicy.TYPE_PING] = filters.filter(o => o.enabled && o.type === 'TYPE_PING');
231 | af[Ci.nsIContentPolicy.TYPE_BEACON] = filters.filter(o => o.enabled && o.type === 'TYPE_BEACON');
232 | };
233 |
234 | unload.when(function () {
235 | prefs['log-blocked'] = false;
236 | prefs['log-passed'] = false;
237 | });
238 |
--------------------------------------------------------------------------------
/JPM/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Policy Control (JavaScript, CSS, Media, ...)",
3 | "id": "jid1-gHwvGmJ8Ii9oOq@jetpack",
4 | "description": "This extension helps you quickly disable and enable different types of resources such as JavaScript, CSS, and Media. The extension can be used to increase privacy as well as saving bandwidth by blocking unwanted contents.",
5 | "author": "Jeremy Schomery",
6 | "license": "MPL 2.0",
7 | "version": "0.2.0",
8 | "icon": "data/icons/mac/32.png",
9 | "icon64": "data/icons/mac/64.png",
10 | "main": "./lib/main.js",
11 | "permissions": {
12 | "private-browsing": true
13 | },
14 | "preferences": [{
15 | "type": "control",
16 | "label": "Options",
17 | "name": "options",
18 | "title": "Open options page:"
19 | }]
20 | }
21 |
--------------------------------------------------------------------------------
/WebExtension/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/.DS_Store
--------------------------------------------------------------------------------
/WebExtension/background.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var tabs = {};
4 | chrome.tabs.onRemoved.addListener(tabId => delete tabs[tabId]);
5 |
6 | var prefs = {
7 | 'enabled': false,
8 |
9 | 'sub_frame': 2, // 0: block, 1: block on third-party, 2: allow
10 | 'stylesheet': 2,
11 | 'script': 1,
12 | 'image': 2,
13 | 'font': 2,
14 | 'object': 1,
15 | 'xmlhttprequest': 1,
16 | 'ping': 0,
17 | 'csp_report': 0,
18 | 'media': 1,
19 | 'websocket': 1,
20 | 'other': 2,
21 |
22 | 'bypass.sub_frame': [],
23 | 'bypass.stylesheet': [],
24 | 'bypass.script': [],
25 | 'bypass.image': [],
26 | 'bypass.font': [],
27 | 'bypass.object': [],
28 | 'bypass.xmlhttprequest': ['https://*.googlevideo.com'],
29 | 'bypass.ping': [],
30 | 'bypass.csp_report': [],
31 | 'bypass.media': [],
32 | 'bypass.websocket': [],
33 | 'bypass.other': [],
34 | };
35 |
36 | var log = function() {
37 | console.log(...arguments);
38 | };
39 |
40 | var filter = (d, callback) => {
41 | // do not block background requests
42 | if (d.tabId === -1) {
43 | //log('[filter][allowed]', d.type, d.url, 'background request');
44 | return false;
45 | }
46 | // do not block "main_frame" requests
47 | if (d.type === 'main_frame') {
48 | chrome.browserAction.setBadgeText({
49 | text: '',
50 | tabId: d.tabId
51 | });
52 |
53 | const {origin, hostname, scheme} = new URL(d.url);
54 | tabs[d.tabId] = {
55 | origin,
56 | hostname,
57 | scheme,
58 | // a temporary solution until the actual domain is resolved asynchronously
59 | domain: hostname.split('.').slice(-2).join('.'),
60 | blocked: []
61 | };
62 | // find the actual domain name
63 | chrome.tabs.executeScript(d.tabId, {
64 | 'runAt': 'document_start',
65 | 'code': `
66 | (function(){
67 | var i=0, domain=document.domain, p=domain.split('.'), s='_gd'+(new Date()).getTime();
68 | while(i < (p.length-1) && document.cookie.indexOf(s+'='+s) === -1){
69 | domain = p.slice(-1-(++i)).join('.');
70 | document.cookie = s+"="+s+";domain="+domain+";";
71 | }
72 | document.cookie = s+"=;expires=Thu, 01 Jan 1970 00:00:01 GMT;domain="+domain+";";
73 | return domain;
74 | })()
75 | `
76 | }, ([domain]) => tabs[d.tabId].domain = domain);
77 |
78 | //log('[allowed]', d.type, d.url, 'main_frame request', 'module.filter');
79 | return false;
80 | }
81 | return callback(d);
82 | };
83 |
84 | var wildCompare = (string, search) => {
85 | let startIndex = 0;
86 | const array = search.split('*');
87 | for (var i = 0; i < array.length; i += 1) {
88 | const index = string.indexOf(array[i], startIndex);
89 | if (index === -1) {
90 | return false;
91 | }
92 | else {
93 | startIndex = index;
94 | }
95 | }
96 | return true;
97 | };
98 |
99 | var bypass = d => {
100 | const type = 'bypass.' + d.type;
101 | const list = type in prefs ? prefs[type] : prefs['bypass.other'];
102 | return list.some(origin => {
103 | if (origin.startsWith('r:')) {
104 | return (new RegExp(origin.substr(2))).test(d.url);
105 | }
106 | else if (origin.indexOf('*') === -1) {
107 | return d.url.startsWith(origin);
108 | }
109 | else {
110 | return wildCompare(d.url, origin);
111 | }
112 | });
113 | };
114 |
115 | var push = d => {
116 | if (bypass(d) === false) {
117 | const {tabId, url, type} = d;
118 | if (tabs[tabId]) {
119 | tabs[tabId].blocked.push({url, type});
120 | chrome.browserAction.setBadgeText({
121 | text: String(tabs[tabId].blocked.length),
122 | tabId: d.tabId
123 | });
124 | }
125 | return true;
126 | }
127 | else {
128 | //log('[allowed]', d.type, d.url, 'origin is in the bypass list', 'module.bypass');
129 | return false;
130 | }
131 | };
132 |
133 | var block = d => {
134 | const rules = tabs[d.tabId] ? prefs[tabs[d.tabId].hostname] || prefs : prefs;
135 | // https://github.com/schomery/policy-control/issues/16
136 | if (d.type === 'imageset') {
137 | d.type = 'image';
138 | }
139 | // use "other" type if d.type is not supported
140 | switch (d.type in rules ? rules[d.type] : rules.other) {
141 | case 0:
142 | //log('[blocked]', d.type, d.url, 'request rule is 0', 'module.block');
143 | return push(d);
144 | case 1: {
145 | if (tabs[d.tabId]) {
146 | let rtn = d.url.startsWith(tabs[d.tabId].origin) === false;
147 | // sub-domain (http://cmd.add0n.com)
148 | rtn = rtn && d.url.indexOf('.' + tabs[d.tabId].domain + '/') === -1;
149 | // domain (http://add0n.com)
150 | rtn = rtn && d.url.indexOf('//' + tabs[d.tabId].domain + '/') === -1;
151 |
152 | //log(`[${rtn ? 'blocked' : 'allowed'}]`, d.type, d.url, 'request rule is 1 and origin is ' + tabs[d.tabId].origin, 'module.block');
153 | return rtn && push(d);
154 | }
155 | else {
156 | // do not allow a request when main_frame is not loaded
157 | //log('[blocked]', d.type, d.url, 'request occurs before main_frame is emitted', 'module.block');
158 | return true;
159 | }
160 | }
161 | case 2: {
162 | //log('[allowed]', d.type, d.url, 'request rule is 2', 'module.block');
163 | return false;
164 | }
165 | // other resources
166 | default:
167 | console.error('default is not supposed to be called', d);
168 | return false;
169 | }
170 | };
171 |
172 | var observe = d => ({
173 | cancel: filter(d, block)
174 | });
175 | var install = () => {
176 | if (prefs.enabled) {
177 | chrome.webRequest.onBeforeRequest.addListener(observe, {
178 | urls: ['*://*/*']
179 | },
180 | ['blocking']);
181 | }
182 | else {
183 | chrome.webRequest.onBeforeRequest.removeListener(observe);
184 | }
185 | chrome.browserAction.setBadgeText({
186 | text: prefs.enabled ? '' : 'd'
187 | });
188 | chrome.browserAction.setTitle({
189 | title: 'Policy Control (' + (prefs.enabled ? 'enabled' : 'disabled') + ')'
190 | });
191 | };
192 |
193 | // communications
194 | chrome.runtime.onMessage.addListener((request, sender, response) => {
195 | if (request.method === 'get-info') {
196 | response({
197 | blocked: tabs[request.tabId] ? tabs[request.tabId].blocked : [],
198 | tab: tabs[request.tabId]
199 | });
200 | }
201 | });
202 |
203 | // prefs
204 | chrome.storage.local.get(null, ps => {
205 | Object.assign(prefs, ps);
206 | if (ps.auto && ps.enabled === false) {
207 | chrome.storage.local.set({
208 | enabled: true
209 | });
210 | }
211 | else {
212 | install();
213 | }
214 | });
215 | chrome.storage.onChanged.addListener(ps => {
216 | Object.keys(ps).forEach(key => prefs[key] = ps[key].newValue);
217 | if (ps.enabled) {
218 | install();
219 | }
220 | });
221 |
222 | // FAQs & Feedback
223 | chrome.storage.local.get({
224 | 'version': null,
225 | 'faqs': false,
226 | 'last-update': 0,
227 | }, prefs => {
228 | const version = chrome.runtime.getManifest().version;
229 |
230 | if (prefs.version ? (prefs.faqs && prefs.version !== version) : true) {
231 | const now = Date.now();
232 | const doUpdate = (now - prefs['last-update']) / 1000 / 60 / 60 / 24 > 30;
233 | chrome.storage.local.set({
234 | version,
235 | 'last-update': doUpdate ? Date.now() : prefs['last-update']
236 | }, () => {
237 | // do not display the FAQs page if last-update occurred less than 30 days ago.
238 | if (doUpdate) {
239 | const p = Boolean(prefs.version);
240 | chrome.tabs.create({
241 | url: chrome.runtime.getManifest().homepage_url + '?version=' + version +
242 | '&type=' + (p ? ('upgrade&p=' + prefs.version) : 'install'),
243 | active: p === false
244 | });
245 | }
246 | });
247 | }
248 | });
249 |
250 | {
251 | const {name, version} = chrome.runtime.getManifest();
252 | chrome.runtime.setUninstallURL(
253 | chrome.runtime.getManifest().homepage_url + '?rd=feedback&name=' + name + '&version=' + version
254 | );
255 | }
256 |
--------------------------------------------------------------------------------
/WebExtension/data/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/.DS_Store
--------------------------------------------------------------------------------
/WebExtension/data/icons/128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/128.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/16.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/20.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/24.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/256.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/32.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/48.png
--------------------------------------------------------------------------------
/WebExtension/data/icons/64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/schomery/policy-control/300f3ef98a6a4db703def379c7bbca3b75594e60/WebExtension/data/icons/64.png
--------------------------------------------------------------------------------
/WebExtension/data/log/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Logs :: Policy Control
6 |
7 |
8 |
44 |
List of all the blocked request for "- ". You can allow a request by modifying the popup rules (either for this origin or globally) or by adding an origin of a particular type to the global white-list
45 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/WebExtension/data/log/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var tabId = window.location.search.replace('?tabId=', '');
4 |
5 | if (tabId) {
6 | tabId = Number(tabId);
7 | }
8 | else {
9 | window.alert('Cannot find tabId argument in the URL!');
10 | }
11 |
12 | chrome.runtime.sendMessage({
13 | method: 'get-info',
14 | tabId
15 | }, response => {
16 | if (response.tab) {
17 | document.getElementById('url').textContent = response.tab.hostname;
18 | }
19 |
20 | const template = document.querySelector('template');
21 | const tbody = document.querySelector('tbody');
22 | response.blocked.forEach(o => {
23 | const clone = document.importNode(template.content, true);
24 |
25 | if (['sub_frame', 'stylesheet', 'script', 'image', 'font', 'object', 'xmlhttprequest', 'ping', 'csp_report', 'media', 'websocket'].indexOf(o.type) === -1) {
26 | o.type = 'other';
27 | }
28 |
29 | clone.querySelector('td:nth-child(1)').textContent = o.type;
30 | clone.querySelector('td:nth-child(2)').textContent = o.url;
31 | const tr = clone.querySelector('tr');
32 | Object.assign(tr.dataset, o);
33 | tbody.appendChild(clone);
34 | });
35 | });
36 |
37 | document.addEventListener('click', ({target}) => {
38 | const cmd = target.dataset.cmd;
39 | if (cmd === 'add') {
40 | const tr = target.closest('tr');
41 | const o = tr.dataset;
42 | const uri = new URL(o.url);
43 | if (window.confirm(`Globally allow "${o.type}" type requests for "${uri.origin}" origin?`)) {
44 | const type = 'bypass.' + o.type;
45 | chrome.storage.local.get({
46 | [type]: []
47 | }, prefs => {
48 | prefs[type].push(uri.origin);
49 | prefs[type] = prefs[type].filter((s, i, l) => l.indexOf(s) === i);
50 | chrome.storage.local.set({
51 | [type]: prefs[type]
52 | }, () => {
53 | [...document.querySelectorAll('tr')].filter(tr => tr.dataset.type === o.type).forEach(tr => {
54 | if (tr.dataset.url.startsWith(uri.origin)) {
55 | tr.parentNode.removeChild(tr);
56 | }
57 | });
58 | tr.parentNode.removeChild(tr);
59 | });
60 | });
61 | }
62 | }
63 | });
64 |
--------------------------------------------------------------------------------
/WebExtension/data/options/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Policy Control :: Options
6 |
7 |
8 |
27 |
37 |
Policy Control is blocking requests based on the general filtering rules defined in the popup (either for a single hostname or globally). In this section, you can define custom origins (e.g. https://www.google.com/ or https://*.google.com) to bypass the blockage. Use "*" for Wild-card matching and "r:" to define a RegExp matching. Enter a comma-separated list of origins to bypass the general blockage rules for each category.
38 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
--------------------------------------------------------------------------------
/WebExtension/data/options/index.js:
--------------------------------------------------------------------------------
1 | /* globals prefs */
2 | 'use strict';
3 |
4 | var restore = () => {
5 | chrome.storage.local.get({
6 | 'bypass.sub_frame': [],
7 | 'bypass.stylesheet': [],
8 | 'bypass.script': [],
9 | 'bypass.image': [],
10 | 'bypass.font': [],
11 | 'bypass.object': [],
12 | 'bypass.xmlhttprequest': ['https://*.googlevideo.com'],
13 | 'bypass.ping': [],
14 | 'bypass.csp_report': [],
15 | 'bypass.media': [],
16 | 'bypass.websocket': [],
17 | 'bypass.other': [],
18 | }, prefs => Object.entries(prefs).forEach(([key, value]) => document.getElementById(key).value = value.join(', ')));
19 | document.getElementById('width').value = localStorage.getItem('width') || 500;
20 | }
21 |
22 | document.addEventListener('DOMContentLoaded', restore);
23 |
24 | chrome.storage.local.get({
25 | 'auto': false
26 | }, prefs => Object.entries(prefs).forEach(([key, value]) => document.getElementById(key).checked = value));
27 |
28 | function cleanup(obj, o2) {
29 | const tmp = {};
30 | Object.entries(obj).forEach(([key, value]) => {
31 | tmp[key] = value.split(/\s*,\s*/).map(s => s.trim())
32 | .filter(s => s.startsWith('http') || s.startsWith('ftp') || s.startsWith('wss') || s.startsWith('r:'))
33 | .filter((s, i, l) => s && l.indexOf(s) === i);
34 | });
35 | return Object.assign(tmp, o2);
36 | }
37 |
38 | document.getElementById('save').addEventListener('click', () => {
39 | chrome.storage.local.set(cleanup({
40 | 'bypass.sub_frame': document.getElementById('bypass.sub_frame').value,
41 | 'bypass.stylesheet': document.getElementById('bypass.stylesheet').value,
42 | 'bypass.script': document.getElementById('bypass.script').value,
43 | 'bypass.image': document.getElementById('bypass.image').value,
44 | 'bypass.font': document.getElementById('bypass.font').value,
45 | 'bypass.object': document.getElementById('bypass.object').value,
46 | 'bypass.xmlhttprequest': document.getElementById('bypass.xmlhttprequest').value,
47 | 'bypass.ping': document.getElementById('bypass.ping').value,
48 | 'bypass.csp_report': document.getElementById('bypass.csp_report').value,
49 | 'bypass.media': document.getElementById('bypass.media').value,
50 | 'bypass.websocket': document.getElementById('bypass.websocket').value,
51 | 'bypass.other': document.getElementById('bypass.other').value,
52 | }, {
53 | 'auto': document.getElementById('auto').checked,
54 | }), () => {
55 | const info = document.getElementById('info');
56 | info.textContent = 'Options saved';
57 | restore();
58 | window.setTimeout(() => info.textContent = '', 750);
59 | });
60 | const num = Number(document.getElementById('width').value);
61 | localStorage.setItem('width', Math.max(Math.min(num, 750), 500));
62 | });
63 |
64 | document.getElementById('export').addEventListener('click', () => chrome.storage.local.get(null, prefs => {
65 | delete prefs.version;
66 | delete prefs.enabled;
67 |
68 | const text = JSON.stringify(prefs, null, '\t');
69 | const url = 'data:text/plain;charset=utf-8,' + encodeURIComponent(text);
70 |
71 | fetch(url)
72 | .then(res => res.blob())
73 | .then(blob => {
74 | const objectURL = URL.createObjectURL(blob);
75 | const link = Object.assign(document.createElement('a'), {
76 | href: objectURL,
77 | type: 'application/json',
78 | download: 'policy-control-settings.json',
79 | });
80 | link.dispatchEvent(new MouseEvent('click'));
81 | setTimeout(() => URL.revokeObjectURL(objectURL));
82 | });
83 | }));
84 |
85 | document.getElementById('import').addEventListener('click', () => {
86 | const input = Object.assign(document.createElement('input'), {
87 | type: 'file',
88 | onchange: function() {
89 | const file = this.files[0];
90 | const reader = new FileReader();
91 | reader.onloadend = event => {
92 | input.remove();
93 | chrome.storage.local.get(null, prefs => {
94 | Object.assign(prefs, JSON.parse(event.target.result));
95 | chrome.storage.local.set(prefs, () => window.location.reload());
96 | });
97 | };
98 | reader.readAsText(file, 'utf-8');
99 | }
100 | });
101 | input.click();
102 | });
103 |
104 | document.getElementById('support').addEventListener('click', () => chrome.tabs.create({
105 | url: chrome.runtime.getManifest().homepage_url + '?rd=donate'
106 | }));
107 |
--------------------------------------------------------------------------------
/WebExtension/data/popup/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: arial,sans-serif;
3 | font-size: 12px;
4 | user-select: none;
5 | background-color: #fff;
6 | }
7 | table {
8 | width: 100%;
9 | table-layout: fixed;
10 | border-collapse: collapse;
11 | }
12 | th {
13 | text-align: left;
14 | height: 24px;
15 | line-height: 24px;
16 | }
17 | tbody tr:hover {
18 | background-color: rgba(0, 0, 0, 0.1);
19 | }
20 | td:last-of-type {
21 | text-align: right;
22 | }
23 |
24 | body[data-perhost=false] [host-only] {
25 | opacity: 0.3;
26 | pointer-events: none;
27 | }
28 |
29 | input[type=button] {
30 | background-color: #fff;
31 | color: #000;
32 | border: solid 1px #ccc;
33 | border-radius: 0;
34 | min-width: 80px;
35 | padding: 4px;
36 | }
37 |
38 | input {
39 | outline: none;
40 | cursor: pointer;
41 | }
42 |
43 | #toolbar {
44 | display: flex;
45 | padding: 10px 0 0 0;
46 | }
47 |
--------------------------------------------------------------------------------
/WebExtension/data/popup/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
96 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/WebExtension/data/popup/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | document.body.style.width = (localStorage.getItem('width') || '500') + 'px';
4 |
5 | var dictionary = {
6 | 0: 'Block all',
7 | 1: 'Block third-party',
8 | 2: 'Allow all',
9 | true: 'enabled',
10 | false: 'disabled'
11 | };
12 |
13 | var update = (hostname, ignore = false) => {
14 | const prefs = {
15 | 'sub_frame': 2,
16 | 'stylesheet': 2,
17 | 'script': 1,
18 | 'image': 2,
19 | 'font': 2,
20 | 'object': 1,
21 | 'xmlhttprequest': 1,
22 | 'ping': 0,
23 | 'csp_report': 0,
24 | 'media': 1,
25 | 'websocket': 1,
26 | 'other': 2
27 | };
28 | if (hostname) {
29 | prefs[hostname] = false;
30 | }
31 | chrome.storage.local.get(prefs, prefs => {
32 | if (ignore === false) {
33 | document.getElementById('hostname').checked = hostname && prefs[hostname];
34 | }
35 |
36 | const config = prefs[hostname] || prefs;
37 | [
38 | 'sub_frame', 'stylesheet', 'script', 'image', 'font', 'object',
39 | 'xmlhttprequest', 'ping', 'csp_report', 'media', 'websocket', 'other'
40 | ].forEach(key => {
41 | const input = document.getElementById(key);
42 |
43 | input.value = config[key];
44 | input.dispatchEvent(new Event('change', {
45 | bubbles: true
46 | }));
47 | });
48 | });
49 | };
50 |
51 | document.addEventListener('change', e => {
52 | const target = e.target;
53 | const tr = target.closest('tr');
54 | if (tr) {
55 | const state = tr.querySelector('.state');
56 | if (state) {
57 | state.textContent = dictionary[target.type === 'checkbox' ? target.checked : target.value];
58 | }
59 | }
60 | if (target.id === 'hostname') {
61 | update(target.checked ? document.body.dataset.hostname : '', true);
62 | }
63 | if (target.id === 'enabled') {
64 | chrome.storage.local.set({
65 | enabled: target.checked
66 | });
67 | }
68 | });
69 |
70 | chrome.storage.local.get({
71 | enabled: false
72 | }, prefs => {
73 | document.getElementById('enabled').checked = prefs.enabled;
74 | document.getElementById('enabled').dispatchEvent(new Event('change', {
75 | bubbles: true
76 | }));
77 | });
78 |
79 | chrome.tabs.query({
80 | active: true,
81 | currentWindow: true
82 | }, ([tab]) => {
83 | const {protocol, hostname} = new URL(tab.url);
84 | const perhost = protocol === 'http:' || protocol === 'https:' || protocol === 'ftp:';
85 |
86 | document.body.dataset.hostname = hostname;
87 | document.body.dataset.perhost = perhost;
88 | document.body.dataset.tabId = tab.id;
89 | document.getElementById('hn').textContent = hostname;
90 |
91 | update(hostname);
92 | });
93 |
94 | document.getElementById('save').addEventListener('click', () => {
95 | const {hostname} = document.body.dataset;
96 | const perhost = document.getElementById('hostname').checked;
97 | if (perhost === false) {
98 | chrome.storage.local.remove(hostname);
99 | }
100 | let prefs = {
101 | 'sub_frame': Number(document.getElementById('sub_frame').value),
102 | 'stylesheet': Number(document.getElementById('stylesheet').value),
103 | 'script': Number(document.getElementById('script').value),
104 | 'image': Number(document.getElementById('image').value),
105 | 'font': Number(document.getElementById('font').value),
106 | 'object': Number(document.getElementById('object').value),
107 | 'xmlhttprequest': Number(document.getElementById('xmlhttprequest').value),
108 | 'ping': Number(document.getElementById('ping').value),
109 | 'csp_report': Number(document.getElementById('csp_report').value),
110 | 'media': Number(document.getElementById('media').value),
111 | 'websocket': Number(document.getElementById('websocket').value),
112 | 'other': Number(document.getElementById('other').value),
113 | };
114 | if (perhost) {
115 | prefs = {
116 | [hostname]: prefs
117 | };
118 | }
119 | chrome.storage.local.set(prefs, () => {
120 | const info = document.getElementById('info');
121 | info.textContent = 'Saved';
122 | window.setTimeout(() => info.textContent = '', 750);
123 | });
124 | });
125 |
126 | document.getElementById('reset').addEventListener('click', () => {
127 | Object.entries({
128 | 'sub_frame': 2,
129 | 'stylesheet': 2,
130 | 'script': 1,
131 | 'image': 2,
132 | 'font': 2,
133 | 'object': 1,
134 | 'xmlhttprequest': 1,
135 | 'ping': 0,
136 | 'csp_report': 0,
137 | 'media': 1,
138 | 'websocket': 1,
139 | 'other': 2
140 | }).forEach(([key, value]) => {
141 | document.getElementById(key).value = value;
142 | document.getElementById(key).dispatchEvent(new Event('change', {
143 | bubbles: true
144 | }));
145 | });
146 | });
147 |
148 | document.getElementById('open-log').addEventListener('click', () => chrome.tabs.create({
149 | url: '/data/log/index.html?tabId=' + document.body.dataset.tabId
150 | }));
151 |
152 | document.getElementById('open-options').addEventListener(
153 | 'click',
154 | () => chrome.runtime.openOptionsPage(() => window.close())
155 | );
156 | document.getElementById('refresh').addEventListener('click', () => {
157 | const id = Number(document.body.dataset.tabId);
158 | chrome.tabs.reload(id, {
159 | bypassCache: true
160 | });
161 | });
162 | document.getElementById('open-faqs').addEventListener('click', () => chrome.tabs.create({
163 | url: 'http://add0n.com/policy-control.html'
164 | }));
165 |
--------------------------------------------------------------------------------
/WebExtension/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "manifest_version": 2,
3 | "version": "0.3.5",
4 | "name": "Policy Control - JavaScript and Flash blocker",
5 | "description": "Block third-party or all resources of style, script, image, font, xmlhttprequest types",
6 |
7 | "permissions": [
8 | "*://*/*",
9 | "storage",
10 | "tabs",
11 | "webRequest",
12 | "webRequestBlocking"
13 | ],
14 | "background": {
15 | "scripts": [
16 | "background.js"
17 | ]
18 | },
19 | "icons": {
20 | "16": "/data/icons/16.png",
21 | "20": "/data/icons/20.png",
22 | "24": "/data/icons/24.png",
23 | "32": "/data/icons/32.png",
24 | "48": "/data/icons/48.png",
25 | "64": "/data/icons/64.png",
26 | "128": "/data/icons/128.png",
27 | "256": "/data/icons/256.png"
28 | },
29 | "browser_action": {
30 | "default_popup": "data/popup/index.html",
31 | "default_icon": {
32 | "16": "/data/icons/16.png",
33 | "20": "/data/icons/20.png",
34 | "24": "/data/icons/24.png",
35 | "32": "/data/icons/32.png",
36 | "48": "/data/icons/48.png",
37 | "64": "/data/icons/64.png"
38 | }
39 | },
40 | "options_ui": {
41 | "page": "data/options/index.html",
42 | "chrome_style": true
43 | },
44 | "homepage_url": "http://add0n.com/policy-control.html",
45 | "applications": {
46 | "gecko": {
47 | "id": "jid1-gHwvGmJ8Ii9oOq@jetpack",
48 | "strict_min_version": "52.0"
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------