From 060a43fe818ed55565300db4fa1cfc789c54726a Mon Sep 17 00:00:00 2001 From: gorhill Date: Thu, 11 Jun 2015 12:12:23 -0400 Subject: [PATCH] this addresses half of #58: find list(s) from which a static network filter originates --- src/_locales/en/messages.json | 4 + src/background.html | 1 + src/css/logger-ui.css | 126 +++++++++++++---------- src/js/logger-ui.js | 161 ++++++++++++++++++++++++------ src/js/messaging.js | 4 + src/js/reverselookup-worker.js | 79 +++++++++++++++ src/js/reverselookup.js | 177 +++++++++++++++++++++++++++++++++ src/js/start.js | 12 +++ src/js/static-net-filtering.js | 41 +++----- src/js/storage.js | 1 + src/logger-ui.html | 9 +- 11 files changed, 505 insertions(+), 110 deletions(-) create mode 100644 src/js/reverselookup-worker.js create mode 100644 src/js/reverselookup.js diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index abb08357d..112ab8449 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -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" diff --git a/src/background.html b/src/background.html index b3d0820f3..5852880a9 100644 --- a/src/background.html +++ b/src/background.html @@ -29,6 +29,7 @@ + diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index 1980d6396..572fadd26 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -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; } diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 0c7c7d5c3..dd239eb27 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -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); }); /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 174a965c4..2c0d9649c 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -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; } diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js new file mode 100644 index 000000000..f484633f9 --- /dev/null +++ b/src/js/reverselookup-worker.js @@ -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; + } +}; + +/******************************************************************************/ diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js new file mode 100644 index 000000000..e59ef3811 --- /dev/null +++ b/src/js/reverselookup.js @@ -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 +}; + +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/start.js b/src/js/start.js index d148cca21..663ee1a3f 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -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. diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 84cee8030..fa8c945a5 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -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 { diff --git a/src/js/storage.js b/src/js/storage.js index f9e7a0541..172cc664d 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -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: diff --git a/src/logger-ui.html b/src/logger-ui.html index 0e9246112..a32392628 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -41,7 +41,7 @@