aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/background.html3
-rw-r--r--src/css/popup.css209
-rw-r--r--src/js/background.js3
-rw-r--r--src/js/contentscript-end.js14
-rw-r--r--src/js/dynamic-net-filtering.js485
-rw-r--r--src/js/messaging.js55
-rw-r--r--src/js/pagestore.js69
-rw-r--r--src/js/popup.js72
-rw-r--r--src/js/static-net-filtering.js (renamed from src/js/net-filtering.js)411
-rw-r--r--src/js/stats.js7
-rw-r--r--src/js/storage.js37
-rw-r--r--src/js/tab.js4
-rw-r--r--src/js/traffic.js29
-rw-r--r--src/js/ublock.js75
-rw-r--r--src/js/xal.js6
-rw-r--r--src/popup.html69
16 files changed, 923 insertions, 625 deletions
diff --git a/src/background.html b/src/background.html
index 1da6f33..7edea35 100644
--- a/src/background.html
+++ b/src/background.html
@@ -16,7 +16,8 @@
<script src="js/liquid-dict.js"></script>
<script src="js/utils.js"></script>
<script src="js/assets.js"></script>
-<script src="js/net-filtering.js"></script>
+<script src="js/dynamic-net-filtering.js"></script>
+<script src="js/static-net-filtering.js"></script>
<script src="js/cosmetic-filtering.js"></script>
<script src="js/ublock.js"></script>
<script src="js/messaging.js"></script>
diff --git a/src/css/popup.css b/src/css/popup.css
index 3a832fd..8a2f2e6 100644
--- a/src/css/popup.css
+++ b/src/css/popup.css
@@ -1,12 +1,12 @@
body {
- margin: 0;
+ background-color: white;
border: 0;
- padding: 0;
+ float: left;
font: 13px sans-serif;
- background-color: white;
- min-width: 180px;
+ margin: 0;
overflow: hidden;
- float: left;
+ padding: 0;
+ white-space: nowrap;
}
h1,h2,h3,h4 {
margin: 0;
@@ -30,7 +30,13 @@ a {
font-size: 11px;
}
body > div {
- padding: 4px 12px 0 12px;
+ background-color: transparent;
+ display: inline-block;
+ vertical-align: top;
+ }
+body > div:nth-of-type(2) {
+ padding: 4px 12px 0 5px;
+ position: relative;
}
p {
margin: 16px 0;
@@ -53,6 +59,9 @@ p {
font-size: 11px;
color: #888;
}
+[data-i18n="popupBlockedRequestPrompt"] {
+ font-size: 16px;
+ }
#page-blocked {
margin-top: 4px;
font-size: 20px;
@@ -80,6 +89,20 @@ p {
color: #444;
}
+#dynamicFilteringToggler {
+ pointer-events: none;
+ }
+#dynamicFilteringToggler::before {
+ color: gray;
+ content: '+\202F';
+ cursor: pointer;
+ font-size: 13px;
+ pointer-events: auto;
+ }
+body.dynamicFilteringEnabled #dynamicFilteringToggler::before {
+ content: '\2212\202F';
+ }
+
.dynamicFiltering div > .tip {
background-color: #fffff4;
border: 1px solid #888;
@@ -105,157 +128,67 @@ p {
font: normal monospace;
padding: 1px 0;
}
-#dynamicFilteringToggler {
- margin: 0;
- border: 0;
- padding: 0;
- width: 100%;
- text-align: center;
- cursor: pointer;
- position: relative;
- }
-#dynamicFilteringToggler.hasBlock:not(.on) {
- background-color: #fbb;
- }
-#dynamicFilteringToggler > div {
- font-size: 9px;
- line-height: 100%;
- color: #888;
- position: absolute;
- bottom: 0;
- width: 25%;
- display: none;
- pointer-events: none;
- }
-#dynamicFilteringToggler.on:hover > div {
- display: initial;
- }
-#dynamicFilteringToggler > div:nth-of-type(1) {
- left: 0;
- }
-#dynamicFilteringToggler > div:nth-of-type(2) {
- left: 25%;
- }
-#dynamicFilteringToggler > div:nth-of-type(3) {
- left: 50%;
- }
-#dynamicFilteringToggler > div:nth-of-type(4) {
- left: 75%;
- }
-#dynamicFilteringToggler > a {
- padding: 0 2px 0 8px;
- position: absolute;
- right: 0;
- color: #666;
- visibility: hidden;
- }
-body[dir=rtl] #dynamicFilteringToggler > a {
- right: auto;
- left: 0;
-}
-#dynamicFilteringToggler a:hover {
- color: #444;
- }
-#dynamicFilteringToggler.on:hover a {
- visibility: visible;
- }
-#dynamicFilteringToggler::before {
- content: '\f107';
- }
-#dynamicFilteringToggler.on::before {
- content: '\f106';
- }
#dynamicFilteringContainer {
- margin: 0;
border: 0;
+ direction: rtl;
+ font-size: 12px;
+ margin: 0;
+ overflow: hidden;
padding: 0;
- display: none;
+ text-align: right;
+ width: 7px;
}
-#dynamicFilteringToggler.on + #dynamicFilteringContainer {
- display: initial;
+body.dynamicFilteringEnabled #dynamicFilteringContainer {
+ display: block;
+ width: 200px;
}
-.dynamicFiltering {
- margin: 0;
+#dynamicFilteringContainer > div {
border: 0;
+ direction: ltr;
+ margin: 0;
padding: 0;
- height: 2.75em;
- position: relative;
+ width: 200px;
}
-.dynamicFiltering.local {
+#dynamicFilteringContainer > div > span {
+ background-color: transparent;
+ border: none;
border-bottom: 1px solid white;
- }
-.dynamicFiltering.global {
- height: 1.25em;
- }
-.dynamicFiltering > div {
- margin: 0;
- border: 1px solid #e6e6e6;
- padding: 0;
box-sizing: border-box;
- background-color: #e6e6e6;
- position: absolute;
- cursor: pointer;
- }
-.dynamicFiltering > div:not(:first-child) {
- border-left: 1px solid white !important;
- }
-.dynamicFiltering > div:nth-of-type(1) {
- left: 0;
- width: 7%;
- height: 100%;
- }
-.dynamicFiltering > div:nth-of-type(2) {
- left: 7%;
- width: 18%;
- height: 100%;
- }
-.dynamicFiltering > div:nth-of-type(3) {
- left: 25%;
- width: 25%;
- height: 100%;
+ color: gray;
+ display: inline-block;
+ height: 2em;
+ line-height: 2em;
+ pointer-events: none;
+ vertical-align: top;
}
-.dynamicFiltering > div:nth-of-type(4) {
- left: 50%;
- width: 25%;
- height: 100%;
+body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
+ background-color: #e6e6e6;
+ pointer-events: auto;
}
-.dynamicFiltering > div:nth-of-type(5) {
- left: 75%;
- width: 25%;
- height: 100%;
+#dynamicFilteringContainer > div > span:nth-of-type(1) {
+ border-right: 1px solid white;
+ padding-right: 4px;
+ width: 75%;
}
-.dynamicFiltering > div:nth-of-type(6) {
- left: 0;
- width: 50%;
+#dynamicFilteringContainer > div > span:nth-of-type(2) {
+ cursor: pointer;
+ width: 9%;
}
-.dynamicFiltering > div:nth-of-type(7) {
- left: 50%;
- width: 50%;
+#dynamicFilteringContainer > div > span:nth-of-type(3) {
+ border-left: 1px solid white;
+ cursor: pointer;
+ width: 16%;
}
-.dynamicFiltering > div.label {
- margin: 0;
- border: 0;
- padding: 0;
- pointer-events: none;
- color: black;
- opacity: 0.4;
- font: 12px monospace;
- text-align: center;
- top: 50%;
- -webkit-transform: translateY(-50%);
- transform: translateY(-50%);
- background-color: transparent;
+body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span:nth-of-type(3) {
+ pointer-events: auto;
}
-.dynamicFiltering > div.blocked {
- border-color: #fbb;
+#dynamicFilteringContainer span.blocked[data-src] {
background-color: #fbb;
}
-.dynamicFiltering > div.ownFilter {
- border-color: #bbb;
+#dynamicFilteringContainer span.ownFilter[data-src] {
background-color: #bbb;
}
-.dynamicFiltering > div.blocked.ownFilter {
- border-color: #f66;
+#dynamicFilteringContainer span.blocked.ownFilter[data-src] {
background-color: #f66;
- }
+ } \ No newline at end of file
diff --git a/src/js/background.js b/src/js/background.js
index 0f33405..c181616 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -55,6 +55,7 @@ return {
autoUpdate: true,
collapseBlocked: true,
contextMenuEnabled: true,
+ dynamicFilteringString: '',
dynamicFilteringSelfie: '',
dynamicFilteringEnabled: false,
experimentalEnabled: false,
@@ -106,7 +107,7 @@ return {
firstUpdateAfter: 5 * oneMinute,
nextUpdateAfter: 7 * oneHour,
- selfieMagic: 'odyxfmbsqllh',
+ selfieMagic: 'qidcglrwobsm',
selfieAfter: 7 * oneMinute,
pageStores: {},
diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js
index 1d8cf7f..b6a5c76 100644
--- a/src/js/contentscript-end.js
+++ b/src/js/contentscript-end.js
@@ -216,7 +216,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
var selector;
while ( i-- ) {
selector = generics[i];
- if ( injectedSelectors[selector] !== undefined ) {
+ if ( injectedSelectors.hasOwnProperty(selector) ) {
continue;
}
injectedSelectors[selector] = true;
@@ -241,14 +241,14 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
if ( !attrValue ) { continue; }
selector = '[' + attr + '="' + attrValue + '"]';
if ( generics[selector] ) {
- if ( injectedSelectors[selector] === undefined ) {
+ if ( injectedSelectors.hasOwnProperty(selector) === false ) {
injectedSelectors[selector] = true;
out.push(selector);
}
}
selector = node.tagName.toLowerCase() + selector;
if ( generics[selector] ) {
- if ( injectedSelectors[selector] === undefined ) {
+ if ( injectedSelectors.hasOwnProperty(selector) === false ) {
injectedSelectors[selector] = true;
out.push(selector);
}
@@ -277,7 +277,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
iSelector = selectors.length;
while ( iSelector-- ) {
selector = selectors[iSelector];
- if ( injectedSelectors[selector] === undefined ) {
+ if ( injectedSelectors.hasOwnProperty(selector) === false ) {
injectedSelectors[selector] = true;
out.push(selector);
}
@@ -293,7 +293,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
var processHighHighGenerics = function() {
processHighHighGenericsTimer = null;
- if ( injectedSelectors['{{highHighGenerics}}'] !== undefined ) { return; }
+ if ( injectedSelectors.hasOwnProperty('{{highHighGenerics}}') ) { return; }
if ( document.querySelector(highGenerics.hideHigh) === null ) { return; }
injectedSelectors['{{highHighGenerics}}'] = true;
// We need to filter out possible exception cosmetic filters from
@@ -443,10 +443,10 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
}
}
if ( addedNodeListsTimer === null ) {
- // I arbitrarily chose 50 ms for now:
+ // I arbitrarily chose 100 ms for now:
// I have to compromise between the overhead of processing too few
// nodes too often and the delay of many nodes less often.
- addedNodeListsTimer = setTimeout(mutationObservedHandler, 75);
+ addedNodeListsTimer = setTimeout(mutationObservedHandler, 100);
}
};
diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js
new file mode 100644
index 0000000..6e137da
--- /dev/null
+++ b/src/js/dynamic-net-filtering.js
@@ -0,0 +1,485 @@
+/*******************************************************************************
+
+ µBlock - a Chromium browser extension to black/white list requests.
+ Copyright (C) 2014 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 punycode, µBlock */
+/* jshint bitwise: false */
+
+/******************************************************************************/
+
+µBlock.dynamicNetFilteringEngine = (function() {
+
+/******************************************************************************/
+
+var magicId = 'chmdgxwtetgu';
+
+/******************************************************************************/
+
+var Matrix = function() {
+ this.reset();
+};
+
+/******************************************************************************/
+
+var typeBitOffsets = {
+ '*': 0,
+'inline-script': 2,
+ '1p-script': 4,
+ '3p-script': 6,
+ '3p-frame': 8,
+ 'image': 10
+};
+
+var stateToNameMap = {
+ '1': 'block',
+ '2': 'allow',
+ '3': 'noop'
+};
+
+var nameToStateMap = {
+ 'block': 1,
+ 'allow': 2,
+ 'noop': 3
+};
+
+/******************************************************************************/
+
+// For performance purpose, as simple tests as possible
+var reHostnameVeryCoarse = /[g-z_-]/;
+var reIPv4VeryCoarse = /\.\d+$/;
+
+// http://tools.ietf.org/html/rfc5952
+// 4.3: "MUST be represented in lowercase"
+// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
+
+var isIPAddress = function(hostname) {
+ if ( reHostnameVeryCoarse.test(hostname) ) {
+ return false;
+ }
+ if ( reIPv4VeryCoarse.test(hostname) ) {
+ return true;
+ }
+ return hostname.charAt(0) === '[';
+};
+
+/******************************************************************************/
+
+var toBroaderHostname = function(hostname) {
+ if ( hostname === '*' ) {
+ return '';
+ }
+ if ( isIPAddress(hostname) ) {
+ return '*';
+ }
+ var pos = hostname.indexOf('.');
+ if ( pos === -1 ) {
+ return '*';
+ }
+ return hostname.slice(pos + 1);
+};
+
+Matrix.toBroaderHostname = toBroaderHostname;
+
+/******************************************************************************/
+
+Matrix.prototype.reset = function() {
+ this.r = 0;
+ this.type = '';
+ this.y = '';
+ this.z = '';
+ this.rules = {};
+};
+
+/******************************************************************************/
+
+Matrix.prototype.setCell = function(srcHostname, desHostname, type, state) {
+ var bitOffset = typeBitOffsets[type];
+ var k = srcHostname + ' ' + desHostname;
+ var oldBitmap = this.rules[k];
+ if ( oldBitmap === undefined ) {
+ oldBitmap = 0;
+ }
+ var newBitmap = oldBitmap & ~(3 << bitOffset) | (state << bitOffset);
+ if ( newBitmap === oldBitmap ) {
+ return false;
+ }
+ if ( newBitmap === 0 ) {
+ delete this.rules[k];
+ } else {
+ this.rules[k] = newBitmap;
+ }
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.blockCell = function(srcHostname, desHostname, type) {
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 1 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 1 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 1);
+ return true;
+};
+
+// https://www.youtube.com/watch?v=Csewb_eIStY
+
+/******************************************************************************/
+
+Matrix.prototype.allowCell = function(srcHostname, desHostname, type) {
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 2 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 2 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 2);
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.unsetCell = function(srcHostname, desHostname, type) {
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 0 ) {
+ return false;
+ }
+ this.setCell(srcHostname, desHostname, type, 0);
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ if ( this.r === 0 || this.r === 3 ) {
+ return true;
+ }
+ this.setCell(srcHostname, desHostname, type, 3);
+ return true;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCell = function(srcHostname, desHostname, type) {
+ var key = srcHostname + ' ' + desHostname;
+ var bitmap = this.rules[key];
+ if ( bitmap === undefined ) {
+ return 0;
+ }
+ return bitmap >> typeBitOffsets[type] & 3;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.clearRegisters = function() {
+ this.r = 0;
+ this.type = '';
+ this.y = '';
+ this.z = '';
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
+ var bitOffset = typeBitOffsets[type];
+ var s = srcHostname;
+ var v;
+ for (;;) {
+ this.z = s;
+ v = this.rules[s + ' ' + desHostname];
+ if ( v !== undefined ) {
+ v = v >> bitOffset & 3;
+ if ( v !== 0 ) {
+ return v;
+ }
+ }
+ s = toBroaderHostname(s);
+ if ( s === '' ) {
+ break;
+ }
+ }
+ // srcHostname is '*' at this point
+ return 0;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
+ if ( typeBitOffsets.hasOwnProperty(type) === false ) {
+ this.type = '';
+ this.r = 0;
+ return this;
+ }
+ this.type = type;
+ // Specific-hostname specific-type cell
+ this.y = desHostname;
+ this.r = this.evaluateCellZ(srcHostname, desHostname, type);
+ if ( this.r !== 0 ) { return this; }
+
+ var d = desHostname;
+ for (;;) {
+ d = toBroaderHostname(d);
+ if ( d === '*' ) {
+ break;
+ }
+ // specific-hostname specific-type cell
+ this.y = d;
+ this.r = this.evaluateCellZ(srcHostname, d, type);
+ if ( this.r !== 0 ) { return this; }
+ }
+
+ // Any-hostname specific-type cells
+ this.y = '*';
+ this.r = this.evaluateCellZ(srcHostname, '*', type);
+ return this;
+};
+
+// http://youtu.be/gSGk1bQ9rcU?t=25m6s
+
+/******************************************************************************/
+
+Matrix.prototype.mustBlockOrAllow = function() {
+ return this.r === 1 || this.r === 2;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.toFilterString = function() {
+ if ( this.type === '' ) {
+ return '';
+ }
+ if ( this.r === 1 ) {
+ return 'db:' + this.z + ' ' + this.y + ' ' + this.type + ' block';
+ }
+ if ( this.r === 2 ) {
+ return 'da:' + this.z + ' ' + this.y + ' ' + this.type + ' allow';
+ }
+ if ( this.r === 3 ) {
+ return 'dn:' + this.z + ' ' + this.y + ' ' + this.type + ' noop';
+ }
+ return '';
+};
+
+/******************************************************************************/
+
+Matrix.prototype.mustBlock = function(srcHostname, desHostname, type) {
+ this.evaluateCellZY(srcHostname, desHostname, type);
+ return this.r === 1;
+};
+
+/******************************************************************************/
+
+Matrix.prototype.srcHostnameFromRule = function(rule) {
+ return rule.slice(0, rule.indexOf(' '));
+};
+
+/******************************************************************************/
+
+Matrix.prototype.desHostnameFromRule = function(rule) {
+ return rule.slice(rule.indexOf(' ') + 1);
+};
+
+/******************************************************************************/
+
+Matrix.prototype.toString = function() {
+ var out = [];
+ var rule, type, val;
+ var srcHostname, desHostname;
+ for ( rule in this.rules ) {
+ if ( this.rules.hasOwnProperty(rule) === false ) {
+ continue;
+ }
+ srcHostname = this.srcHostnameFromRule(rule);
+ desHostname = this.desHostnameFromRule(rule);
+ for ( type in typeBitOffsets ) {
+ if ( typeBitOffsets.hasOwnProperty(type) === false ) {
+ continue;
+ }
+ val = this.evaluateCell(srcHostname, desHostname, type);
+ if ( val === 0 ) {
+ continue;
+ }
+ out.push(
+ punycode.toUnicode(srcHostname) + ' ' +
+ punycode.toUnicode(desHostname) + ' ' +
+ type + ' ' +
+ stateToNameMap[val]
+ );
+ }
+ }
+ return out.join('\n');
+};
+
+/******************************************************************************/
+
+Matrix.prototype.fromString = function(text, append) {
+ var textEnd = text.length;
+ var lineBeg = 0, lineEnd;
+ var line, pos;
+ var fields, fieldVal;
+ var srcHostname = '';
+ var desHostname = '';
+ var type, state;
+
+ while ( lineBeg < textEnd ) {
+ lineEnd = text.indexOf('\n', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = text.indexOf('\r', lineBeg);
+ if ( lineEnd < 0 ) {
+ lineEnd = textEnd;
+ }
+ }
+ line = text.slice(lineBeg, lineEnd).trim();
+ lineBeg = lineEnd + 1;
+
+ pos = line.indexOf('# ');
+ if ( pos !== -1 ) {
+ line = line.slice(0, pos).trim();
+ }
+ if ( line === '' ) {
+ continue;
+ }
+
+ fields = line.split(/\s+/);
+
+ // Less than 2 fields makes no sense
+ if ( fields.length < 2 ) {
+ continue;
+ }
+
+ fieldVal = fields[0];
+
+ // Valid rule syntax:
+
+ // srcHostname desHostname [type [state]]
+ // type = a valid request type
+ // state = [`block`, `allow`, `inherit`]
+
+ // srcHostname desHostname type
+ // type = a valid request type
+ // state = `allow`
+
+ // srcHostname desHostname
+ // type = `*`
+ // state = `allow`
+
+ // Lines with invalid syntax silently ignored
+
+ srcHostname = punycode.toASCII(fields[0]);
+ desHostname = punycode.toASCII(fields[1]);
+
+ fieldVal = fields[2];
+
+ if ( fieldVal !== undefined ) {
+ type = fieldVal;
+ // Unknown type: reject
+ if ( typeBitOffsets.hasOwnProperty(type) === false ) {
+ continue;
+ }
+ } else {
+ type = '*';
+ }
+
+ fieldVal = fields[3];
+
+ if ( fieldVal !== undefined ) {
+ // Unknown state: reject
+ if ( nameToStateMap.hasOwnProperty(fieldVal) === false ) {
+ continue;
+ }
+ state = nameToStateMap[fieldVal];
+ } else {
+ state = 2;
+ }
+
+ this.setCell(srcHostname, desHostname, type, state);
+ }
+};
+
+/******************************************************************************/
+
+Matrix.prototype.fromObsoleteSelfie = function(selfie) {
+ if ( selfie === '' ) {
+ return '';
+ }
+ var bin = JSON.parse(selfie);
+ var filters = bin.filters;
+ var bits, val;
+ for ( var hostname in filters ) {
+ if ( filters.hasOwnProperty(hostname) === false ) {
+ continue;
+ }
+ bits = filters[hostname];
+ val = bits & 3;
+ if ( val === 1 ) {
+ this.setCell(hostname, '*', 'inline-script', 1);
+ } else if ( val === 2 ) {
+ this.setCell(hostname, '*', 'inline-script', 3);
+ }
+ val = (bits >> 2) & 3;
+ if ( val === 1 ) {
+ this.setCell(hostname, '*', '1p-script', 1);
+ } else if ( val === 2 ) {
+ this.setCell(hostname, '*', '1p-script', 3);
+ }
+ val = (bits >> 4) & 3;
+ if ( val === 1 ) {
+ this.setCell(hostname, '*', '3p-script', 1);
+ } else if ( val === 2 ) {
+ this.setCell(hostname, '*', '3p-script', 3);
+ }
+ val = (bits >> 8) & 3;
+ if ( val === 1 ) {
+ this.setCell(hostname, '*', '3p-frame', 1);
+ } else if ( val === 2 ) {
+ this.setCell(hostname, '*', '3p-frame', 3);
+ }
+ }
+};
+
+/******************************************************************************/
+
+Matrix.prototype.toSelfie = function() {
+ return {
+ magicId: magicId,
+ rules: this.rules
+ };
+};
+
+/******************************************************************************/
+
+Matrix.prototype.fromSelfie = function(selfie) {
+ this.rules = selfie.rules;
+};
+
+/******************************************************************************/
+
+return new Matrix;
+
+/******************************************************************************/
+
+// http://youtu.be/5-K8R1hDG9E?t=31m1s
+
+})();
+
+/******************************************************************************/
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 35ad5be..db662c5 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -42,10 +42,6 @@ var onMessage = function(request, sender, callback) {
µb.assets.get(request.url, callback);
return;
- case 'loadUbiquitousAllowRules':
- µb.loadUbiquitousWhitelists();
- return;
-
default:
break;
}
@@ -108,13 +104,14 @@ var µb = µBlock;
/******************************************************************************/
var getDynamicFilterResults = function(scope) {
- return [
- µb.netFilteringEngine.matchDynamicFilters(scope, 'inline-script', true),
- µb.netFilteringEngine.matchDynamicFilters(scope, 'script', true),
- µb.netFilteringEngine.matchDynamicFilters(scope, 'script', false),
- µb.netFilteringEngine.matchDynamicFilters(scope, 'sub_frame', true),
- µb.netFilteringEngine.matchDynamicFilters(scope, 'sub_frame', false)
- ];
+ var r = {};
+ var dFiltering = µb.dynamicNetFilteringEngine;
+ r['image'] = dFiltering.evaluateCellZY(scope, '*', 'image').toFilterString();
+ r['inline-script'] = dFiltering.evaluateCellZY(scope, '*', 'inline-script').toFilterString();
+ r['1p-script'] = dFiltering.evaluateCellZY(scope, '*', '1p-script').toFilterString();
+ r['3p-script'] = dFiltering.evaluateCellZY(scope, '*', '3p-script').toFilterString();
+ r['3p-frame'] = dFiltering.evaluateCellZY(scope, '*', '3p-frame').toFilterString();
+ return r;
};
/******************************************************************************/
@@ -134,7 +131,7 @@ var getStats = function(tab) {
logRequests: µb.userSettings.logRequests,
dynamicFilteringEnabled: µb.userSettings.dynamicFilteringEnabled,
dynamicFilterResults: {
- '/': getDynamicFilterResults('*')
+ '*': getDynamicFilterResults('*')
}
};
var pageStore = tab && µb.pageStoreFromTabId(tab.id);
@@ -145,7 +142,7 @@ var getStats = function(tab) {
r.pageBlockedRequestCount = pageStore.perLoadBlockedRequestCount;
r.pageAllowedRequestCount = pageStore.perLoadAllowedRequestCount;
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
- r.dynamicFilterResults['.'] = getDynamicFilterResults(r.pageHostname);
+ r.dynamicFilterResults['local'] = getDynamicFilterResults(r.pageHostname);
}
return r;
};
@@ -185,9 +182,9 @@ var onMessage = function(request, sender, callback) {
case 'toggleDynamicFilter':
µb.toggleDynamicFilter(request);
- response = { '/': getDynamicFilterResults('*') };
+ response = { '*': getDynamicFilterResults('*') };
if ( request.pageHostname ) {
- response['.'] = getDynamicFilterResults(request.pageHostname);
+ response['local'] = getDynamicFilterResults(request.pageHostname);
}
break;
@@ -281,9 +278,13 @@ var tagNameToRequestTypeMap = {
// Evaluate many requests
var filterRequests = function(pageStore, details) {
- details.pageDomain = µb.URI.domainFromHostname(details.pageHostname);
+ var µburi = µb.URI;
+
+ // Create evaluation context
+ details.pageDomain = µburi.domainFromHostname(details.pageHostname);
details.rootHostname = pageStore.rootHostname;
details.rootDomain = pageStore.rootDomain;
+ details.requestHostname = '';
var inRequests = details.requests;
var outRequests = [];
@@ -294,11 +295,10 @@ var filterRequests = function(pageStore, details) {
if ( tagNameToRequestTypeMap.hasOwnProperty(request.tagName) === false ) {
continue;
}
- result = pageStore.filterRequest(
- details,
- tagNameToRequestTypeMap[request.tagName],
- request.url
- );
+ details.requestURL = request.url;
+ details.requestHostname = µburi.hostnameFromURI(request.url);
+ details.requestType = tagNameToRequestTypeMap[request.tagName];
+ result = pageStore.filterRequest(details);
if ( pageStore.boolFromResult(result) ) {
outRequests.push(request);
}
@@ -317,14 +317,13 @@ var filterRequest = function(pageStore, details) {
if ( tagNameToRequestTypeMap.hasOwnProperty(details.tagName) === false ) {
return;
}
- details.pageDomain = µb.URI.domainFromHostname(details.pageHostname);
+ var µburi = µb.URI;
+ details.pageDomain = µburi.domainFromHostname(details.pageHostname);
details.rootHostname = pageStore.rootHostname;
details.rootDomain = pageStore.rootDomain;
- var result = pageStore.filterRequest(
- details,
- tagNameToRequestTypeMap[details.tagName],
- details.requestURL
- );
+ details.requestHostname = µburi.hostnameFromURI(details.requestURL);
+ details.requestType = tagNameToRequestTypeMap[details.tagName];
+ var result = pageStore.filterRequest(details);
if ( pageStore.boolFromResult(result) ) {
return { collapse: µb.userSettings.collapseBlocked };
}
@@ -485,7 +484,7 @@ var getLists = function(callback) {
available: null,
current: µb.remoteBlacklists,
cosmetic: µb.userSettings.parseAllABPHideFilters,
- netFilterCount: µb.netFilteringEngine.getFilterCount(),
+ netFilterCount: µb.staticNetFilteringEngine.getFilterCount(),
cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(),
autoUpdate: µb.userSettings.autoUpdate,
userFiltersPath: µb.userFiltersPath,
diff --git a/src/js/pagestore.js b/src/js/pagestore.js
index d042f64..51d67d0 100644
--- a/src/js/pagestore.js
+++ b/src/js/pagestore.js
@@ -245,6 +245,9 @@ FrameStore.prototype.init = function(rootHostname, frameURL) {
this.pageDomain = µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
this.rootHostname = rootHostname;
this.rootDomain = µburi.domainFromHostname(rootHostname) || rootHostname;
+ // This is part of the filtering evaluation context
+ this.requestURL = this.requestHostname = this.requestType = '';
+
return this;
};
@@ -252,7 +255,8 @@ FrameStore.prototype.init = function(rootHostname, frameURL) {
FrameStore.prototype.dispose = function() {
this.pageHostname = this.pageDomain =
- this.rootHostname = this.rootDomain = '';
+ this.rootHostname = this.rootDomain =
+ this.requestURL = this.requestHostname = this.requestType = '';
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
frameStoreJunkyard.push(this);
}
@@ -292,6 +296,16 @@ PageStore.factory = function(tabId, pageURL) {
/******************************************************************************/
+PageStore.prototype.bitFromRequestType = {
+ '': 1,
+ 'sb': 2,
+ 'sa': 4,
+ 'db': 8,
+ 'da': 16
+};
+
+/******************************************************************************/
+
PageStore.prototype.init = function(tabId, pageURL) {
this.tabId = tabId;
this.previousPageURL = '';
@@ -304,6 +318,10 @@ PageStore.prototype.init = function(tabId, pageURL) {
this.rootHostname = this.pageHostname;
this.rootDomain = this.pageDomain;
+ // This is part of the filtering evaluation context
+ this.requestURL = this.requestHostname = this.requestType = '';
+ this.requestHostnames = {};
+
this.frames = {};
this.netFiltering = true;
this.netFilteringReadTime = 0;
@@ -361,7 +379,9 @@ PageStore.prototype.dispose = function() {
// used as a key).
this.pageURL = this.previousPageURL =
this.pageHostname = this.pageDomain =
- this.rootHostname = this.rootDomain = '';
+ this.rootHostname = this.rootDomain =
+ this.requestURL = this.requestHostname = this.requestType = '';
+ this.requestHostnames = null;
this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose();
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
@@ -374,11 +394,8 @@ PageStore.prototype.dispose = function() {
PageStore.prototype.disposeFrameStores = function() {
var frames = this.frames;
- if ( typeof frames === 'object' ) {
- for ( var k in frames ) {
- if ( frames.hasOwnProperty(k) === false ) {
- continue;
- }
+ for ( var k in frames ) {
+ if ( frames.hasOwnProperty(k) ) {
frames[k].dispose();
}
}
@@ -414,20 +431,32 @@ PageStore.prototype.getNetFilteringSwitch = function() {
/******************************************************************************/
-PageStore.prototype.filterRequest = function(context, requestType, requestURL) {
- var result = '';
- if ( this.getNetFilteringSwitch() ) {
- var entry = this.netFilteringCache.lookup(requestURL);
- if ( entry !== undefined ) {
- //console.debug(' cache HIT: PageStore.filterRequest("%s")', requestURL);
- return entry.result;
- }
- //console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL);
- result = µb.netFilteringEngine.matchString(context, requestURL, requestType);
+PageStore.prototype.filterRequest = function(context) {
+ var requestURL = context.requestURL;
+
+ if ( this.getNetFilteringSwitch() === false ) {
+ this.recordResult(context.requestType, requestURL, '');
+ return '';
}
- if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) {
- this.netFilteringCache.add(requestURL, result, requestType, 0);
+
+ var entry = this.netFilteringCache.lookup(requestURL);
+ if ( entry !== undefined ) {
+ //console.debug('cache HIT: PageStore.filterRequest("%s")', requestURL);
+ return entry.result;
}
+
+ var result = µb.filterRequest(context);
+
+ //console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL);
+ this.recordResult(context.requestType, requestURL, result);
+
+ var requestHostname = context.requestHostname;
+ if ( this.requestHostnames.hasOwnProperty(requestHostname) ) {
+ this.requestHostnames[requestHostname] |= this.bitFromRequestType[result.slice(0, 2)];
+ } else {
+ this.requestHostnames[requestHostname] = this.bitFromRequestType[result.slice(0, 2)];
+ }
+
return result;
};
@@ -454,7 +483,7 @@ PageStore.prototype.recordResult = function(requestType, requestURL, result) {
// true: blocked
PageStore.prototype.boolFromResult = function(result) {
- return typeof result === 'string' && result !== '' && result.slice(0, 2) !== '@@';
+ return typeof result === 'string' && result.charAt(1) === 'b';
};
/******************************************************************************/
diff --git a/src/js/popup.js b/src/js/popup.js
index 554cb55..fdb82a6 100644
--- a/src/js/popup.js
+++ b/src/js/popup.js
@@ -30,7 +30,6 @@
/******************************************************************************/
var stats;
-var reResultParser = /^(@@)?(\*|\|\|([^$^]+)\^)\$(.+)$/;
/******************************************************************************/
@@ -50,23 +49,23 @@ var formatNumber = function(count) {
/******************************************************************************/
-var syncDynamicFilter = function(scope, i, result) {
- var el = uDom('[data-scope="' + scope + '"] > div:nth-of-type(' + i + ')');
- var matches = reResultParser.exec(result) || [];
- var blocked = matches.length !== 0 && matches[1] !== '@@';
+var syncDynamicFilter = function(scope, des, type, result) {
+ var el = uDom('span[data-src="' + scope + '"][data-des="' + des + '"][data-type="' + type + '"]');
+ var blocked = result.charAt(1) === 'b';
el.toggleClass('blocked', blocked);
// https://github.com/gorhill/uBlock/issues/340
// Use dark shade visual cue if the filter is specific to the page hostname
// or one of the ancestor hostname.
var ownFilter = false;
- // There might be no page hostname on pages where uBlock can't be active,
- // like on browser's built-in pages, etc.
- if ( stats.pageHostname ) {
- var filterHostname = matches[3] || '*';
- if ( stats.pageHostname.slice(0 - filterHostname.length) === filterHostname ) {
- ownFilter = (stats.pageHostname.length === filterHostname.length) ||
- (stats.pageHostname.substr(0 - filterHostname.length - 1, 1) === '.');
+ var matches = /^d[abn]:([^ ]+)/.exec(result);
+ if ( matches !== null ) {
+ var thisSrc = scope === 'local' ? stats.pageHostname : '*';
+ var otherSrc = matches[1];
+ ownFilter = thisSrc.slice(0 - otherSrc.length) === thisSrc;
+ if ( ownFilter && thisSrc.length !== otherSrc.length ) {
+ var c = thisSrc.substr(0 - otherSrc.length - 1, 1);
+ ownFilter = c === '' || c === '.';
}
}
el.toggleClass('ownFilter', ownFilter);
@@ -76,23 +75,25 @@ var syncDynamicFilter = function(scope, i, result) {
var syncAllDynamicFilters = function() {
var hasBlock = false;
- var scopes = ['.', '/'];
+ var scopes = ['*', 'local'];
var scope, results, i, result;
while ( scope = scopes.pop() ) {
if ( stats.dynamicFilterResults.hasOwnProperty(scope) === false ) {
continue;
}
results = stats.dynamicFilterResults[scope];
- i = 5;
- while ( i-- ) {
- result = results[i];
- syncDynamicFilter(scope, i + 1, result);
- if ( scope === '.' && result.length !== 0 && result.slice(0, 2) !== '@@' ) {
+ for ( var type in results ) {
+ if ( results.hasOwnProperty(type) === false ) {
+ continue;
+ }
+ result = results[type];
+ syncDynamicFilter(scope, '*', type, result);
+ if ( scope === 'local' && result.charAt(1) === 'b' ) {
hasBlock = true;
}
}
}
- uDom('#dynamicFilteringToggler').toggleClass('hasBlock', hasBlock);
+ uDom('body').toggleClass('hasDynamicBlock', hasBlock);
};
/******************************************************************************/
@@ -165,7 +166,7 @@ var renderPopup = function(details) {
uDom('#total-blocked').html(html.join(''));
uDom('#switch .fa').toggleClass('off', stats.pageURL === '' || !stats.netFilteringSwitch);
- uDom('#dynamicFilteringToggler').toggleClass('on', stats.dynamicFilteringEnabled);
+ uDom('body').toggleClass('dynamicFilteringEnabled', stats.dynamicFilteringEnabled);
};
/******************************************************************************/
@@ -174,12 +175,11 @@ var toggleNetFilteringSwitch = function(ev) {
if ( !stats || !stats.pageURL ) {
return;
}
- var off = uDom(this).toggleClass('off').hasClassName('off');
messager.send({
what: 'toggleNetFiltering',
url: stats.pageURL,
scope: ev.ctrlKey || ev.metaKey ? 'page' : '',
- state: !off,
+ state: !uDom(this).toggleClass('off').hasClass('off'),
tabId: stats.tabId
});
};
@@ -242,37 +242,35 @@ var gotoLink = function(ev) {
/******************************************************************************/
var onDynamicFilterClicked = function(ev) {
- var elScope = uDom(ev.currentTarget);
- var scope = elScope.attr('data-scope') === '/' ? '*' : stats.pageHostname;
+ // This can happen on pages where uBlock does not work
+ if ( typeof stats.pageHostname !== 'string' || stats.pageHostname === '' ) {
+ return;
+ }
var elFilter = uDom(ev.target);
+ var scope = elFilter.attr('data-src') === '*' ? '*' : stats.pageHostname;
var onDynamicFilterChanged = function(details) {
stats.dynamicFilterResults = details;
syncAllDynamicFilters();
};
messager.send({
what: 'toggleDynamicFilter',
- hostname: scope,
+ pageHostname: stats.pageHostname,
+ srcHostname: scope,
+ desHostname: elFilter.attr('data-des'),
requestType: elFilter.attr('data-type'),
- firstParty: elFilter.attr('data-first-party') !== null,
- block: elFilter.hasClassName('blocked') === false,
- pageHostname: stats.pageHostname
+ block: elFilter.hasClassName('blocked') === false
}, onDynamicFilterChanged);
-
};
/******************************************************************************/
var toggleDynamicFiltering = function(ev) {
- // Discard events destined to child elements.
- if ( ev !== undefined && ev.target !== this ) {
- return;
- }
- var el = uDom('#dynamicFilteringToggler');
- el.toggleClass('on');
+ var el = uDom('body');
+ el.toggleClass('dynamicFilteringEnabled');
messager.send({
what: 'userSettings',
name: 'dynamicFilteringEnabled',
- value: el.hasClassName('on')
+ value: el.hasClassName('dynamicFilteringEnabled')
});
};
@@ -285,7 +283,7 @@ var installEventHandlers = function() {
uDom('#gotoPick').on('click', gotoPick);
uDom('a[href^=http]').on('click', gotoLink);
uDom('#dynamicFilteringToggler').on('click', toggleDynamicFiltering);
- uDom('.dynamicFiltering').on('click', 'div', onDynamicFilterClicked);
+ uDom('#dynamicFilteringContainer').on('click', 'span[data-type]', onDynamicFilterClicked);
};
/******************************************************************************/
diff --git a/src/js/net-filtering.js b/src/js/static-net-filtering.js
index 5f9cd67..3e2647a 100644
--- a/src/js/net-filtering.js
+++ b/src/js/static-net-filtering.js
@@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
-/* jshint bitwise: false */
+/* jshint bitwise: false, esnext: true */
/* global µBlock */
// Older Safari throws an exception for const when it's used with 'use strict'.
@@ -27,7 +27,7 @@
/******************************************************************************/
-µBlock.netFilteringEngine = (function(){
+µBlock.staticNetFilteringEngine = (function(){
/******************************************************************************/
@@ -1284,7 +1284,6 @@ var FilterContainer = function() {
this.buckets = new Array(4);
this.blockedAnyPartyHostnames = new µb.LiquidDict();
this.blocked3rdPartyHostnames = new µb.LiquidDict();
- this.dynamicFilters = {};
this.filterParser = new FilterParser();
this.reset();
};
@@ -1642,193 +1641,6 @@ FilterContainer.prototype.addToCategory = function(category, tokenKey, filter) {
/******************************************************************************/
-// Dynamic filters
-
-// Bits:
-// 0: inline script blocked
-// 1: inline script allowed
-// 2: first-party script blocked
-// 3: first-party script allowed
-// 4: third-party script blocked
-// 5: third-party script allowed
-// 6: first-party frame blocked
-// 7: first-party frame allowed
-// 8: third-party frame blocked
-// 9: third-party frame allowed
-//
-// I chose separate bits for blocked/unblocked as I want to have an "undefined"
-// state, which will be used to inherit from wider-scoped filters.
-//
-// undefined: 0x00
-// blocked: 0x01
-// allowed: 0x02
-// unused: 0x03
-
-FilterContainer.prototype.dynamicFilterBitOffsets = {};
-FilterContainer.prototype.dynamicFilterBitOffsets[FirstParty | typeNameToTypeValue['inline-script']] = 0;
-FilterContainer.prototype.dynamicFilterBitOffsets[FirstParty | typeNameToTypeValue.script] = 2;
-FilterContainer.prototype.dynamicFilterBitOffsets[ThirdParty | typeNameToTypeValue.script] = 4;
-FilterContainer.prototype.dynamicFilterBitOffsets[FirstParty | typeNameToTypeValue.sub_frame] = 6;
-FilterContainer.prototype.dynamicFilterBitOffsets[ThirdParty | typeNameToTypeValue.sub_frame] = 8;
-FilterContainer.prototype.dynamicFiltersMagicId = 'numrebvoacir';
-
-/******************************************************************************/
-
-FilterContainer.prototype.dynamicFilterSet = function(hostname, requestType, firstParty, value) {
- if ( typeNameToTypeValue.hasOwnProperty(requestType) === false ) {
- return false;
- }
- var party = firstParty ? FirstParty : ThirdParty;
- var categoryKey = party | typeNameToTypeValue[requestType];
- if ( this.dynamicFilterBitOffsets.hasOwnProperty(categoryKey) === false ) {
- return false;
- }
- var bitOffset = this.dynamicFilterBitOffsets[categoryKey];
- var oldFilter = this.dynamicFilters[hostname] || 0;
- var newFilter = (oldFilter & ~(0x0003 << bitOffset)) | (value << bitOffset);
- if ( newFilter === oldFilter ) {
- return false;
- }
- if ( newFilter === 0 ) {
- delete this.dynamicFilters[hostname];
- } else {
- this.dynamicFilters[hostname] = newFilter;
- }
- return true;
-};
-
-/******************************************************************************/
-
-FilterContainer.prototype.dynamicFilterBlock = function(hostname, requestType, firstParty) {
- if ( typeof hostname !== 'string' || hostname === '' ) {
- return false;
- }
- var result = this.matchDynamicFilters(hostname, requestType, firstParty);
- if ( result !== '' && result.slice(0, 2) !== '@@' ) {
- return false;
- }
- this.dynamicFilterSet(hostname, requestType, firstParty, 0x00);
- result = this.matchDynamicFilters(hostname, requestType, firstParty);
- if ( result !== '' && result.slice(0, 2) !== '@@' ) {
- return true;
- }
- this.dynamicFilterSet(hostname, requestType, firstParty, 0x01);
- return true;
-};
-
-/******************************************************************************/
-
-FilterContainer.prototype.dynamicFilterUnblock = function(hostname, requestType, firstParty) {
- if ( typeof hostname !== 'string' || hostname === '' ) {
- return false;
- }
- var result = this.matchDynamicFilters(hostname, requestType, firstParty);
- if ( result === '' || result.slice(0, 2) === '@@' ) {
- return false;
- }
- this.dynamicFilterSet(hostname, requestType, firstParty, 0x00);
- result = this.matchDynamicFilters(hostname, requestType, firstParty);
- if ( result === '' || result.slice(0, 2) === '@@' ) {
- return true;
- }
- this.dynamicFilterSet(hostname, requestType, firstParty, 0x02);
- return true;
-};
-
-/******************************************************************************/
-
-FilterContainer.prototype.dynamicFilterStateToType = {
- 0x0001: '',
- 0x0002: '@@',
- 0x0004: '',
- 0x0008: '@@',
- 0x0010: '',
- 0x0020: '@@',
- 0x0040: '',
- 0x0080: '@@',
- 0x0100: '',
- 0x0200: '@@'
-};
-
-FilterContainer.prototype.dynamicFilterStateToOption = {
- 0x0001: '$inline-script,important',
- 0x0002: '$inline-script',
- 0x0004: '$~third-party,script,important',
- 0x0008: '$~third-party,script',
- 0x0010: '$third-party,script,important',
- 0x0020: '$third-party,script',
- 0x0040: '$~third-party,subdocument,important',
- 0x0080: '$~third-party,subdocument',
- 0x0100: '$third-party,subdocument,important',
- 0x0200: '$third-party,subdocument'
-};
-
-FilterContainer.prototype.matchDynamicFilters = function(hostname, requestType, firstParty) {
- if ( typeof hostname !== 'string' || hostname === '' ) {
- return '';
- }
- var party = firstParty ? FirstParty : ThirdParty;
- if ( typeNameToTypeValue.hasOwnProperty(requestType) === false ) {
- return '';
- }
- var categoryKey = party | typeNameToTypeValue[requestType];
- if ( this.dynamicFilterBitOffsets.hasOwnProperty(categoryKey) === false ) {
- return '';
- }
- var bitOffset = this.dynamicFilterBitOffsets[categoryKey];
- var bitMask = 0x0003 << bitOffset;
- var bitState, pos;
- for ( ;; ) {
- if ( this.dynamicFilters.hasOwnProperty(hostname) !== false ) {
- bitState = this.dynamicFilters[hostname] & bitMask;
- if ( bitState !== 0 ) {
- if ( hostname !== '*' ) {
- hostname = '||' + hostname + '^';
- }
- return this.dynamicFilterStateToType[bitState] +
- hostname +
- this.dynamicFilterStateToOption[bitState];
- }
- }
- pos = hostname.indexOf('.');
- if ( pos === -1 ) {
- if ( hostname === '*' ) {
- return '';
- }
- hostname = '*';
- } else {
- hostname = hostname.slice(pos + 1);
- }
- }
- // unreachable
-};
-
-/******************************************************************************/
-
-
-FilterContainer.prototype.selfieFromDynamicFilters = function() {
- var bin = {
- magicId: this.dynamicFiltersMagicId,
- filters: this.dynamicFilters
- };
- return JSON.stringify(bin);
-};
-
-/******************************************************************************/
-
-FilterContainer.prototype.dynamicFiltersFromSelfie = function(selfie) {
- if ( selfie === '' ) {
- return;
- }
- var bin = JSON.parse(selfie);
- if ( bin.magicId !== this.dynamicFiltersMagicId ) {
- return;
- }
- this.dynamicFilters = bin.filters;
-};
-
-/******************************************************************************/
-
// Since the addition of the `important` evaluation, this means it is now
// likely that the url will have to be scanned more than once. So this is
// to ensure we do it once only, and reuse results.
@@ -1858,15 +1670,9 @@ FilterContainer.prototype.tokenize = function(url) {
/******************************************************************************/
-FilterContainer.prototype.matchTokens = function(url) {
- var buckets = this.buckets;
- var bucket0 = buckets[0];
- var bucket1 = buckets[1];
- var bucket2 = buckets[2];
- var bucket3 = buckets[3];
-
+FilterContainer.prototype.matchTokens = function(bucket, url) {
var tokens = this.tokens;
- var tokenEntry, beg, token, f;
+ var tokenEntry, token, f;
var i = 0;
for (;;) {
tokenEntry = tokens[i++];
@@ -1874,30 +1680,9 @@ FilterContainer.prototype.matchTokens = function(url) {
if ( token === '' ) {
break;
}
- beg = tokenEntry.beg;
- if ( bucket0 !== undefined ) {
- f = bucket0[token];
- if ( f !== undefined && f.match(url, beg) !== false ) {
- return f;
- }
- }
- if ( bucket1 !== undefined ) {
- f = bucket1[token];
- if ( f !== undefined && f.match(url, beg) !== false ) {
- return f;
- }
- }
- if ( bucket2 !== undefined ) {
- f = bucket2[token];
- if ( f !== undefined && f.match(url, beg) !== false ) {
- return f;
- }
- }
- if ( bucket3 !== undefined ) {
- f = bucket3[token];
- if ( f !== undefined && f.match(url, beg) !== false ) {
- return f;
- }
+ f = bucket[token];
+ if ( f !== undefined && f.match(url, tokenEntry.beg) !== false ) {
+ return f;
}
}
return false;
@@ -1955,73 +1740,77 @@ FilterContainer.prototype.match3rdPartyHostname = function(requestHostname) {
// Some type of requests are exceptional, they need custom handling,
// not the generic handling.
-FilterContainer.prototype.matchStringExactType = function(pageDetails, requestURL, requestType) {
+FilterContainer.prototype.matchStringExactType = function(context, requestURL, requestType) {
var url = requestURL.toLowerCase();
var requestHostname = µb.URI.hostnameFromURI(requestURL);
-
- // Evaluate dynamic filters first. "Block" dynamic filters are always
- // "important", they override everything else.
- var bf = this.matchDynamicFilters(
- pageDetails.rootHostname,
- requestType,
- isFirstParty(pageDetails.rootDomain, requestHostname)
- );
- if ( bf !== '' && bf.slice(0, 2) !== '@@' ) {
- return bf;
- }
-
- var party = isFirstParty(pageDetails.pageDomain, requestHostname) ? FirstParty : ThirdParty;
+ var party = isFirstParty(context.pageDomain, requestHostname) ? FirstParty : ThirdParty;
// This will be used by hostname-based filters
- pageHostname = pageDetails.pageHostname || '';
+ pageHostname = context.pageHostname || '';
var type = typeNameToTypeValue[requestType];
var categories = this.categories;
- var buckets = this.buckets;
+ var bucket;
// Tokenize only once
this.tokenize(url);
- // We are testing for a specific type, skip "any type" buckets
- buckets[0] = buckets[1] = undefined;
-
// https://github.com/gorhill/uBlock/issues/139
// Test against important block filters
- buckets[2] = categories[this.makeCategoryKey(BlockAnyParty | Important | type)];
- buckets[3] = categories[this.makeCategoryKey(BlockAction | Important | type | party)];
- bf = this.matchTokens(url);
- if ( bf !== false ) {
- return bf.toString();
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | Important | type)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString();
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(BlockAction | Important | type | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString();
+ }
}
// Test against block filters
+ bf = false;
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | type)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
+ if ( bf === false ) {
+ if ( bucket = categories[this.makeCategoryKey(BlockAction | type | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
+ }
// If there is no block filter, no need to test against allow filters
- buckets[2] = categories[this.makeCategoryKey(BlockAnyParty | type)];
- buckets[3] = categories[this.makeCategoryKey(BlockAction | type | party)];
- bf = this.matchTokens(url);
if ( bf === false ) {
return '';
}
// Test against allow filters
- buckets[2] = categories[this.makeCategoryKey(AllowAnyParty | type)];
- buckets[3] = categories[this.makeCategoryKey(AllowAction | type | party)];
- var af = this.matchTokens(url);
- if ( af !== false ) {
- return '@@' + af.toString();
+ var af;
+ if ( bucket = categories[this.makeCategoryKey(AllowAnyParty | type)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(AllowAction | type | party)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
}
- return bf.toString();
+ return 'sb:' + bf.toString();
};
/******************************************************************************/
-FilterContainer.prototype.matchString = function(pageDetails, requestURL, requestType) {
+FilterContainer.prototype.matchString = function(context) {
// https://github.com/gorhill/httpswitchboard/issues/239
// Convert url to lower case:
// `match-case` option not supported, but then, I saw only one
// occurrence of it in all the supported lists (bulgaria list).
- var url = requestURL.toLowerCase();
+ var url = context.requestURL.toLowerCase();
// The logic here is simple:
//
@@ -2044,27 +1833,15 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// filters are tested *only* if there is a (unlikely) hit on a block
// filter.
- var requestHostname = µb.URI.hostnameFromURI(requestURL);
-
- // Evaluate dynamic filters first. "Block" dynamic filters are always
- // "important", they override everything else.
- var bf = this.matchDynamicFilters(
- pageDetails.rootHostname,
- requestType,
- isFirstParty(pageDetails.rootDomain, requestHostname)
- );
- if ( bf !== '' && bf.slice(0, 2) !== '@@' ) {
- return bf;
- }
-
- var party = isFirstParty(pageDetails.pageDomain, requestHostname) ? FirstParty : ThirdParty ;
+ var requestHostname = context.requestHostname;
+ var party = isFirstParty(context.pageDomain, requestHostname) ? FirstParty : ThirdParty;
// This will be used by hostname-based filters
- pageHostname = pageDetails.pageHostname || '';
+ pageHostname = context.pageHostname || '';
- var type = typeNameToTypeValue[requestType];
+ var type = typeNameToTypeValue[context.requestType];
var categories = this.categories;
- var buckets = this.buckets;
+ var bucket;
// Tokenize only once
this.tokenize(url);
@@ -2074,13 +1851,29 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// The purpose of the `important` option is to reverse the order of
// evaluation. Normally, it is "evaluate block then evaluate allow", with
// the `important` property it is "evaluate allow then evaluate block".
- buckets[0] = categories[this.makeCategoryKey(BlockAnyTypeAnyParty | Important)];
- buckets[1] = categories[this.makeCategoryKey(BlockAnyType | Important | party)];
- buckets[2] = categories[this.makeCategoryKey(BlockAnyParty | Important | type)];
- buckets[3] = categories[this.makeCategoryKey(BlockAction | Important | type | party)];
- bf = this.matchTokens(url);
- if ( bf !== false ) {
- return bf.toString() + '$important';
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyTypeAnyParty | Important)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString() + '$important';
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyType | Important | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString() + '$important';
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | Important | type)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString() + '$important';
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(BlockAction | Important | type | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ if ( bf !== false ) {
+ return 'sb:' + bf.toString() + '$important';
+ }
}
// Test hostname-based block filters
@@ -2091,11 +1884,24 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
// Test against block filters
if ( bf === false ) {
- buckets[0] = categories[this.makeCategoryKey(BlockAnyTypeAnyParty)];
- buckets[1] = categories[this.makeCategoryKey(BlockAnyType | party)];
- buckets[2] = categories[this.makeCategoryKey(BlockAnyParty | type)];
- buckets[3] = categories[this.makeCategoryKey(BlockAction | type | party)];
- bf = this.matchTokens(url);
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyTypeAnyParty)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
+ }
+ if ( bf === false ) {
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyType | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
+ }
+ if ( bf === false ) {
+ if ( bucket = categories[this.makeCategoryKey(BlockAnyParty | type)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
+ }
+ if ( bf === false ) {
+ if ( bucket = categories[this.makeCategoryKey(BlockAction | type | party)] ) {
+ bf = this.matchTokens(bucket, url);
+ }
}
// If there is no block filter, no need to test against allow filters
@@ -2104,16 +1910,33 @@ FilterContainer.prototype.matchString = function(pageDetails, requestURL, reques
}
// Test against allow filters
- buckets[0] = categories[this.makeCategoryKey(AllowAnyTypeAnyParty)];
- buckets[1] = categories[this.makeCategoryKey(AllowAnyType | party)];
- buckets[2] = categories[this.makeCategoryKey(AllowAnyParty | type)];
- buckets[3] = categories[this.makeCategoryKey(AllowAction | type | party)];
- var af = this.matchTokens(url);
- if ( af !== false ) {
- return '@@' + af.toString();
+ var af;
+ if ( bucket = categories[this.makeCategoryKey(AllowAnyTypeAnyParty)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(AllowAnyType | party)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(AllowAnyParty | type)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
+ }
+ if ( bucket = categories[this.makeCategoryKey(AllowAction | type | party)] ) {
+ af = this.matchTokens(bucket, url);
+ if ( af !== false ) {
+ return 'sa:' + af.toString();
+ }
}
- return bf.toString();
+ return 'sb:' + bf.toString();
};
/******************************************************************************/
diff --git a/src/js/stats.js b/src/js/stats.js
index 9eb3b18..df5a924 100644
--- a/src/js/stats.js
+++ b/src/js/stats.js
@@ -21,12 +21,13 @@
/* jshint bitwise: false */
/* global uDom */
-'use strict';
/******************************************************************************/
(function() {
+'use strict';
+
/******************************************************************************/
var messager = vAPI.messaging.channel('stats.js');
@@ -77,8 +78,8 @@ var renderURL = function(url, filter) {
if ( pos > 0 ) {
reText = reText.slice(0, pos);
}
- if ( reText.slice(0, 2) === '@@' ) {
- reText = reText.slice(2);
+ if ( reText.charAt(0) === 's' ) {
+ reText = reText.slice(3);
}
if ( reText === '*' ) {
reText = '\\*';
diff --git a/src/js/storage.js b/src/js/storage.js
index 6947508..b672722 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -149,7 +149,7 @@
return;
}
µb.mergeFilterText(content);
- µb.netFilteringEngine.freeze();
+ µb.staticNetFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
µb.destroySelfie();
µb.toSelfieAsync();
@@ -278,7 +278,7 @@
}
var loadBlacklistsEnd = function() {
- µb.netFilteringEngine.freeze();
+ µb.staticNetFilteringEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists });
vAPI.messaging.broadcast({ what: 'loadUbiquitousBlacklistCompleted' });
@@ -296,7 +296,7 @@
var loadBlacklistsStart = function(lists) {
µb.remoteBlacklists = lists;
- µb.netFilteringEngine.reset();
+ µb.staticNetFilteringEngine.reset();
µb.cosmeticFilteringEngine.reset();
µb.destroySelfie();
var locations = Object.keys(lists);
@@ -328,17 +328,17 @@
µBlock.mergeFilterList = function(details) {
// console.log('µBlock > mergeFilterList from "%s": "%s..."', details.path, details.content.slice(0, 40));
- var netFilteringEngine = this.netFilteringEngine;
+ var staticNetFilteringEngine = this.staticNetFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
- var duplicateCount = netFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount;
- var acceptedCount = netFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount;
+ var duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount;
+ var acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount;
this.mergeFilterText(details.content);
// For convenience, store the number of entries for this
// blacklist, user might be happy to know this information.
- duplicateCount = netFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount - duplicateCount;
- acceptedCount = netFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount - acceptedCount;
+ duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount - duplicateCount;
+ acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount - acceptedCount;
var filterListMeta = this.remoteBlacklists[details.path];
@@ -363,7 +363,7 @@
// Useful references:
// https://adblockplus.org/en/filter-cheatsheet
// https://adblockplus.org/en/filters
- var netFilteringEngine = this.netFilteringEngine;
+ var staticNetFilteringEngine = this.staticNetFilteringEngine;
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
var parseCosmeticFilters = this.userSettings.parseAllABPHideFilters;
@@ -433,7 +433,7 @@
continue;
}
- netFilteringEngine.add(matches[0]);
+ staticNetFilteringEngine.add(matches[0]);
}
};
@@ -520,7 +520,7 @@
magic: this.selfieMagic,
publicSuffixList: publicSuffixList.toSelfie(),
filterLists: this.remoteBlacklists,
- netFilteringEngine: this.netFilteringEngine.toSelfie(),
+ staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(),
cosmeticFilteringEngine: this.cosmeticFilteringEngine.toSelfie()
};
vAPI.storage.set({ selfie: selfie });
@@ -565,7 +565,7 @@
}
// console.log('µBlock.fromSelfie> selfie looks good');
µb.remoteBlacklists = selfie.filterLists;
- µb.netFilteringEngine.fromSelfie(selfie.netFilteringEngine);
+ µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine);
callback(true);
};
@@ -649,12 +649,21 @@
// Important: block remote fetching for when loading assets at launch
// time.
µb.assets.allowRemoteFetch = false;
-
µb.assets.autoUpdate = settings.autoUpdate;
- µb.netFilteringEngine.dynamicFiltersFromSelfie(settings.dynamicFilteringSelfie);
µb.fromSelfie(onSelfieReady);
µb.mirrors.toggle(settings.experimentalEnabled);
µb.contextMenu.toggle(settings.contextMenuEnabled);
+
+ if ( typeof settings.dynamicFilteringSelfie === 'string' ) {
+ if ( settings.dynamicFilteringString === '' && settings.dynamicFilteringSelfie !== '' ) {
+ µb.dynamicNetFilteringEngine.fromObsoleteSelfie(settings.dynamicFilteringSelfie);
+ µb.userSettings.dynamicFilteringString = µb.dynamicNetFilteringEngine.toString();
+ µb.XAL.keyvalSetOne('dynamicFilteringString', µb.userSettings.dynamicFilteringString);
+ }
+ delete µb.userSettings.dynamicFilteringSelfie;
+ µb.XAL.keyvalRemoveOne('dynamicFilteringSelfie');
+ }
+ µb.dynamicNetFilteringEngine.fromString(µb.userSettings.dynamicFilteringString);
};
this.loadUserSettings(onUserSettingsReady);
diff --git a/src/js/tab.js b/src/js/tab.js
index 3b3a2f7..048548b 100644
--- a/src/js/tab.js
+++ b/src/js/tab.js
@@ -55,6 +55,8 @@ vAPI.tabs.onClosed = function(tabId) {
// https://github.com/gorhill/uBlock/issues/297
vAPI.tabs.onPopup = function(details) {
+ //console.debug('vAPI.tabs.onPopup: url="%s"', details.url);
+
var pageStore = µBlock.pageStoreFromTabId(details.sourceTabId);
if ( !pageStore ) {
return;
@@ -65,7 +67,7 @@ vAPI.tabs.onPopup = function(details) {
// https://github.com/gorhill/uBlock/issues/323
// If popup URL is whitelisted, do not block it
if ( µBlock.getNetFilteringSwitch(requestURL) ) {
- result = µBlock.netFilteringEngine.matchStringExactType(pageStore, requestURL, 'popup');
+ result = µBlock.staticNetFilteringEngine.matchStringExactType(pageStore, requestURL, 'popup');
}
// https://github.com/gorhill/uBlock/issues/91
diff --git a/src/js/traffic.js b/src/js/traffic.js
index 72d7f59..b4c86fb 100644
--- a/src/js/traffic.js
+++ b/src/js/traffic.js
@@ -80,7 +80,6 @@ var onBeforeRequest = function(details) {
// https://github.com/gorhill/uBlock/issues/114
var requestContext = pageStore;
-
var frameStore;
var frameId = details.frameId;
if ( frameId > 0 ) {
@@ -89,7 +88,12 @@ var onBeforeRequest = function(details) {
}
}
- var result = pageStore.filterRequest(requestContext, requestType, requestURL);
+ // Setup context and evaluate
+ requestContext.requestURL = requestURL;
+ requestContext.requestHostname = µb.URI.hostnameFromURI(requestURL);
+ requestContext.requestType = requestType;
+
+ var result = pageStore.filterRequest(requestContext);
// Not blocked
if ( pageStore.boolFromResult(result) === false ) {
@@ -208,11 +212,12 @@ var onBeforeSendHeaders = function(details) {
var pageDetails = {
pageHostname: referrerHostname,
pageDomain: µburi.domainFromHostname(referrerHostname),
+ firstParty: false
};
pageDetails.rootHostname = pageDetails.pageHostname;
pageDetails.rootDomain = pageDetails.pageDomain;
//console.debug('Referrer="%s"', referrer);
- var result = µb.netFilteringEngine.matchStringExactType(pageDetails, requestURL, 'popup');
+ var result = µb.staticNetFilteringEngine.matchStringExactType(pageDetails, requestURL, 'popup');
// Not blocked?
if ( result === '' || result.slice(0, 2) === '@@' ) {
@@ -251,22 +256,14 @@ var onHeadersReceived = function(details) {
// https://github.com/gorhill/uBlock/issues/384
pageStore.skipLocalMirroring = headerValue(details.responseHeaders, 'content-security-policy');
-
- var result = '';
- if ( pageStore.getNetFilteringSwitch() ) {
- result = µb.netFilteringEngine.matchStringExactType(pageStore, details.url, 'inline-script');
- }
-
- // Not blocked?
- if ( result === '' || result.slice(0, 2) === '@@' ) {
+ pageStore.requestURL = details.url;
+ pageStore.requestHostname = µb.URI.hostnameFromURI(details.url);
+ pageStore.requestType = 'inline-script';
+ var result = pageStore.filterRequest(pageStore);
+ if ( result === '' ) {
return;
}
- // Record request
- if ( result !== '' ) {
- pageStore.recordResult('script', details.url, result);
- }
-
// Blocked
pageStore.perLoadBlockedRequestCount++;
µb.localSettings.blockedRequestCount++;
diff --git a/src/js/ublock.js b/src/js/ublock.js
index ea1fc33..82bbe95 100644
--- a/src/js/ublock.js
+++ b/src/js/ublock.js
@@ -309,23 +309,88 @@ var matchWhitelistDirective = function(url, hostname, directive) {
µBlock.toggleDynamicFilter = function(details) {
var changed = false;
if ( details.block ) {
- changed = this.netFilteringEngine.dynamicFilterBlock(details.hostname, details.requestType, details.firstParty);
+ changed = this.dynamicNetFilteringEngine.blockCell(details.srcHostname, details.desHostname, details.requestType);
} else {
- changed = this.netFilteringEngine.dynamicFilterUnblock(details.hostname, details.requestType, details.firstParty);
+ changed = this.dynamicNetFilteringEngine.unsetCell(details.srcHostname, details.desHostname, details.requestType);
}
if ( !changed ) {
return;
}
- this.userSettings.dynamicFilteringSelfie = this.netFilteringEngine.selfieFromDynamicFilters();
- this.XAL.keyvalSetOne('dynamicFilteringSelfie', this.userSettings.dynamicFilteringSelfie);
+ this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString();
+ this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString);
// https://github.com/gorhill/uBlock/issues/420
- if ( details.requestType === 'sub_frame' && !details.block ) {
+ if ( details.requestType === '3p-frame' && !details.block ) {
this.cosmeticFilteringEngine.removeFromSelectorCache(details.hostname, 'net');
}
};
/******************************************************************************/
+µBlock.isFirstParty = function(firstPartyDomain, hostname) {
+ if ( hostname.slice(0 - firstPartyDomain.length) !== firstPartyDomain ) {
+ return false;
+ }
+ // Be sure to not confuse 'example.com' with 'anotherexample.com'
+ var c = hostname.charAt(hostname.length - firstPartyDomain.length - 1);
+ return c === '.' || c === '';
+};
+
+/******************************************************************************/
+
+// The core logic to evaluate requests through dynamic/static filtering
+// is here.
+
+µBlock.filterRequest = function(context) {
+ // Given that:
+ // - Dynamic filtering override static filtering
+ // - Evaluating dynamic filtering is much faster than static filtering
+ // We evaluate dynamic filtering first, and hopefully we can skip
+ // evaluation of static filtering.
+ // Dynamic filtering evaluation is ordered from most-specific to least-
+ // specific.
+ var df = this.dynamicNetFilteringEngine;
+ var rootHostname = context.rootHostname;
+ var requestHostname = context.requestHostname;
+ var requestType = context.requestType;
+
+ // Dynamic filters:
+ // 1. specific source, specific destination, any type, allow/block
+ // 2. any source, specific destination, any type, allow/block
+ df.evaluateCellZY(rootHostname, requestHostname, '*');
+ if ( df.mustBlockOrAllow() ) {
+ return df.toFilterString();
+ }
+
+ // Dynamic filters:
+ // 3. specific source, any destination, specific type, allow/block
+ // 4. any source, any destination, specific type, allow/block
+ if ( requestType === 'script' ) {
+ df.evaluateCellZY(rootHostname, requestHostname, this.isFirstParty(rootHostname, requestHostname) ? '1p-script' : '3p-script');
+ if ( df.mustBlockOrAllow() ) {
+ return df.toFilterString();
+ }
+ }
+
+ if ( requestType === 'sub_frame' ) {
+ if ( this.isFirstParty(rootHostname, requestHostname) === false ) {
+ df.evaluateCellZY(rootHostname, requestHostname, '3p-frame');
+ if ( df.mustBlockOrAllow() ) {
+ return df.toFilterString();
+ }
+ }
+ }
+
+ df.evaluateCellZY(rootHostname, requestHostname, requestType);
+ if ( df.mustBlockOrAllow() ) {
+ return df.toFilterString();
+ }
+
+ // 5. Static filtering never override dynamic filtering
+ return this.staticNetFilteringEngine.matchString(context);
+};
+
+/******************************************************************************/
+
})(); \ No newline at end of file
diff --git a/src/js/xal.js b/src/js/xal.js
index 6d3366b..a8bf273 100644
--- a/src/js/xal.js
+++ b/src/js/xal.js
@@ -48,6 +48,12 @@ exports.keyvalSetMany = function(dict, callback) {
/******************************************************************************/
+exports.keyvalRemoveOne = function(key, callback) {
+ vAPI.storage.remove(key, callback || noopFunc);
+};
+
+/******************************************************************************/
+
exports.keyvalRemoveAll = function(callback) {
vAPI.storage.clear(callback || noopFunc);
};
diff --git a/src/popup.html b/src/popup.html
index e411eb2..952232f 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -11,9 +11,17 @@
<body>
<h4 title="popupTipDashboard">v<span id="version"></span></h4>
<div>
+ <div id="dynamicFilteringContainer">
+ <div><span>images</span><span data-src="*" data-des="*" data-type="image"></span><span data-src="local" data-des="*" data-type="image"></span></div>
+ <div><span>inline scripts</span><span data-src="*" data-des="*" data-type="inline-script"></span><span data-src="local" data-des="*" data-type="inline-script"></span></div>
+ <div><span>1st-party scripts</span><span data-src="*" data-des="*" data-type="1p-script"></span><span data-src="local" data-des="*" data-type="1p-script"></span></div>
+ <div><span>3rd-party scripts</span><span data-src="*" data-des="*" data-type="3p-script"></span><span data-src="local" data-des="*" data-type="3p-script"></span></div>
+ <div><span>3rd-party frames</span><span data-src="*" data-des="*" data-type="3p-frame"></span><span data-src="local" data-des="*" data-type="3p-frame"></span></div>
+ </div>
+</div><!-- DO NOT REMOVE --><div>
<p id="switch" data-i18n-tip="popupPowerSwitchInfo"><span class="fa">&#xf011;</span></p>
<p id="switch-hint"></p>
- <p style="font-size: 16px;" data-i18n="popupBlockedRequestPrompt"></p>
+ <p id="dynamicFilteringToggler" data-i18n="popupBlockedRequestPrompt"></p>
<p id="stats">
<span data-i18n="popupBlockedOnThisPagePrompt"></span>&ensp;
<span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-anchor="top">&#xf1fb;</span>&ensp;
@@ -24,65 +32,6 @@
<p id="total-blocked">?</p>
</div>
-<div id="dynamicFilteringToggler" class="fa">
- <div></div>
- <div></div>
- <div></div>
- <div></div>
- <a href="https://github.com/gorhill/uBlock/wiki/Dynamic-filtering" target="_blank">?</a>
- </div>
-
-
-<div id="dynamicFilteringContainer">
- <div class="dynamicFiltering local" data-scope=".">
- <div data-first-party data-type="inline-script">
- <div class="tip" data-i18n="popupSiteInlineScriptEnabled"></div>
- <div class="tip" data-i18n="popupSiteInlineScriptDisabled"></div>
- </div>
- <div data-first-party data-type="script">
- <div class="tip" data-i18n="popupSite1pScriptEnabled"></div>
- <div class="tip" data-i18n="popupSite1pScriptDisabled"></div>
- </div>
- <div data-type="script">
- <div class="tip" data-i18n="popupSite3pScriptEnabled"></div>
- <div class="tip" data-i18n="popupSite3pScriptDisabled"></div>
- </div>
- <div data-first-party data-type="sub_frame">
- <div class="tip" data-i18n="popupSite1pFrameEnabled"></div>
- <div class="tip" data-i18n="popupSite1pFrameDisabled"></div>
- </div>
- <div data-type="sub_frame">
- <div class="tip" data-i18n="popupSite3pFrameEnabled"></div>
- <div class="tip" data-i18n="popupSite3pFrameDisabled"></div>
- </div>
- <div class="label">&lt;script&gt;</div>
- <div class="label">&lt;iframe&gt;</div>
- </div>
- <div class="dynamicFiltering global" data-scope="/">
- <div data-first-party data-type="inline-script">
- <div class="tip" data-i18n="popupDefaultInlineScriptEnabled"></div>
- <div class="tip" data-i18n="popupDefaultInlineScriptDisabled"></div>
- </div>
- <div data-first-party data-type="script">
- <div class="tip" data-i18n="popupDefault1pScriptEnabled"></div>
- <div class="tip" data-i18n="popupDefault1pScriptDisabled"></div>
- </div>
- <div data-type="script">
- <div class="tip" data-i18n="popupDefault3pScriptEnabled"></div>
- <div class="tip" data-i18n="popupDefault3pScriptDisabled"></div>
- </div>
- <div data-first-party data-type="sub_frame">
- <div class="tip" data-i18n="popupDefault1pFrameEnabled"></div>
- <div class="tip" data-i18n="popupDefault1pFrameDisabled"></div>
- </div>
- <div data-type="sub_frame">
- <div class="tip" data-i18n="popupDefault3pFrameEnabled"></div>
- <div class="tip" data-i18n="popupDefault3pFrameDisabled"></div>
- </div>
- <div class="label">&lt;script&gt;</div>
- <div class="label">&lt;iframe&gt;</div>
- </div>
- </div>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
<script src="js/udom.js"></script>