diff options
author | Alessio Vanni <vannilla@firemail.cc> | 2019-02-19 21:06:09 +0100 |
---|---|---|
committer | Alessio Vanni <vannilla@firemail.cc> | 2019-02-19 21:06:09 +0100 |
commit | fe2f8acc8210c2ddead4621797b47106a9b38f5b (patch) | |
tree | 5fb103d45d7e4345f56fc068ce8173b82fa7051f /js/user-rules.js | |
download | ematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.tar.lz ematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.tar.xz ematrix-fe2f8acc8210c2ddead4621797b47106a9b38f5b.zip |
Fork uMatrix
Pretty much just changing the name and the copyright.
Diffstat (limited to 'js/user-rules.js')
-rw-r--r-- | js/user-rules.js | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/js/user-rules.js b/js/user-rules.js new file mode 100644 index 0000000..c9c48be --- /dev/null +++ b/js/user-rules.js @@ -0,0 +1,341 @@ +/******************************************************************************* + + ηMatrix - a browser extension to black/white list requests. + Copyright (C) 2014-2019 Raymond Hill + Copyright (C) 2019 Alessio Vanni + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + uMatrix Home: https://github.com/gorhill/uMatrix +*/ + +/* global uDom */ + +'use strict'; + +/******************************************************************************/ + +(function() { + +/******************************************************************************/ + +// Switches before, rules after + +var directiveSort = function(a, b) { + var aIsSwitch = a.indexOf(':') !== -1; + var bIsSwitch = b.indexOf(':') !== -1; + if ( aIsSwitch === bIsSwitch ) { + return a.localeCompare(b); + } + return aIsSwitch ? -1 : 1; +}; + +/******************************************************************************/ + +var processUserRules = function(response) { + var rules, rule, i; + var allRules = {}; + var permanentRules = {}; + var temporaryRules = {}; + var onLeft, onRight; + + rules = response.permanentRules.split(/\n+/); + i = rules.length; + while ( i-- ) { + rule = rules[i].trim(); + if ( rule.length !== 0 ) { + permanentRules[rule] = allRules[rule] = true; + } + } + rules = response.temporaryRules.split(/\n+/); + i = rules.length; + while ( i-- ) { + rule = rules[i].trim(); + if ( rule.length !== 0 ) { + temporaryRules[rule] = allRules[rule] = true; + } + } + + var permanentList = document.createDocumentFragment(), + temporaryList = document.createDocumentFragment(), + li; + + rules = Object.keys(allRules).sort(directiveSort); + for ( i = 0; i < rules.length; i++ ) { + rule = rules[i]; + onLeft = permanentRules.hasOwnProperty(rule); + onRight = temporaryRules.hasOwnProperty(rule); + if ( onLeft && onRight ) { + li = document.createElement('li'); + li.textContent = rule; + permanentList.appendChild(li); + li = document.createElement('li'); + li.textContent = rule; + temporaryList.appendChild(li); + } else if ( onLeft ) { + li = document.createElement('li'); + li.textContent = rule; + permanentList.appendChild(li); + li = document.createElement('li'); + li.textContent = rule; + li.className = 'notRight toRemove'; + temporaryList.appendChild(li); + } else if ( onRight ) { + li = document.createElement('li'); + li.textContent = '\xA0'; + permanentList.appendChild(li); + li = document.createElement('li'); + li.textContent = rule; + li.className = 'notLeft'; + temporaryList.appendChild(li); + } + } + + // TODO: build incrementally. + + uDom('#diff > .left > ul > li').remove(); + document.querySelector('#diff > .left > ul').appendChild(permanentList); + uDom('#diff > .right > ul > li').remove(); + document.querySelector('#diff > .right > ul').appendChild(temporaryList); + uDom('#diff').toggleClass('dirty', response.temporaryRules !== response.permanentRules); +}; + +/******************************************************************************/ + +// https://github.com/chrisaljoudi/uBlock/issues/757 +// Support RequestPolicy rule syntax + +var fromRequestPolicy = function(content) { + var matches = /\[origins-to-destinations\]([^\[]+)/.exec(content); + if ( matches === null || matches.length !== 2 ) { + return; + } + return matches[1].trim() + .replace(/\|/g, ' ') + .replace(/\n/g, ' * allow\n'); +}; + +/******************************************************************************/ + +// https://github.com/gorhill/uMatrix/issues/270 + +var fromNoScript = function(content) { + var noscript = null; + try { + noscript = JSON.parse(content); + } catch (e) { + } + if ( + noscript === null || + typeof noscript !== 'object' || + typeof noscript.prefs !== 'object' || + typeof noscript.prefs.clearClick === 'undefined' || + typeof noscript.whitelist !== 'string' || + typeof noscript.V !== 'string' + ) { + return; + } + var out = new Set(); + var reBad = /[a-z]+:\w*$/; + var reURL = /[a-z]+:\/\/([0-9a-z.-]+)/; + var directives = noscript.whitelist.split(/\s+/); + var i = directives.length; + var directive, matches; + while ( i-- ) { + directive = directives[i].trim(); + if ( directive === '' ) { + continue; + } + if ( reBad.test(directive) ) { + continue; + } + matches = reURL.exec(directive); + if ( matches !== null ) { + directive = matches[1]; + } + out.add('* ' + directive + ' * allow'); + out.add('* ' + directive + ' script allow'); + out.add('* ' + directive + ' frame allow'); + } + return Array.from(out).join('\n'); +}; + +/******************************************************************************/ + +var handleImportFilePicker = function() { + var fileReaderOnLoadHandler = function() { + if ( typeof this.result !== 'string' || this.result === '' ) { + return; + } + var result = fromRequestPolicy(this.result); + if ( result === undefined ) { + result = fromNoScript(this.result); + if ( result === undefined ) { + result = this.result; + } + } + if ( this.result === '' ) { return; } + var request = { + 'what': 'setUserRules', + 'temporaryRules': rulesFromHTML('#diff .right li') + '\n' + result + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); + }; + var file = this.files[0]; + if ( file === undefined || file.name === '' ) { + return; + } + if ( file.type.indexOf('text') !== 0 && file.type !== 'application/json') { + return; + } + var fr = new FileReader(); + fr.onload = fileReaderOnLoadHandler; + fr.readAsText(file); +}; + +/******************************************************************************/ + +var startImportFilePicker = function() { + var input = document.getElementById('importFilePicker'); + // Reset to empty string, this will ensure an change event is properly + // triggered if the user pick a file, even if it is the same as the last + // one picked. + input.value = ''; + input.click(); +}; + +/******************************************************************************/ + +function exportUserRulesToFile() { + vAPI.download({ + 'url': 'data:text/plain,' + encodeURIComponent(rulesFromHTML('#diff .left li') + '\n'), + 'filename': uDom('[data-i18n="userRulesDefaultFileName"]').text() + }); +} + +/******************************************************************************/ + +var rulesFromHTML = function(selector) { + var rules = []; + var lis = uDom(selector); + var li; + for ( var i = 0; i < lis.length; i++ ) { + li = lis.at(i); + if ( li.hasClassName('toRemove') ) { + rules.push(''); + } else { + rules.push(li.text()); + } + } + return rules.join('\n'); +}; + +/******************************************************************************/ + +var revertHandler = function() { + var request = { + 'what': 'setUserRules', + 'temporaryRules': rulesFromHTML('#diff .left li') + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); +}; + +/******************************************************************************/ + +var commitHandler = function() { + var request = { + 'what': 'setUserRules', + 'permanentRules': rulesFromHTML('#diff .right li') + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); +}; + +/******************************************************************************/ + +var editStartHandler = function() { + uDom('#diff .right textarea').val(rulesFromHTML('#diff .right li')); + var parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', true); +}; + +/******************************************************************************/ + +var editStopHandler = function() { + var parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', false); + var request = { + 'what': 'setUserRules', + 'temporaryRules': uDom('#diff .right textarea').val() + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); +}; + +/******************************************************************************/ + +var editCancelHandler = function() { + var parent = uDom(this).ancestors('#diff'); + parent.toggleClass('edit', false); +}; + +/******************************************************************************/ + +var temporaryRulesToggler = function() { + var li = uDom(this); + li.toggleClass('toRemove'); + var request = { + 'what': 'setUserRules', + 'temporaryRules': rulesFromHTML('#diff .right li') + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); +}; + +/******************************************************************************/ + +self.cloud.onPush = function() { + return rulesFromHTML('#diff .left li'); +}; + +self.cloud.onPull = function(data, append) { + if ( typeof data !== 'string' ) { return; } + if ( append ) { + data = rulesFromHTML('#diff .right li') + '\n' + data; + } + var request = { + 'what': 'setUserRules', + 'temporaryRules': data + }; + vAPI.messaging.send('user-rules.js', request, processUserRules); +}; + +/******************************************************************************/ + +uDom.onLoad(function() { + // Handle user interaction + uDom('#importButton').on('click', startImportFilePicker); + uDom('#importFilePicker').on('change', handleImportFilePicker); + uDom('#exportButton').on('click', exportUserRulesToFile); + uDom('#revertButton').on('click', revertHandler); + uDom('#commitButton').on('click', commitHandler); + uDom('#editEnterButton').on('click', editStartHandler); + uDom('#editStopButton').on('click', editStopHandler); + uDom('#editCancelButton').on('click', editCancelHandler); + uDom('#diff > .right > ul').on('click', 'li', temporaryRulesToggler); + + vAPI.messaging.send('user-rules.js', { what: 'getUserRules' }, processUserRules); +}); + +/******************************************************************************/ + +})(); + |