logger: ability to create static net filters

This commit is contained in:
gorhill 2015-06-06 18:31:38 -04:00
parent a364a869f5
commit 48f47a6849
8 changed files with 596 additions and 239 deletions

View File

@ -415,6 +415,42 @@
"message":"Dynamic URL filtering", "message":"Dynamic URL filtering",
"description":"Small header to identify the dynamic URL filtering section" "description":"Small header to identify the dynamic URL filtering section"
}, },
"loggerStaticFilteringHeader":{
"message":"Static filtering",
"description":"Small header to identify the static filtering section"
},
"loggerStaticFilteringSentence":{
"message":"{{action}} network requests of {{type}} {{br}}which URL address matches {{url}} {{br}}and which originates from {{origin}},{{br}}{{importance}} there is an exception filter.",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartBlock":{
"message":"Block",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartAllow":{
"message":"Allow",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartType":{
"message":"type “{{type}}”",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartAnyType":{
"message":"any type",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartAnyOrigin":{
"message":"anywhere",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartNotImportant":{
"message":"except when",
"description":"Used in the static filtering wizard"
},
"loggerStaticFilteringSentencePartImportant":{
"message":"even if",
"description":"Used in the static filtering wizard"
},
"aboutChangelog":{ "aboutChangelog":{
"message":"Change log", "message":"Change log",
"description":"English: Change log" "description":"English: Change log"

View File

@ -17,6 +17,28 @@ body {
color: black; color: black;
font: 14px/1.3 sans-serif; font: 14px/1.3 sans-serif;
} }
button.important {
padding: 5px;
border: 1px solid transparent;
border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
border-radius: 3px;
background-color: hsl(36, 100%, 75%);
background-image: linear-gradient(#ffdca8, #ffcc7f);
background-repeat: repeat-x;
color: #222;
opacity: 0.8;
}
button.important[disabled],
button.important.disabled {
border-color: #dddddd #dddddd hsl(36, 0%, 85%);
background-color: hsl(36, 0%, 72%);
background-image: linear-gradient(#f2f2f2, #dddddd);
color: #888;
pointer-events: none;
}
button.important:hover {
opacity: 1.0;
}
.hiddenFileInput { .hiddenFileInput {
height: 0; height: 0;
visibility: hidden; visibility: hidden;

View File

@ -25,29 +25,6 @@ div > p:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
button.important {
padding: 5px;
border: 1px solid transparent;
border-color: #ffcc7f #ffcc7f hsl(36, 100%, 73%);
border-radius: 3px;
background-color: hsl(36, 100%, 75%);
background-image: linear-gradient(#ffdca8, #ffcc7f);
background-repeat: repeat-x;
color: #222;
opacity: 0.8;
}
button.important[disabled],
button.important.disabled {
border-color: #dddddd #dddddd hsl(36, 0%, 85%);
background-color: hsl(36, 0%, 72%);
background-image: linear-gradient(#f2f2f2, #dddddd);
color: #888;
pointer-events: none;
}
button.important:hover {
opacity: 1.0;
}
.para { .para {
width: 40em; width: 40em;
} }

View File

@ -286,6 +286,7 @@ body[dir="rtl"] #popupContainer > div {
top: 0; top: 0;
z-index: 400; z-index: 400;
} }
#urlFilteringMenu .dialog { #urlFilteringMenu .dialog {
background-color: white; background-color: white;
border: 2px solid white; border: 2px solid white;
@ -296,24 +297,23 @@ body[dir="rtl"] #popupContainer > div {
transform-style: preserve-3d; transform-style: preserve-3d;
width: 80%; width: 80%;
} }
#urlFilteringMenu .dialog table {
border: 0; #urlFilteringMenu .dialog p {
border-collapse: collapse; line-height: 2em;
table-layout: fixed;
width: 100%;
}
#urlFilteringMenu .dialog table > colgroup > col:nth-of-type(1) {
width: 3.8em;
}
#urlFilteringMenu .dialog table > colgroup > col:nth-of-type(2) {
} }
#urlFilteringMenu .dialog td { #urlFilteringMenu .dialog select {
border: 0; appearance: none;
padding: 0; -webkit-appearance: none;
vertical-align: middle; -moz-appearance: none;
} background-color: #f4f4f4;
#urlFilteringMenu .dialog > table.toolbar td.preview { border: 1px solid #ddd;
max-width: 75%;
outline: none;
padding: 0.2em;
}
#urlFilteringMenu .dialog > div.preview {
/* http://lea.verou.me/css3patterns/ */ /* http://lea.verou.me/css3patterns/ */
background-color: #aaa; background-color: #aaa;
background-image: background-image:
@ -337,20 +337,83 @@ body[dir="rtl"] #popupContainer > div {
background-size: 18px 18px; background-size: 18px 18px;
text-align: center; text-align: center;
} }
#urlFilteringMenu .dialog > table.toolbar td.preview > * { #urlFilteringMenu .dialog > div.preview > * {
max-width: 100%; max-width: 100%;
max-height: 40vh; max-height: 40vh;
} }
#urlFilteringMenu .dialog > table.toolbar select {
font: 14px; #urlFilteringMenu .dialog table {
height: 2em; border: 0;
border-collapse: collapse;
table-layout: fixed;
width: 100%;
} }
#urlFilteringMenu .dialog > table.toolbar .fa { #urlFilteringMenu .dialog table > colgroup > col:nth-of-type(1) {
width: 3.8em;
}
#urlFilteringMenu .dialog table > colgroup > col:nth-of-type(2) {
}
#urlFilteringMenu .dialog td {
border: 0;
padding: 0;
vertical-align: middle;
}
#urlFilteringMenu .dialog > div.headers {
border-bottom: 1px solid #888;
position: relative;
}
#urlFilteringMenu .dialog > div.headers > span.header {
background-color: #eee;
border: 1px solid #aaa;
border-bottom: 1px solid #888;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
color: #888;
cursor: pointer;
display: inline-block;
font-size: small;
line-height: 2em;
margin-left: 0.5em;
padding: 0 1em;
position: relative;
text-align: center;
top: 1px;
}
#urlFilteringMenu .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 {
display: inline-block;
position: absolute;
right: 0;
top: 50%;
transform: translate(0, -50%);
}
#urlFilteringMenu .dialog > div.headers > span.tools > span {
cursor: pointer; cursor: pointer;
font-size: 1.2em; font-size: 1.2em;
text-align: center; text-align: center;
} }
#urlFilteringMenu .dialog > table.toolbar .fa.save { #urlFilteringMenu .dialog > div.containers {
height: 40vh;
overflow: hidden;
overflow-y: auto;
}
#urlFilteringMenu .dialog > div.containers > div {
display: none;
}
#urlFilteringMenu .dialog > div.containers > div.selected {
display: block;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar select {
font: 14px;
height: 2em;
}
#urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
background-color: #ffe; background-color: #ffe;
border: 1px solid #ddc; border: 1px solid #ddc;
border-radius: 4px; border-radius: 4px;
@ -360,49 +423,37 @@ body[dir="rtl"] #popupContainer > div {
padding: 0.25em 0.5em; padding: 0.25em 0.5em;
visibility: hidden; visibility: hidden;
} }
body.dirty #urlFilteringMenu .dialog > table.toolbar .fa.save { body.dirty #urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules {
visibility: visible; visibility: visible;
} }
#urlFilteringMenu .dialog > table.toolbar .fa.save:hover { #urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar #saveRules:hover {
color: black; color: black;
} }
#urlFilteringMenu .dialog > table.toolbar tr.entry { #urlFilteringMenu .dialog > div.containers > div.dynamic > table.toolbar tr.entry {
display: none; display: none;
} }
#urlFilteringMenu .dialog > div.entries { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry {
max-height: 30vh;
overflow: hidden;
overflow-y: auto;
}
#urlFilteringMenu .dialog > div.header {
background-color: #666;
color: white;
font-size: smaller;
padding: 2px;
text-align: center;
}
#urlFilteringMenu .dialog > div.entries tr.entry {
background-color: #e6e6e6; background-color: #e6e6e6;
border: 0; border: 0;
border-bottom: 1px solid white; border-bottom: 1px solid white;
font-size: 13px; font-size: 13px;
} }
#urlFilteringMenu .dialog > div.entries tr.entry:hover { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry:hover {
background-color: #f0f0f0; background-color: #f0f0f0;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td:first-of-type { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td:first-of-type {
border: 0; border: 0;
border-right: 1px solid white; border-right: 1px solid white;
text-align: center; text-align: center;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action {
background-color: transparent; background-color: transparent;
border: 0; border: 0;
cursor: pointer; cursor: pointer;
height: 2em; height: 2em;
width: 100%; width: 100%;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span {
background-color: transparent; background-color: transparent;
border: 0; border: 0;
display: inline-block; display: inline-block;
@ -411,67 +462,79 @@ body.dirty #urlFilteringMenu .dialog > table.toolbar .fa.save {
visibility: hidden; visibility: hidden;
width: 33.33%; width: 33.33%;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.allow { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
background-color: rgba(0, 160, 0, 0.3); background-color: rgba(0, 160, 0, 0.3);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.allow { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.allow {
background-color: rgba(255, 194, 57, 0.4); background-color: rgba(255, 194, 57, 0.4);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.noop { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
background-color: rgba(108, 108, 108, 0.3); background-color: rgba(108, 108, 108, 0.3);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.noop { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.noop {
background-color: rgba(96, 96, 96, 0.4); background-color: rgba(96, 96, 96, 0.4);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.block { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
background-color: rgba(192, 0, 0, 0.3); background-color: rgba(192, 0, 0, 0.3);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.block { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.block {
background-color: rgba(0, 19, 110, 0.4); background-color: rgba(0, 19, 110, 0.4);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.own.allow { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
background-color: rgba(0, 160, 0, 1); background-color: rgba(0, 160, 0, 1);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.own.allow { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.allow {
background-color: rgba(255, 194, 57, 1); background-color: rgba(255, 194, 57, 1);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.own.noop { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.noop {
background-color: rgba(108, 108, 108, 1); background-color: rgba(108, 108, 108, 1);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.own.block { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
background-color: rgba(192, 0, 0, 1); background-color: rgba(192, 0, 0, 1);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action.own.block { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action.own.block {
background-color: rgba(0, 19, 110, 1); background-color: rgba(0, 19, 110, 1);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action:not(.own):hover > span { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span {
opacity: 0.2; opacity: 0.2;
visibility: visible; visibility: visible;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action:not(.own):hover > span:hover { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action:not(.own):hover > span:hover {
opacity: 0.75; opacity: 0.75;
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span.allow { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
background-color: rgb(0, 160, 0); background-color: rgb(0, 160, 0);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span.allow { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.allow {
background-color: rgb(255, 194, 57); background-color: rgb(255, 194, 57);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span.noop { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.noop {
background-color: rgb(108, 108, 108); background-color: rgb(108, 108, 108);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span.block { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
background-color: rgb(192, 0, 0); background-color: rgb(192, 0, 0);
} }
body.colorBlind #urlFilteringMenu .dialog > div.entries tr.entry > td > div.action > span.block { body.colorBlind #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td > div.action > span.block {
background-color: rgb(0, 19, 110); background-color: rgb(0, 19, 110);
} }
#urlFilteringMenu .dialog > div.entries tr.entry > td.url { #urlFilteringMenu .dialog > div.containers > div.dynamic tr.entry > td.url {
overflow: hidden; overflow: hidden;
padding-left: 4px; padding-left: 4px;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
#urlFilteringMenu .dialog > div.containers > div.static > p {
margin: 0.75em 0;
}
#urlFilteringMenu .dialog > div.containers > div.static textarea {
box-sizing: border-box;
height: 6em;
resize: none;
width: 100%;
}
#urlFilteringMenu .dialog > div.containers > div.static > p:nth-of-type(2) {
text-align: center;
}
.hide { .hide {
display: none; display: none;
} }

View File

@ -197,8 +197,8 @@ var createRow = function(layout) {
var tr = trJunkyard.pop(); var tr = trJunkyard.pop();
if ( tr ) { if ( tr ) {
tr.className = ''; tr.className = '';
tr.removeAttribute('data-context'); tr.removeAttribute('data-hn-page');
tr.removeAttribute('data-frame'); tr.removeAttribute('data-hn-frame');
} else { } else {
tr = document.createElement('tr'); tr = document.createElement('tr');
} }
@ -270,10 +270,10 @@ var renderNetLogEntry = function(tr, entry) {
// Contexts // Contexts
if ( entry.d3 ) { if ( entry.d3 ) {
tr.setAttribute('data-context', entry.d3); tr.setAttribute('data-hn-page', entry.d3);
} }
if ( entry.d4 ) { if ( entry.d4 ) {
tr.setAttribute('data-frame', entry.d4); tr.setAttribute('data-hn-frame', entry.d4);
} }
// Cosmetic filter? // Cosmetic filter?
@ -590,12 +590,19 @@ var onMaxEntriesChanged = function() {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
var urlFilteringDialog = (function() { var filteringDialog = (function() {
var targetRow = null;
var dialog = null; var dialog = null;
var selectContext = null; var createdStaticFilters = {};
var selectType = null;
var targetType;
var targetURLs = []; var targetURLs = [];
var tabId = ''; var targetFrameHostname;
var targetPageHostname;
var targetTabId;
var targetDomain;
var targetPageDomain;
var targetFrameDomain;
var removeAllChildren = function(node) { var removeAllChildren = function(node) {
while ( node.firstChild ) { while ( node.firstChild ) {
@ -603,11 +610,23 @@ var urlFilteringDialog = (function() {
} }
}; };
var uglyTypeFromSelector = function() { var uglyTypeFromSelector = function(pane) {
var prettyType = selectType.value; var prettyType = selectValue('select.type.' + pane);
return uglyRequestTypes[prettyType] || prettyType; return uglyRequestTypes[prettyType] || prettyType;
}; };
var selectNode = function(selector) {
return dialog.querySelector(selector);
};
var selectValue = function(selector) {
return selectNode(selector).value || '';
};
var staticFilterNode = function() {
return dialog.querySelector('div.containers > div.static textarea');
};
var onColorsReady = function(response) { var onColorsReady = function(response) {
document.body.classList.toggle('dirty', response.dirty); document.body.classList.toggle('dirty', response.dirty);
var colorEntries = response.colors; var colorEntries = response.colors;
@ -617,7 +636,7 @@ var urlFilteringDialog = (function() {
continue; continue;
} }
colorEntry = colorEntries[url]; colorEntry = colorEntries[url];
node = urlFilteringMenu.querySelector('.entry .action[data-url="' + url + '"]'); node = dialog.querySelector('.dynamic .entry .action[data-url="' + url + '"]');
if ( node === null ) { if ( node === null ) {
continue; continue;
} }
@ -631,12 +650,54 @@ var urlFilteringDialog = (function() {
var colorize = function() { var colorize = function() {
messager.send({ messager.send({
what: 'getURLFilteringData', what: 'getURLFilteringData',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
urls: targetURLs, urls: targetURLs,
type: uglyTypeFromSelector() type: uglyTypeFromSelector('dynamic')
}, onColorsReady); }, onColorsReady);
}; };
var parseStaticInputs = function() {
var filter = '';
var value;
value = selectValue('select.static.action');
if ( value !== '' ) {
filter = '@@';
}
value = selectValue('select.static.url');
if ( value !== '' ) {
filter += '||' + value;
}
var options = [];
if ( selectValue('select.static.importance') !== '' ) {
options.push('important');
}
value = selectValue('select.static.type');
if ( value !== '' ) {
options.push(uglyTypeFromSelector('static'));
}
value = selectValue('select.static.origin');
if ( value !== '' ) {
if ( value === targetDomain ) {
options.push('first-party');
} else {
options.push('domain=' + value);
}
}
if ( options.length ) {
filter += '$' + options.join(',');
}
staticFilterNode().value = filter;
updateWidgets();
};
var updateWidgets = function() {
var value = staticFilterNode().value;
dialog.querySelector('#createStaticFilter').classList.toggle(
'disabled',
createdStaticFilters.hasOwnProperty(value) || value === ''
);
};
var onClick = function(ev) { var onClick = function(ev) {
var target = ev.target; var target = ev.target;
@ -649,14 +710,47 @@ var urlFilteringDialog = (function() {
ev.stopPropagation(); ev.stopPropagation();
var tcl = target.classList; var tcl = target.classList;
var value;
// Select a mode
if ( tcl.contains('header') ) {
if ( tcl.contains('selected') ) {
return;
}
uDom('.header').removeClass('selected');
uDom('.container').removeClass('selected');
value = target.getAttribute('data-container');
uDom('.header.' + value).addClass('selected');
uDom('.container.' + value).addClass('selected');
return;
}
// Create static filter
if ( target.id === 'createStaticFilter' ) {
value = staticFilterNode().value;
// Avoid duplicates
if ( createdStaticFilters.hasOwnProperty(value) ) {
return;
}
createdStaticFilters[value] = true;
if ( value !== '' ) {
var d = new Date();
messager.send({
what: 'createUserFilter',
filters: '! ' + d.toLocaleString() + ' ' + targetPageDomain + '\n' + value
});
}
updateWidgets();
return;
}
// Save url filtering rule(s) // Save url filtering rule(s)
if ( tcl.contains('save') ) { if ( target.id === 'saveRules' ) {
messager.send({ messager.send({
what: 'saveURLFilteringRules', what: 'saveURLFilteringRules',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
urls: targetURLs, urls: targetURLs,
type: uglyTypeFromSelector() type: uglyTypeFromSelector('dynamic')
}, colorize); }, colorize);
return; return;
} }
@ -667,9 +761,9 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('action') ) { if ( tcl.contains('action') ) {
messager.send({ messager.send({
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
url: target.getAttribute('data-url'), url: target.getAttribute('data-url'),
type: uglyTypeFromSelector(), type: uglyTypeFromSelector('dynamic'),
action: 0, action: 0,
persist: persist persist: persist
}, colorize); }, colorize);
@ -680,9 +774,9 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('allow') ) { if ( tcl.contains('allow') ) {
messager.send({ messager.send({
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector(), type: uglyTypeFromSelector('dynamic'),
action: 2, action: 2,
persist: persist persist: persist
}, colorize); }, colorize);
@ -693,9 +787,9 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('noop') ) { if ( tcl.contains('noop') ) {
messager.send({ messager.send({
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector(), type: uglyTypeFromSelector('dynamic'),
action: 3, action: 3,
persist: persist persist: persist
}, colorize); }, colorize);
@ -706,9 +800,9 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('block') ) { if ( tcl.contains('block') ) {
messager.send({ messager.send({
what: 'setURLFilteringRule', what: 'setURLFilteringRule',
context: selectContext.value, context: selectValue('select.dynamic.origin'),
url: target.parentNode.getAttribute('data-url'), url: target.parentNode.getAttribute('data-url'),
type: uglyTypeFromSelector(), type: uglyTypeFromSelector('dynamic'),
action: 1, action: 1,
persist: persist persist: persist
}, colorize); }, colorize);
@ -719,7 +813,7 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('reload') ) { if ( tcl.contains('reload') ) {
messager.send({ messager.send({
what: 'reloadTab', what: 'reloadTab',
tabId: tabId tabId: targetTabId
}); });
return; return;
} }
@ -728,7 +822,7 @@ var urlFilteringDialog = (function() {
if ( tcl.contains('picker') ) { if ( tcl.contains('picker') ) {
messager.send({ messager.send({
what: 'launchElementPicker', what: 'launchElementPicker',
tabId: tabId, tabId: targetTabId,
targetURL: 'img\t' + targetURLs[0], targetURL: 'img\t' + targetURLs[0],
select: true select: true
}); });
@ -736,7 +830,34 @@ var urlFilteringDialog = (function() {
} }
}; };
var onSelectChange = function(ev) {
var target = ev.target;
var tcl = target.classList;
if ( tcl.contains('dynamic') ) {
colorize();
return;
}
if ( tcl.contains('static') ) {
parseStaticInputs();
return;
}
};
var onInputChange = function() {
updateWidgets();
};
var createPreview = function(type, url) { var createPreview = function(type, url) {
// First, whether picker can be used
dialog.querySelector('.picker').classList.toggle(
'hide',
targetTabId === noTabId ||
targetType !== 'image' ||
/(?:^| )[dlsu]b(?: |$)/.test(targetRow.className)
);
var preview = null; var preview = null;
if ( type === 'image' ) { if ( type === 'image' ) {
@ -744,9 +865,7 @@ var urlFilteringDialog = (function() {
preview.setAttribute('src', url); preview.setAttribute('src', url);
} }
// More... var container = dialog.querySelector('div.preview');
var container = dialog.querySelector('table.toolbar td.preview');
container.classList.toggle('hide', preview === null); container.classList.toggle('hide', preview === null);
if ( preview === null ) { if ( preview === null ) {
return; return;
@ -754,94 +873,53 @@ var urlFilteringDialog = (function() {
container.appendChild(preview); container.appendChild(preview);
}; };
var toggleOn = function(ev) { // Build list of candidate URLs
if ( dialog !== null ) { var createTargetURLs = function(url) {
return toggleOff(); var urls = [];
var matches = reRFC3986.exec(url);
if ( matches === null || !matches[1] || !matches[2] ) {
return urls;
} }
dialog = urlFilteringMenu.querySelector('.dialog'); // Shortest URL for a valid URL filtering rule
selectContext = dialog.querySelector('.context'); var rootURL = matches[1] + matches[2];
selectType = dialog.querySelector('.type'); urls.unshift(rootURL);
var path = matches[3] || '';
var td = ev.target; var pos = path.charAt(0) === '/' ? 1 : 0;
var tr = td.parentElement; while ( pos < path.length ) {
var cells = tr.cells; pos = path.indexOf('/', pos + 1);
var context = tr.getAttribute('data-context');
if ( !context ) {
return toggleOff();
}
var type = cells[4].textContent.trim();
if ( !type ) {
return toggleOff();
}
tabId = tabIdFromClassName(tr.className);
var pos, option;
// Fill context selector
removeAllChildren(selectContext);
for (;;) {
option = document.createElement('option');
option.textContent = context;
option.setAttribute('value', context);
pos = context.indexOf('.');
selectContext.appendChild(option);
if ( pos === -1 ) { if ( pos === -1 ) {
break; pos = path.length;
} }
context = context.slice(pos + 1); urls.unshift(rootURL + path.slice(0, pos));
} }
option = document.createElement('option'); var query = matches[4] || '';
if ( query !== '') {
urls.unshift(rootURL + path + query);
}
return urls;
};
// Fill dynamic URL filtering pane
var fillDynamicPane = function() {
var select;
// Fill context selector
select = selectNode('select.dynamic.origin');
removeAllChildren(select);
fillOriginSelect(select, targetPageHostname, targetPageDomain);
var option = document.createElement('option');
option.textContent = '*'; option.textContent = '*';
option.setAttribute('value', '*'); option.setAttribute('value', '*');
selectContext.appendChild(option); select.appendChild(option);
// Fill type selector // Fill type selector
selectType.options[0].textContent = type; select = selectNode('select.dynamic.type');
selectType.options[0].setAttribute('value', type); select.options[0].textContent = targetType;
selectType.selectedIndex = 0; select.options[0].setAttribute('value', targetType);
select.selectedIndex = 0;
// Extract data needed to build URL filtering menu // Fill entries
var candidateURL = cells[5].textContent;
var matches = reRFC3986.exec(candidateURL);
if ( matches === null || !matches[1] || !matches[2] ) {
return toggleOff();
}
uDom(dialog).descendants('.picker').toggleClass(
'hide',
tr.classList.contains('tab_bts') ||
type !== 'image' ||
/(?:^| )[dlsu]b(?: |$)/.test(tr.className)
);
// Shortest URL which for a valid URL filtering rule
var candidateRootURL = matches[1] + matches[2];
targetURLs.unshift(candidateRootURL);
var candidatePath = matches[3] || '';
pos = candidatePath.charAt(0) === '/' ? 1 : 0;
while ( pos < candidatePath.length ) {
pos = candidatePath.indexOf('/', pos + 1);
if ( pos === -1 ) {
pos = candidatePath.length;
}
targetURLs.unshift(candidateRootURL + candidatePath.slice(0, pos));
}
var candidateQuery = matches[4] || '';
if ( candidateQuery !== '') {
targetURLs.unshift(candidateRootURL + candidatePath + candidateQuery);
}
// Create preview whenever possible
createPreview(type, targetURLs[0]);
// Fill menu
var menuEntryTemplate = dialog.querySelector('table.toolbar tr.entry'); var menuEntryTemplate = dialog.querySelector('table.toolbar tr.entry');
var tbody = dialog.querySelector('div.entries tbody'); var tbody = dialog.querySelector('div.dynamic table.entries tbody');
// Adding URL filtering rules
var url, menuEntry; var url, menuEntry;
for ( var i = 0; i < targetURLs.length; i++ ) { for ( var i = 0; i < targetURLs.length; i++ ) {
url = targetURLs[i]; url = targetURLs[i];
@ -852,30 +930,169 @@ var urlFilteringDialog = (function() {
} }
colorize(); colorize();
};
var fillOriginSelect = function(select, hostname, domain) {
var option, pos;
var value = hostname;
for (;;) {
option = document.createElement('option');
option.setAttribute('value', value);
option.textContent = value;
select.appendChild(option);
if ( value === domain ) {
break;
}
pos = value.indexOf('.');
if ( pos === -1 ) {
break;
}
value = value.slice(pos + 1);
}
};
// Fill static filtering pane
var fillStaticPane = function() {
var template = vAPI.i18n('loggerStaticFilteringSentence');
var rePlaceholder = /\{\{[^}]+?\}\}/g;
var nodes = [];
var match, pos = 0;
var select, option, i, value;
for (;;) {
match = rePlaceholder.exec(template);
if ( match === null ) {
break;
}
if ( pos !== match.index ) {
nodes.push(document.createTextNode(template.slice(pos, match.index)));
}
pos = rePlaceholder.lastIndex;
switch ( match[0] ) {
case '{{br}}':
nodes.push(document.createElement('br'));
break;
case '{{action}}':
select = document.createElement('select');
select.className = 'static action';
option = document.createElement('option');
option.setAttribute('value', '');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartBlock');
select.appendChild(option);
option = document.createElement('option');
option.setAttribute('value', '@@');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAllow');
select.appendChild(option);
nodes.push(select);
break;
case '{{type}}':
select = document.createElement('select');
select.className = 'static type';
option = document.createElement('option');
option.setAttribute('value', targetType);
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartType').replace('{{type}}', targetType);
select.appendChild(option);
option = document.createElement('option');
option.setAttribute('value', '');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAnyType');
select.appendChild(option);
nodes.push(select);
break;
case '{{url}}':
select = document.createElement('select');
select.className = 'static url';
for ( i = 0; i < targetURLs.length; i++ ) {
value = targetURLs[i].replace(/^[a-z]+:\/\//, '');
option = document.createElement('option');
option.setAttribute('value', value);
option.textContent = value;
select.appendChild(option);
}
nodes.push(select);
break;
case '{{origin}}':
select = document.createElement('select');
select.className = 'static origin';
fillOriginSelect(select, targetFrameHostname, targetFrameDomain);
option = document.createElement('option');
option.setAttribute('value', '');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartAnyOrigin');
select.appendChild(option);
nodes.push(select);
break;
case '{{importance}}':
select = document.createElement('select');
select.className = 'static importance';
option = document.createElement('option');
option.setAttribute('value', '');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartNotImportant');
select.appendChild(option);
option = document.createElement('option');
option.setAttribute('value', 'important');
option.textContent = vAPI.i18n('loggerStaticFilteringSentencePartImportant');
select.appendChild(option);
nodes.push(select);
break;
default:
break;
}
}
if ( pos < template.length ) {
nodes.push(document.createTextNode(template.slice(pos)));
}
var parent = dialog.querySelector('div.containers > div.static > p:first-of-type');
removeAllChildren(parent);
for ( i = 0; i < nodes.length; i++ ) {
parent.appendChild(nodes[i]);
}
parseStaticInputs();
};
var fillDialog = function(domains) {
targetDomain = domains[0];
targetPageDomain = domains[1];
targetFrameDomain = domains[2];
createPreview(targetType, targetURLs[0]);
fillDynamicPane();
fillStaticPane();
document.body.appendChild(urlFilteringMenu); document.body.appendChild(urlFilteringMenu);
urlFilteringMenu.addEventListener('click', onClick, true); urlFilteringMenu.addEventListener('click', onClick, true);
selectContext.addEventListener('change', colorize); urlFilteringMenu.addEventListener('change', onSelectChange, true);
selectType.addEventListener('change', colorize); urlFilteringMenu.addEventListener('input', onInputChange, true);
};
var toggleOn = function(ev) {
dialog = urlFilteringMenu.querySelector('.dialog');
targetRow = ev.target.parentElement;
targetTabId = tabIdFromClassName(targetRow.className);
targetType = targetRow.cells[4].textContent.trim() || '';
targetURLs = createTargetURLs(targetRow.cells[5].textContent);
targetPageHostname = targetRow.getAttribute('data-hn-page') || '';
targetFrameHostname = targetRow.getAttribute('data-hn-frame') || '';
// We need the root domain names for best user experience.
messager.send({
what: 'getDomainNames',
targets: [targetURLs[0], targetPageHostname, targetFrameHostname]
}, fillDialog);
}; };
var toggleOff = function() { var toggleOff = function() {
if ( selectContext !== null ) { removeAllChildren(dialog.querySelector('div.preview'));
selectContext.removeEventListener('change', colorize); removeAllChildren(dialog.querySelector('div.dynamic table.entries tbody'));
selectContext = null;
}
if ( selectType !== null ) {
selectType.removeEventListener('change', colorize);
selectType = null;
}
if ( dialog !== null ) {
uDom(dialog).descendants('table.toolbar td.preview > *').remove();
uDom(dialog).descendants('div.entries tr').remove();
dialog = null; dialog = null;
} targetRow = null;
urlFilteringMenu.removeEventListener('click', onClick, true);
document.body.removeChild(urlFilteringMenu);
targetURLs = []; targetURLs = [];
urlFilteringMenu.removeEventListener('click', onClick, true);
urlFilteringMenu.removeEventListener('change', onSelectChange, true);
urlFilteringMenu.removeEventListener('input', onInputChange, true);
document.body.removeChild(urlFilteringMenu);
}; };
return { return {
@ -1186,7 +1403,7 @@ uDom.onLoad(function() {
uDom('#clear').on('click', clearBuffer); uDom('#clear').on('click', clearBuffer);
uDom('#maxEntries').on('change', onMaxEntriesChanged); uDom('#maxEntries').on('change', onMaxEntriesChanged);
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn); 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)', urlFilteringDialog.toggleOn); uDom('#content').on('click', 'tr.cat_net > td:nth-of-type(4)', filteringDialog.toggleOn);
}); });
/******************************************************************************/ /******************************************************************************/

View File

@ -32,9 +32,29 @@
/******************************************************************************/ /******************************************************************************/
var onMessage = function(request, sender, callback) { var µb = µBlock;
var µb = µBlock;
/******************************************************************************/
var getDomainNames = function(targets) {
var out = [];
var µburi = µb.URI;
var target, domain;
for ( var i = 0; i < targets.length; i++ ) {
target = targets[i];
if ( target.indexOf('/') !== -1 ) {
domain = µburi.domainFromURI(target) || '';
} else {
domain = µburi.domainFromHostname(target) || target;
}
out.push(domain);
}
return out;
};
/******************************************************************************/
var onMessage = function(request, sender, callback) {
// Async // Async
switch ( request.what ) { switch ( request.what ) {
case 'getAssetContent': case 'getAssetContent':
@ -71,6 +91,10 @@ var onMessage = function(request, sender, callback) {
} }
break; break;
case 'createUserFilter':
µb.appendUserFilters(request.filters);
break;
case 'forceUpdateAssets': case 'forceUpdateAssets':
µb.assetUpdater.force(); µb.assetUpdater.force();
break; break;
@ -79,6 +103,10 @@ var onMessage = function(request, sender, callback) {
response = {name: vAPI.app.name, version: vAPI.app.version}; response = {name: vAPI.app.name, version: vAPI.app.version};
break; break;
case 'getDomainNames':
response = getDomainNames(request.targets);
break;
case 'getUserSettings': case 'getUserSettings':
response = µb.userSettings; response = µb.userSettings;
break; break;
@ -601,10 +629,6 @@ var onMessage = function(request, sender, callback) {
var response; var response;
switch ( request.what ) { switch ( request.what ) {
case 'createUserFilter':
µb.appendUserFilters(request.filters);
break;
case 'elementPickerEprom': case 'elementPickerEprom':
µb.epickerEprom = request; µb.epickerEprom = request;
break; break;

View File

@ -1294,7 +1294,10 @@ FilterParser.prototype.parseOptType = function(raw, not) {
/******************************************************************************/ /******************************************************************************/
FilterParser.prototype.parseOptParty = function(not) { FilterParser.prototype.parseOptParty = function(firstParty, not) {
if ( firstParty ) {
not = !not;
}
if ( not ) { if ( not ) {
this.firstParty = true; this.firstParty = true;
} else { } else {
@ -1330,7 +1333,7 @@ FilterParser.prototype.parseOptions = function(s) {
opt = opt.slice(1); opt = opt.slice(1);
} }
if ( opt === 'third-party' ) { if ( opt === 'third-party' ) {
this.parseOptParty(not); this.parseOptParty(false, not);
continue; continue;
} }
if ( opt === 'elemhide' ) { if ( opt === 'elemhide' ) {
@ -1358,6 +1361,10 @@ FilterParser.prototype.parseOptions = function(s) {
this.important = Important; this.important = Important;
continue; continue;
} }
if ( opt === 'first-party' ) {
this.parseOptParty(true, not);
continue;
}
this.unsupported = true; this.unsupported = true;
break; break;
} }

View File

@ -43,29 +43,40 @@
<div id="hiddenTemplate"><span style="display:none;"></span></div> <div id="hiddenTemplate"><span style="display:none;"></span></div>
<div id="urlFilteringMenu"> <div id="urlFilteringMenu">
<div class="dialog"> <div class="dialog">
<div class="hide preview"></div>
<div class="headers">
<span class="header dynamic selected" data-container="dynamic" data-i18n="loggerURLFilteringHeader"></span>
<span class="header static" data-container="static" data-i18n="loggerStaticFilteringHeader"></span>
<span class="tools"><span class="fa reload">&#xf021;</span>&emsp;<span class="fa picker">&#xf1fb;</span></span>
</div>
<div class="containers">
<div class="container dynamic selected">
<table class="toolbar"> <table class="toolbar">
<colgroup><col><col></colgroup> <colgroup><col><col></colgroup>
<tbody> <tbody>
<tr> <tr>
<td colspan="2" class="hide preview"></td> <td><span id="saveRules" class="fa">&#xf023;</span>
<tr> <td><p>
<td><span class="fa save">&#xf023;</span> <label><span data-i18n="loggerURLFilteringContextLabel"></span> <select class="dynamic origin"></select></label>&emsp;
<td><label><span data-i18n="loggerURLFilteringContextLabel"></span> <select class="context"></select></label>&emsp; <label><span data-i18n="loggerURLFilteringTypeLabel"></span> <select class="dynamic type"><option><option value="*">*</select></label>
<label><span data-i18n="loggerURLFilteringTypeLabel"></span> <select class="type"><option><option value="*">*</select></label>&emsp; </p>
<span class="fa reload">&#xf021;</span>&emsp;<span class="fa picker">&#xf1fb;</span>
<tr class="entry"> <tr class="entry">
<td><div class="action"><span class="allow">&nbsp;</span><span class="noop">&nbsp;</span><span class="block">&nbsp;</span></div> <td><div class="action"><span class="allow">&nbsp;</span><span class="noop">&nbsp;</span><span class="block">&nbsp;</span></div>
<td class="url"> <td class="url">
</tbody> </tbody>
</table> </table>
<div class="header" data-i18n="loggerURLFilteringHeader"></div> <table class="entries">
<div class="entries">
<table>
<colgroup><col><col></colgroup> <colgroup><col><col></colgroup>
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
<!-- <div class="header">Static URL filtering (TBD)</div> --> <div class="container static">
<p></p>
<p><textarea class="staticFilter" value=""></textarea>
<button id="createStaticFilter" class="important" type="button" data-i18n="pickerCreate"></button>
</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>