this addresses half of #58: find list(s) from which a static network filter originates

This commit is contained in:
gorhill 2015-06-11 12:12:23 -04:00
parent 74ad47bc71
commit 060a43fe81
11 changed files with 505 additions and 110 deletions

View file

@ -459,6 +459,10 @@
"message":"even if",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringFinderSentence1":{
"message":"Static filter {{filter}} found in:",
"description":"Below this sentence, the filter lists in which the filter was found"
},
"aboutChangelog":{
"message":"Change log",
"description":"English: Change log"

View file

@ -29,6 +29,7 @@
<script src="js/tab.js"></script>
<script src="js/traffic.js"></script>
<script src="js/contextmenu.js"></script>
<script src="js/reverselookup.js"></script>
<script src="js/start.js"></script>
</body>
</html>

View file

@ -189,6 +189,9 @@ body:not(.popupOn) #content tr.canMtx td:nth-of-type(2) {
body:not(.popupOn) #content tr.canMtx td:nth-of-type(2):hover {
background: #ccc;
}
#content tr.cat_net[data-filter] td:nth-of-type(3) {
cursor: zoom-in;
}
#content tr.cat_net td:nth-of-type(4),
#content tr.cat_cosmetic td:nth-of-type(4) {
font: 12px monospace;
@ -275,7 +278,7 @@ body[dir="rtl"] #popupContainer > div {
display: none;
}
#urlFilteringMenu {
.modalDialog {
background-color: rgba(0, 0, 0, 0.5);
border: 0;
bottom: 0;
@ -287,9 +290,10 @@ body[dir="rtl"] #popupContainer > div {
z-index: 400;
}
#urlFilteringMenu .dialog {
.modalDialog .dialog {
background-color: white;
border: 2px solid white;
box-sizing: border-box;
left: 10%;
position: absolute;
top: 50%;
@ -298,11 +302,11 @@ body[dir="rtl"] #popupContainer > div {
width: 80%;
}
#urlFilteringMenu .dialog p {
#netFilteringDialog .dialog p {
line-height: 2em;
}
#urlFilteringMenu .dialog select {
#netFilteringDialog .dialog select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
@ -313,7 +317,7 @@ body[dir="rtl"] #popupContainer > div {
padding: 0.2em;
}
#urlFilteringMenu .dialog > div.preview {
#netFilteringDialog .dialog > div.preview {
/* http://lea.verou.me/css3patterns/ */
background-color: #aaa;
background-image:
@ -337,33 +341,33 @@ body[dir="rtl"] #popupContainer > div {
background-size: 18px 18px;
text-align: center;
}
#urlFilteringMenu .dialog > div.preview > * {
#netFilteringDialog .dialog > div.preview > * {
max-width: 100%;
max-height: 40vh;
}
#urlFilteringMenu .dialog table {
#netFilteringDialog .dialog table {
border: 0;
border-collapse: collapse;
table-layout: fixed;
width: 100%;
}
#urlFilteringMenu .dialog table > colgroup > col:nth-of-type(1) {
#netFilteringDialog .dialog table > colgroup > col:nth-of-type(1) {
width: 3.8em;
}
#urlFilteringMenu .dialog table > colgroup > col:nth-of-type(2) {
#netFilteringDialog .dialog table > colgroup > col:nth-of-type(2) {
}
#urlFilteringMenu .dialog td {
#netFilteringDialog .dialog td {
border: 0;
padding: 0;
vertical-align: middle;
}
#urlFilteringMenu .dialog > div.headers {
#netFilteringDialog .dialog > div.headers {
border-bottom: 1px solid #888;
position: relative;
}
#urlFilteringMenu .dialog > div.headers > span.header {
#netFilteringDialog .dialog > div.headers > span.header {
background-color: #eee;
border: 1px solid #aaa;
border-bottom: 1px solid #888;
@ -380,45 +384,45 @@ body[dir="rtl"] #popupContainer > div {
text-align: center;
top: 1px;
}
#urlFilteringMenu .dialog > div.headers > span.header.selected {
#netFilteringDialog .dialog > div.headers > span.header.selected {
background-color: white;
border-color: #888;
border-bottom: 1px solid white;
color: black;
}
#urlFilteringMenu .dialog > div.headers > span.tools {
#netFilteringDialog .dialog > div.headers > span.tools {
display: inline-block;
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
body[dir="ltr"] #urlFilteringMenu .dialog > div.headers > span.tools {
body[dir="ltr"] #netFilteringDialog .dialog > div.headers > span.tools {
right: 0.1em;
}
body[dir="rtl"] #urlFilteringMenu .dialog > div.headers > span.tools {
body[dir="rtl"] #netFilteringDialog .dialog > div.headers > span.tools {
left: 0.1em;
}
#urlFilteringMenu .dialog > div.headers > span.tools > span {
#netFilteringDialog .dialog > div.headers > span.tools > span {
cursor: pointer;
font-size: 1.2em;
text-align: center;
}
#urlFilteringMenu .dialog > div.containers {
#netFilteringDialog .dialog > div.containers {
height: 40vh;
overflow: hidden;
overflow-y: auto;
}
#urlFilteringMenu .dialog > div.containers > div {
#netFilteringDialog .dialog > div.containers > div {
display: none;
}
#urlFilteringMenu .dialog > div.containers > div.selected {
#netFilteringDialog .dialog > div.containers > div.selected {
display: block;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar select {
#netFilteringDialog .dialog > div.containers > div.dynamic > table.toolbar select {
font: 14px;
height: 2em;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
#netFilteringDialog .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
background-color: #ffe;
border: 1px solid #ddc;
border-radius: 4px;
@ -428,37 +432,37 @@ body[dir="rtl"] #urlFilteringMenu .dialog > div.headers > span.tools {
padding: 0.25em 0.5em;
visibility: hidden;
}
body.dirty #urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
body.dirty #netFilteringDialog .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
visibility: visible;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules:hover {
#netFilteringDialog .dialog > div.containers > div.dynamic > table.toolbar #saveRules:hover {
color: black;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar tr.entry {
#netFilteringDialog .dialog > div.containers > div.dynamic > table.toolbar tr.entry {
display: none;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry {
background-color: #e6e6e6;
border: 0;
border-bottom: 1px solid white;
font-size: 13px;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry:hover {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry:hover {
background-color: #f0f0f0;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td:first-of-type {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td:first-of-type {
border: 0;
border-right: 1px solid white;
text-align: center;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action {
background-color: transparent;
border: 0;
cursor: pointer;
height: 2em;
width: 100%;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span {
background-color: transparent;
border: 0;
display: inline-block;
@ -467,80 +471,98 @@ body.dirty #urlFilteringMenu .dialog > div.containers > div.dynamic > table.tool
visibility: hidden;
width: 33.33%;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
background-color: rgba(0, 160, 0, 0.3);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
background-color: rgba(255, 194, 57, 0.4);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
background-color: rgba(108, 108, 108, 0.3);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
background-color: rgba(96, 96, 96, 0.4);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
background-color: rgba(192, 0, 0, 0.3);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
background-color: rgba(0, 19, 110, 0.4);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
background-color: rgba(0, 160, 0, 1);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
background-color: rgba(255, 194, 57, 1);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.noop {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.noop {
background-color: rgba(108, 108, 108, 1);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
background-color: rgba(192, 0, 0, 1);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
background-color: rgba(0, 19, 110, 1);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span {
opacity: 0.2;
visibility: visible;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span:hover {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span:hover {
opacity: 0.75;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
background-color: rgb(0, 160, 0);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
background-color: rgb(255, 194, 57);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.noop {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.noop {
background-color: rgb(108, 108, 108);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
background-color: rgb(192, 0, 0);
}
body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
body.colorBlind #netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
background-color: rgb(0, 19, 110);
}
#urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td.url {
#netFilteringDialog .dialog > div.containers > div.dynamic tr.entry > td.url {
overflow: hidden;
padding-left: 4px;
text-overflow: ellipsis;
white-space: nowrap;
}
#urlFilteringMenu .dialog > div.containers > div.static > p {
#netFilteringDialog .dialog > div.containers > div.static > p {
margin: 0.75em 0;
}
#urlFilteringMenu .dialog > div.containers > div.static textarea {
#netFilteringDialog .dialog > div.containers > div.static textarea {
box-sizing: border-box;
direction: ltr;
height: 6em;
resize: none;
width: 100%;
}
#urlFilteringMenu .dialog > div.containers > div.static > p:nth-of-type(2) {
#netFilteringDialog .dialog > div.containers > div.static > p:nth-of-type(2) {
text-align: center;
}
#filterFinderDialog .dialog {
padding: 1em;
}
#filterFinderDialog .dialog code {
background: #eee;
padding: 3px;
}
#filterFinderDialog .dialog ul {
font-size: larger;
}
#filterFinderDialog .dialog > *:first-child {
margin-top: 0;
}
#filterFinderDialog .dialog > *:last-child {
margin-bottom: 0;
}
.hide {
display: none;
}

View file

@ -1,6 +1,6 @@
/*******************************************************************************
uMatrix - a browser extension to benchmark browser session.
uBlock Origin - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
This program is free software: you can redistribute it and/or modify
@ -16,7 +16,7 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/sessbench
Home: https://github.com/gorhill/uBlock
*/
/* jshint boss: true */
@ -32,9 +32,9 @@
// Adjust top padding of content table, to match that of toolbar height.
document.getElementById('content').style.setProperty(
uDom.nodeFromId('content').style.setProperty(
'margin-top',
document.getElementById('toolbar').offsetHeight + 'px'
uDom.nodeFromId('toolbar').offsetHeight + 'px'
);
/******************************************************************************/
@ -51,7 +51,8 @@ var allTabIds = {};
var allTabIdsToken;
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
var reRFC3986 = /^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#]*)(\?[^#]*)?(#.*)?/;
var urlFilteringMenu = document.querySelector('#urlFilteringMenu');
var netFilteringDialog = uDom.nodeFromId('netFilteringDialog');
var filterFinderDialog = uDom.nodeFromId('filterFinderDialog');
var prettyRequestTypes = {
'main_frame': 'doc',
@ -159,19 +160,19 @@ var filterDecompiler = (function() {
var opts = [];
var vfields = compiled.split('\v');
var filter = '';
var bits = parseInt(vfields[1], 16) | 0;
var bits = parseInt(vfields[0], 16) | 0;
if ( bits & 0x01 ) {
filter += '@@';
}
var fid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = fid !== '.' ? vfields[4].split('\t') : [];
var fid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = fid !== '.' ? vfields[3].split('\t') : [];
var tfield0 = tfields[0];
switch ( fid ) {
case '.':
filter += '||' + vfields[3] + '^';
filter += '||' + vfields[2] + '^';
break;
case 'a':
case 'ah':
@ -258,14 +259,13 @@ var filterDecompiler = (function() {
var toRegex = function(compiled) {
var vfields = compiled.split('\v');
var fid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = fid !== '.' ? vfields[4].split('\t') : [];
var fid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = fid !== '.' ? vfields[3].split('\t') : [];
var reStr;
switch ( fid ) {
case '.':
reStr = vfields[3]
.replace(reEscape, '\\$&');
reStr = vfields[2].replace(reEscape, '\\$&');
break;
case 'a':
case 'ah':
@ -343,6 +343,7 @@ var createRow = function(layout) {
tr.className = '';
tr.removeAttribute('data-hn-page');
tr.removeAttribute('data-hn-frame');
tr.removeAttribute('data-filter');
} else {
tr = document.createElement('tr');
}
@ -431,6 +432,7 @@ var renderNetLogEntry = function(tr, entry) {
filter = filter.slice(3);
if ( filteringType === 's' ) {
td.textContent = filterDecompiler.toString(filter);
tr.setAttribute('data-filter', filter);
} else {
td.textContent = filter;
}
@ -587,7 +589,7 @@ var synchronizeTabIds = function(newTabIds) {
}
}
var select = document.getElementById('pageSelector');
var select = uDom.nodeFromId('pageSelector');
var selectValue = select.value;
var tabIds = Object.keys(newTabIds).sort(function(a, b) {
return newTabIds[a].localeCompare(newTabIds[b]);
@ -692,8 +694,8 @@ var readLogBuffer = function() {
/******************************************************************************/
var pageSelectorChanged = function() {
var style = document.getElementById('tabFilterer');
var tabClass = document.getElementById('pageSelector').value;
var style = uDom.nodeFromId('tabFilterer');
var tabClass = uDom.nodeFromId('pageSelector').value;
var sheet = style.sheet;
while ( sheet.cssRules.length !== 0 ) {
sheet.deleteRule(0);
@ -713,7 +715,7 @@ var pageSelectorChanged = function() {
/******************************************************************************/
var reloadTab = function() {
var tabClass = document.getElementById('pageSelector').value;
var tabClass = uDom.nodeFromId('pageSelector').value;
var tabId = tabIdFromClassName(tabClass);
if ( tabId === 'bts' || tabId === '' ) {
return;
@ -746,7 +748,7 @@ var onMaxEntriesChanged = function() {
/******************************************************************************/
/******************************************************************************/
var filteringDialog = (function() {
var netFilteringManager = (function() {
var targetRow = null;
var dialog = null;
var createdStaticFilters = {};
@ -856,8 +858,8 @@ var filteringDialog = (function() {
var onClick = function(ev) {
var target = ev.target;
// click outside the url filtering menu
if ( target.id === 'urlFilteringMenu' ) {
// click outside the dialog proper
if ( target.classList.contains('modalDialog') ) {
toggleOff();
return;
}
@ -1217,14 +1219,14 @@ var filteringDialog = (function() {
createPreview(targetType, targetURLs[0]);
fillDynamicPane();
fillStaticPane();
document.body.appendChild(urlFilteringMenu);
urlFilteringMenu.addEventListener('click', onClick, true);
urlFilteringMenu.addEventListener('change', onSelectChange, true);
urlFilteringMenu.addEventListener('input', onInputChange, true);
document.body.appendChild(netFilteringDialog);
netFilteringDialog.addEventListener('click', onClick, true);
netFilteringDialog.addEventListener('change', onSelectChange, true);
netFilteringDialog.addEventListener('input', onInputChange, true);
};
var toggleOn = function(ev) {
dialog = urlFilteringMenu.querySelector('.dialog');
dialog = netFilteringDialog.querySelector('.dialog');
targetRow = ev.target.parentElement;
targetTabId = tabIdFromClassName(targetRow.className);
targetType = targetRow.cells[4].textContent.trim() || '';
@ -1245,10 +1247,104 @@ var filteringDialog = (function() {
dialog = null;
targetRow = null;
targetURLs = [];
urlFilteringMenu.removeEventListener('click', onClick, true);
urlFilteringMenu.removeEventListener('change', onSelectChange, true);
urlFilteringMenu.removeEventListener('input', onInputChange, true);
document.body.removeChild(urlFilteringMenu);
netFilteringDialog.removeEventListener('click', onClick, true);
netFilteringDialog.removeEventListener('change', onSelectChange, true);
netFilteringDialog.removeEventListener('input', onInputChange, true);
document.body.removeChild(netFilteringDialog);
};
return {
toggleOn: toggleOn
};
})();
/******************************************************************************/
/******************************************************************************/
var reverseLookupManager = (function() {
var rawFilter = '';
var reSentence1 = /\{\{filter\}\}/g;
var sentence1Template = vAPI.i18n('loggerStaticFilteringFinderSentence1');
var removeAllChildren = function(node) {
while ( node.firstChild ) {
node.removeChild(node.firstChild);
}
};
var onClick = function(ev) {
var target = ev.target;
// click outside the dialog proper
if ( target.classList.contains('modalDialog') ) {
toggleOff();
return;
}
ev.stopPropagation();
};
var reverseLookupDone = function(response) {
var lists = response.matches;
if ( Array.isArray(lists) === false ) {
return;
}
var dialog = filterFinderDialog.querySelector('.dialog');
var p = dialog.querySelector('p');
removeAllChildren(p);
var node;
reSentence1.lastIndex = 0;
var matches = reSentence1.exec(sentence1Template);
if ( matches === null ) {
node = document.createTextNode(sentence1Template);
} else {
node = uDom.nodeFromSelector('#filterFinderDialogSentence1 > span').cloneNode(true);
node.childNodes[0].textContent = sentence1Template.slice(0, matches.index);
node.childNodes[1].textContent = rawFilter;
node.childNodes[2].textContent = sentence1Template.slice(reSentence1.lastIndex);
}
p.appendChild(node);
var ul = dialog.querySelector('ul');
removeAllChildren(ul);
var list, li;
for ( var i = 0; i < lists.length; i++ ) {
list = lists[i];
li = document.createElement('li');
if ( list.supportURL ) {
node = document.createElement('a');
node.textContent = list.title;
node.setAttribute('href', list.supportURL);
node.setAttribute('target', '_blank');
} else {
node = document.createTextNode(list.title);
}
li.appendChild(node);
ul.appendChild(li);
}
document.body.appendChild(filterFinderDialog);
filterFinderDialog.addEventListener('click', onClick, true);
};
var toggleOn = function(ev) {
var row = ev.target.parentElement;
var filter = row.getAttribute('data-filter') || '';
if ( filter === '' ) {
return;
}
rawFilter = row.cells[2].textContent;
messager.send({
what: 'reverseLookupFilter',
filter: filter
}, reverseLookupDone);
};
var toggleOff = function() {
filterFinderDialog.removeEventListener('click', onClick, true);
document.body.removeChild(filterFinderDialog);
};
return {
@ -1489,7 +1585,7 @@ var popupManager = (function() {
realTabId = noTabId;
}
container = document.getElementById('popupContainer');
container = uDom.nodeFromId('popupContainer');
container.querySelector('div > span:nth-of-type(1)').addEventListener('click', toggleSize);
container.querySelector('div > span:nth-of-type(2)').addEventListener('click', toggleOff);
@ -1500,7 +1596,7 @@ var popupManager = (function() {
popupObserver = new MutationObserver(resizePopup);
container.appendChild(popup);
style = document.getElementById('popupFilterer');
style = uDom.nodeFromId('popupFilterer');
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
document.body.classList.add('popupOn');
@ -1559,7 +1655,8 @@ uDom.onLoad(function() {
uDom('#clear').on('click', clearBuffer);
uDom('#maxEntries').on('change', onMaxEntriesChanged);
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
uDom('#content').on('click', 'tr.cat_net > td:nth-of-type(4)', filteringDialog.toggleOn);
uDom('#content').on('click', 'tr.cat_net > td:nth-of-type(4)', netFilteringManager.toggleOn);
uDom('#content').on('click', 'tr[data-filter] > td:nth-of-type(3)', reverseLookupManager.toggleOn);
});
/******************************************************************************/

View file

@ -66,6 +66,10 @@ var onMessage = function(request, sender, callback) {
µb.reloadAllFilters(callback);
return;
case 'reverseLookupFilter':
µb.staticFilteringReverseLookup.lookup(request.filter, callback);
return;
default:
break;
}

View file

@ -0,0 +1,79 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
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/}.
Home: https://github.com/gorhill/uBlock
*/
/* global onmessage, postMessage */
'use strict';
/******************************************************************************/
var listEntries = Object.create(null);
/******************************************************************************/
var lookup = function(details) {
var matches = [];
var entry, pos;
for ( var path in listEntries ) {
entry = listEntries[path];
if ( entry === undefined ) {
continue;
}
pos = entry.content.indexOf(details.filter);
if ( pos === -1 ) {
continue;
}
matches.push({
title: entry.title,
supportURL: entry.supportURL
});
}
postMessage({
id: details.id,
response: {
filter: details.filter,
matches: matches
}
});
};
/******************************************************************************/
onmessage = function(e) {
var msg = e.data;
switch ( msg.what ) {
case 'resetLists':
listEntries = Object.create(null);
break;
case 'setList':
listEntries[msg.details.path] = msg.details;
break;
case 'reverseLookup':
lookup(msg);
break;
}
};
/******************************************************************************/

177
src/js/reverselookup.js Normal file
View file

@ -0,0 +1,177 @@
/*******************************************************************************
uBlock - a browser extension to block requests.
Copyright (C) 2015 Raymond Hill
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/}.
Home: https://github.com/gorhill/uBlock
*/
/* global µBlock */
/******************************************************************************/
µBlock.staticFilteringReverseLookup = (function() {
'use strict';
/******************************************************************************/
var worker = null;
var workerTTL = 11 * 60 * 1000;
var workerTTLTimer = null;
var needLists = true;
var messageId = 1;
var pendingResponses = Object.create(null);
/******************************************************************************/
var onWorkerMessage = function(e) {
var msg = e.data;
var callback = pendingResponses[msg.id];
delete pendingResponses[msg.id];
callback(msg.response);
};
/******************************************************************************/
var stopWorker = function() {
workerTTLTimer = null;
if ( worker === null ) {
return;
}
worker.terminate();
worker = null;
needLists = true;
pendingResponses = Object.create(null);
};
/******************************************************************************/
var initWorker = function(callback) {
if ( worker === null ) {
worker = new Worker('js/reverselookup-worker.js');
worker.onmessage = onWorkerMessage;
}
if ( needLists === false ) {
callback();
return;
}
needLists = false;
var entries = Object.create(null);
var countdown = 0;
var path, entry;
var onListLoaded = function(details) {
var entry = entries[details.path];
worker.postMessage({
what: 'setList',
details: {
path: details.path,
title: entry.title || '',
supportURL: entry.supportURL,
content: details.content
}
});
countdown -= 1;
if ( countdown === 0 ) {
callback();
}
};
var µb = µBlock;
for ( path in µb.remoteBlacklists ) {
if ( µb.remoteBlacklists.hasOwnProperty(path) === false ) {
continue;
}
entry = µb.remoteBlacklists[path];
if ( entry.off === true ) {
continue;
}
entries[path] = {
title: entry.title,
supportURL: entry.supportURL || ''
};
countdown += 1;
}
if ( countdown === 0 ) {
callback();
return;
}
for ( path in entries ) {
µb.getCompiledFilterList(path, onListLoaded);
}
};
/******************************************************************************/
var lookup = function(compiledFilter, callback) {
if ( typeof callback !== 'function' ) {
return;
}
if ( workerTTLTimer !== null ) {
clearTimeout(workerTTLTimer);
workerTTLTimer = null;
}
var onWorkerReady = function() {
var id = messageId++;
var message = {
what: 'reverseLookup',
id: id,
filter: compiledFilter
};
pendingResponses[id] = callback;
worker.postMessage(message);
// The worker will be shutdown after n minutes without being used.
workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL);
};
initWorker(onWorkerReady);
};
/******************************************************************************/
// This tells the worker that filter lists may have changed.
var resetLists = function() {
needLists = true;
if ( worker === null ) {
return;
}
worker.postMessage({ what: 'resetLists' });
};
/******************************************************************************/
return {
lookup: lookup,
resetLists: resetLists,
shutdown: stopWorker
};
/******************************************************************************/
})();
/******************************************************************************/

View file

@ -37,6 +37,18 @@ var µb = µBlock;
/******************************************************************************/
vAPI.app.onShutdown = function() {
µb.staticFilteringReverseLookup.shutdown();
µb.staticNetFilteringEngine.reset();
µb.sessionFirewall.reset();
µb.permanentFirewall.reset();
µb.permanentFirewall.reset();
µb.sessionURLFiltering.reset();
µb.permanentURLFiltering.reset();
};
/******************************************************************************/
// Final initialization steps after all needed assets are in memory.
// - Initialize internal state with maybe already existing tabs.
// - Schedule next update operation.

View file

@ -160,11 +160,6 @@ histogram = function(label, categories) {
// Local helpers
// Could be replaced with encodeURIComponent/decodeURIComponent,
// which seems faster on Firefox.
var encode = JSON.stringify;
var decode = JSON.parse;
var cachedParseInt = parseInt;
var atoi = function(s) {
@ -1651,12 +1646,10 @@ FilterContainer.prototype.toSelfie = function() {
var categoryToSelfie = function(dict) {
var selfie = [];
var bucket, ff, n, i, f;
for ( var k in dict ) {
for ( var token in dict ) {
// No need for hasOwnProperty() here: there is no prototype chain.
// We need to encode the key because there could be a `\n` or '\t'
// character in it, which would trip the code at parse time.
selfie.push('k2\t' + encode(k));
bucket = dict[k];
selfie.push('k2\t' + token);
bucket = dict[token];
selfie.push(bucket.fid + '\t' + bucket.toSelfie());
if ( bucket.fid !== '[]' ) {
continue;
@ -1673,12 +1666,10 @@ FilterContainer.prototype.toSelfie = function() {
var categoriesToSelfie = function(dict) {
var selfie = [];
for ( var k in dict ) {
for ( var key in dict ) {
// No need for hasOwnProperty() here: there is no prototype chain.
// We need to encode the key because there could be a `\n` or '\t'
// character in it, which would trip the code at parse time.
selfie.push('k1\t' + encode(k));
selfie.push(categoryToSelfie(dict[k]));
selfie.push('k1\t' + key);
selfie.push(categoryToSelfie(dict[key]));
}
return selfie.join('\n');
};
@ -1722,13 +1713,13 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
pos = line.indexOf('\t');
what = line.slice(0, pos);
if ( what === 'k1' ) {
catKey = decode(line.slice(pos + 1));
catKey = line.slice(pos + 1);
subdict = dict[catKey] = Object.create(null);
bucket = null;
continue;
}
if ( what === 'k2' ) {
tokenKey = decode(line.slice(pos + 1));
tokenKey = line.slice(pos + 1);
bucket = null;
continue;
}
@ -2015,18 +2006,18 @@ FilterContainer.prototype.filterStringFromCompiled = function(compiled) {
var opts = [];
var vfields = compiled.split('\v');
var filter = '';
var bits = parseInt(vfields[1], 16) | 0;
var bits = parseInt(vfields[0], 16) | 0;
if ( bits & 0x01 ) {
filter += '@@';
}
var rfid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = rfid !== '.' ? vfields[4].split('\t') : [];
var rfid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = rfid !== '.' ? vfields[3].split('\t') : [];
switch ( rfid ) {
case '.':
filter += '||' + vfields[3] + '^';
filter += '||' + vfields[2] + '^';
break;
case 'a':
case 'ah':
@ -2102,13 +2093,13 @@ FilterContainer.prototype.filterStringFromCompiled = function(compiled) {
FilterContainer.prototype.filterRegexFromCompiled = function(compiled, flags) {
var vfields = compiled.split('\v');
var rfid = vfields[2] === '.' ? '.' : vfields[3];
var tfields = rfid !== '.' ? vfields[4].split('\t') : [];
var rfid = vfields[1] === '.' ? '.' : vfields[2];
var tfields = rfid !== '.' ? vfields[3].split('\t') : [];
var re = null;
switch ( rfid ) {
case '.':
re = strToRegex(vfields[3], 0, flags);
re = strToRegex(vfields[2], 0, flags);
break;
case 'a':
case 'ah':
@ -2476,7 +2467,7 @@ FilterContainer.prototype.toResultString = function(verbose) {
if ( !verbose ) {
return s;
}
s += 'n\v' + this.keyRegister + '\v' + this.tokenRegister + '\v';
s += this.keyRegister + '\v' + this.tokenRegister + '\v';
if ( this.tokenRegister === '.' ) {
s += this.fRegister.rtCompile();
} else {

View file

@ -400,6 +400,7 @@
µb.cosmeticFilteringEngine.reset();
µb.staticNetFilteringEngine.reset();
µb.destroySelfie();
µb.staticFilteringReverseLookup.resetLists();
// We need to build a complete list of assets to pull first: this is
// because it *may* happens that some load operations are synchronous:

View file

@ -41,7 +41,7 @@
<div id="templates" style="display: none;">
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
<div id="hiddenTemplate"><span style="display:none;"></span></div>
<div id="urlFilteringMenu">
<div id="netFilteringDialog" class="modalDialog">
<div class="dialog">
<div class="hide preview"></div>
<div class="headers">
@ -79,6 +79,13 @@
</div>
</div>
</div>
<div id="filterFinderDialog" class="modalDialog">
<div class="dialog">
<p></p>
<ul></ul>
</div>
</div>
<div id="filterFinderDialogSentence1"><span><span></span><code></code><span></span></span></div>
</div>
<script src="js/vapi-common.js"></script>