mirror of https://github.com/gorhill/uBlock.git
refactor where appropriate to make use of ES6 Set/Map (#1070)
At the same time, the following issues were fixed: - #1954: automatically lookup site-specific scriptlets - https://github.com/uBlockOrigin/uAssets/issues/23
This commit is contained in:
parent
aa20b6185d
commit
a7fe367eec
|
@ -0,0 +1,214 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2014-2016 The uBlock Origin authors
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For background page or non-background pages
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/1067
|
||||||
|
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
|
||||||
|
// Firefox 17/Chromium 41 supports `startsWith`.
|
||||||
|
|
||||||
|
if ( String.prototype.startsWith instanceof Function === false ) {
|
||||||
|
String.prototype.startsWith = function(needle, pos) {
|
||||||
|
if ( typeof pos !== 'number' ) {
|
||||||
|
pos = 0;
|
||||||
|
}
|
||||||
|
return this.lastIndexOf(needle, pos) === pos;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/1067
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
|
||||||
|
// Firefox 17/Chromium 41 supports `endsWith`.
|
||||||
|
|
||||||
|
if ( String.prototype.endsWith instanceof Function === false ) {
|
||||||
|
String.prototype.endsWith = function(needle, pos) {
|
||||||
|
if ( typeof pos !== 'number' ) {
|
||||||
|
pos = this.length;
|
||||||
|
}
|
||||||
|
pos -= needle.length;
|
||||||
|
return this.indexOf(needle, pos) === pos;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/1070
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility
|
||||||
|
// This polyfill is designed to fulfill *only* what uBlock Origin needs -- this
|
||||||
|
// is not an accurate API of the real Set() type.
|
||||||
|
|
||||||
|
if ( self.Set instanceof Function === false ) {
|
||||||
|
self.Set = function(iter) {
|
||||||
|
this.clear();
|
||||||
|
if ( Array.isArray(iter) ) {
|
||||||
|
for ( var i = 0, n = iter.length; i < n; i++ ) {
|
||||||
|
this.add(iter[i]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.polyfill = true;
|
||||||
|
|
||||||
|
self.Set.prototype.clear = function() {
|
||||||
|
this._set = Object.create(null);
|
||||||
|
this.size = 0;
|
||||||
|
// Iterator stuff
|
||||||
|
this._values = undefined;
|
||||||
|
this._i = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.prototype.add = function(k) {
|
||||||
|
if ( this._set[k] === undefined ) {
|
||||||
|
this._set[k] = true;
|
||||||
|
this.size += 1;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.prototype.delete = function(k) {
|
||||||
|
if ( this._set[k] !== undefined ) {
|
||||||
|
delete this._set[k];
|
||||||
|
this.size -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.prototype.has = function(k) {
|
||||||
|
return this._set[k] !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.prototype.next = function() {
|
||||||
|
if ( this._i < this.size ) {
|
||||||
|
this.value = this._values[this._i++];
|
||||||
|
} else {
|
||||||
|
this._values = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Set.prototype.values = function() {
|
||||||
|
this._values = Object.keys(this._set);
|
||||||
|
this._i = 0;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = false;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/gorhill/uBlock/issues/1070
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility
|
||||||
|
// This polyfill is designed to fulfill *only* what uBlock Origin needs -- this
|
||||||
|
// is not an accurate API of the real Map() type.
|
||||||
|
|
||||||
|
if ( self.Map instanceof Function === false ) {
|
||||||
|
self.Map = function(iter) {
|
||||||
|
this.clear();
|
||||||
|
if ( Array.isArray(iter) ) {
|
||||||
|
for ( var i = 0, n = iter.length, entry; i < n; i++ ) {
|
||||||
|
entry = iter[i];
|
||||||
|
this.set(entry[0], entry[1]);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.polyfill = true;
|
||||||
|
|
||||||
|
self.Map.prototype.clear = function() {
|
||||||
|
this._map = Object.create(null);
|
||||||
|
this.size = 0;
|
||||||
|
// Iterator stuff
|
||||||
|
this._keys = undefined;
|
||||||
|
this._i = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.delete = function(k) {
|
||||||
|
if ( this._map[k] !== undefined ) {
|
||||||
|
delete this._map[k];
|
||||||
|
this.size -= 1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.entries = function() {
|
||||||
|
this._keys = Object.keys(this._map);
|
||||||
|
this._i = 0;
|
||||||
|
this.value = [ undefined, undefined ];
|
||||||
|
this.done = false;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.get = function(k) {
|
||||||
|
return this._map[k];
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.has = function(k) {
|
||||||
|
return this._map[k] !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.next = function() {
|
||||||
|
if ( this._i < this.size ) {
|
||||||
|
var key = this._keys[this._i++];
|
||||||
|
this.value[0] = key;
|
||||||
|
this.value[1] = this._map[key];
|
||||||
|
} else {
|
||||||
|
this._keys = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
self.Map.prototype.set = function(k, v) {
|
||||||
|
if ( v !== undefined ) {
|
||||||
|
if ( this._map[k] === undefined ) {
|
||||||
|
this.size += 1;
|
||||||
|
}
|
||||||
|
this._map[k] = v;
|
||||||
|
} else {
|
||||||
|
if ( this._map[k] !== undefined ) {
|
||||||
|
this.size -= 1;
|
||||||
|
}
|
||||||
|
delete this._map[k];
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a browser extension to block requests.
|
||||||
|
Copyright (C) 2016 The uBlock Origin authors
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For background page or non-background pages
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// Patching for Pale Moon which does not implement ES6 Set/Map.
|
||||||
|
// Test for non-ES6 Set/Map: check if property `iterator` is present.
|
||||||
|
// The code is strictly to satisfy uBO's core, not to be an accurate
|
||||||
|
// implementation of ES6.
|
||||||
|
|
||||||
|
if ( self.Set.prototype.iterator instanceof Function ) {
|
||||||
|
//console.log('Patching non-ES6 Set() to be more ES6-like.');
|
||||||
|
self.Set.prototype._values = self.Set.prototype.values;
|
||||||
|
self.Set.prototype.values = function() {
|
||||||
|
this._valueIter = this._values();
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = false;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
self.Set.prototype.next = function() {
|
||||||
|
try {
|
||||||
|
this.value = this._valueIter.next();
|
||||||
|
} catch (ex) {
|
||||||
|
this._valueIter = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( self.Map.prototype.iterator instanceof Function ) {
|
||||||
|
//console.log('Patching non-ES6 Map() to be more ES6-like.');
|
||||||
|
self.Map.prototype._entries = self.Map.prototype.entries;
|
||||||
|
self.Map.prototype.entries = function() {
|
||||||
|
this._entryIter = this._entries();
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = false;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
self.Map.prototype.next = function() {
|
||||||
|
try {
|
||||||
|
this.value = this._entryIter.next();
|
||||||
|
} catch (ex) {
|
||||||
|
this._entryIter = undefined;
|
||||||
|
this.value = undefined;
|
||||||
|
this.done = true;
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
@ -5,10 +5,10 @@
|
||||||
<title>uBlock Origin</title>
|
<title>uBlock Origin</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<script src="js/polyfill.js"></script>
|
||||||
<script src="lib/punycode.js"></script>
|
<script src="lib/punycode.js"></script>
|
||||||
<script src="lib/publicsuffixlist.js"></script>
|
<script src="lib/publicsuffixlist.js"></script>
|
||||||
<script src="lib/yamd5.js"></script>
|
<script src="lib/yamd5.js"></script>
|
||||||
<script src="js/vapi-polyfill.js"></script>
|
|
||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-background.js"></script>
|
<script src="js/vapi-background.js"></script>
|
||||||
<script src="js/background.js"></script>
|
<script src="js/background.js"></script>
|
||||||
|
|
|
@ -93,8 +93,8 @@ return {
|
||||||
|
|
||||||
// read-only
|
// read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 'splsmclwnvoj',
|
compiledMagic: 'ryegxvatkfxe',
|
||||||
selfieMagic: 'rkzqonintytj'
|
selfieMagic: 'ryegxvatkfxe'
|
||||||
},
|
},
|
||||||
|
|
||||||
restoreBackupSettings: {
|
restoreBackupSettings: {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -987,166 +987,24 @@ FilterRegexHostname.fromSelfie = function(s) {
|
||||||
|
|
||||||
// Dictionary of hostnames
|
// Dictionary of hostnames
|
||||||
//
|
//
|
||||||
// FilterHostnameDict is the main reason why uBlock is not equipped to keep
|
|
||||||
// track of which filter comes from which list, and also why it's not equipped
|
|
||||||
// to be able to disable a specific filter -- other than through using a
|
|
||||||
// counter-filter.
|
|
||||||
//
|
|
||||||
// On the other hand it is also *one* of the reason uBlock's memory and CPU
|
|
||||||
// footprint is smaller. Compacting huge list of hostnames into single strings
|
|
||||||
// saves a lot of memory compared to having one dictionary entry per hostname.
|
|
||||||
|
|
||||||
var FilterHostnameDict = function() {
|
var FilterHostnameDict = function() {
|
||||||
this.h = ''; // short-lived register
|
this.h = ''; // short-lived register
|
||||||
this.dict = {};
|
this.dict = new Set();
|
||||||
this.count = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Somewhat arbitrary: I need to come up with hard data to know at which
|
|
||||||
// point binary search is better than indexOf.
|
|
||||||
//
|
|
||||||
// http://jsperf.com/string-indexof-vs-binary-search
|
|
||||||
// Tuning above performance benchmark, it appears 250 is roughly a good value
|
|
||||||
// for both Chromium/Firefox.
|
|
||||||
// Example of benchmark values: '------30', '-----100', etc. -- the
|
|
||||||
// needle string must always be 8-character long.
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.cutoff = 250;
|
|
||||||
|
|
||||||
// Probably not needed under normal circumstances.
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.meltBucket = function(len, bucket) {
|
|
||||||
var map = {};
|
|
||||||
if ( bucket.startsWith(' ') ) {
|
|
||||||
bucket.trim().split(' ').map(function(k) {
|
|
||||||
map[k] = true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
var offset = 0;
|
|
||||||
while ( offset < bucket.length ) {
|
|
||||||
map[bucket.substr(offset, len)] = true;
|
|
||||||
offset += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
};
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.freezeBucket = function(bucket) {
|
|
||||||
var hostnames = Object.keys(bucket);
|
|
||||||
if ( hostnames[0].length * hostnames.length < this.cutoff ) {
|
|
||||||
return ' ' + hostnames.join(' ') + ' ';
|
|
||||||
}
|
|
||||||
return hostnames.sort().join('');
|
|
||||||
};
|
|
||||||
|
|
||||||
// How the key is derived dictates the number and size of buckets:
|
|
||||||
// - more bits = more buckets = higher memory footprint
|
|
||||||
// - less bits = less buckets = lower memory footprint
|
|
||||||
// - binary search mitigates very well the fact that some buckets may grow
|
|
||||||
// large when fewer bits are used (or when a large number of items are
|
|
||||||
// stored). Binary search also mitigate to the point of non-issue the
|
|
||||||
// CPU footprint requirement with large buckets, as far as reference
|
|
||||||
// benchmark shows.
|
|
||||||
//
|
|
||||||
// A hash key capable of better spread while being as fast would be
|
|
||||||
// just great.
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.makeKey = function(hn) {
|
|
||||||
var len = hn.length;
|
|
||||||
if ( len > 255 ) {
|
|
||||||
len = 255;
|
|
||||||
}
|
|
||||||
var i8 = len >>> 3;
|
|
||||||
var i4 = len >>> 2;
|
|
||||||
var i2 = len >>> 1;
|
|
||||||
|
|
||||||
// http://jsperf.com/makekey-concat-vs-join/3
|
|
||||||
|
|
||||||
// Be sure the msb is not set, this will guarantee a valid unicode
|
|
||||||
// character (because 0xD800-0xDFFF).
|
|
||||||
return String.fromCharCode(
|
|
||||||
(hn.charCodeAt( i8) & 0x01) << 14 |
|
|
||||||
// (hn.charCodeAt( i4 ) & 0x01) << 13 |
|
|
||||||
(hn.charCodeAt( i4+i8) & 0x01) << 12 |
|
|
||||||
(hn.charCodeAt(i2 ) & 0x01) << 11 |
|
|
||||||
(hn.charCodeAt(i2 +i8) & 0x01) << 10 |
|
|
||||||
// (hn.charCodeAt(i2+i4 ) & 0x01) << 9 |
|
|
||||||
(hn.charCodeAt(i2+i4+i8) & 0x01) << 8 ,
|
|
||||||
len
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterHostnameDict.prototype.add = function(hn) {
|
FilterHostnameDict.prototype.add = function(hn) {
|
||||||
var key = this.makeKey(hn);
|
if ( this.dict.has(hn) ) {
|
||||||
var bucket = this.dict[key];
|
|
||||||
if ( bucket === undefined ) {
|
|
||||||
bucket = this.dict[key] = {};
|
|
||||||
bucket[hn] = true;
|
|
||||||
this.count += 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ( typeof bucket === 'string' ) {
|
|
||||||
bucket = this.dict[key] = this.meltBucket(hn.length, bucket);
|
|
||||||
}
|
|
||||||
if ( bucket.hasOwnProperty(hn) ) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bucket[hn] = true;
|
this.dict.add(hn);
|
||||||
this.count += 1;
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterHostnameDict.prototype.freeze = function() {
|
|
||||||
var buckets = this.dict;
|
|
||||||
var bucket;
|
|
||||||
for ( var key in buckets ) {
|
|
||||||
bucket = buckets[key];
|
|
||||||
if ( typeof bucket === 'object' ) {
|
|
||||||
buckets[key] = this.freezeBucket(bucket);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.matchesExactly = function(hn) {
|
|
||||||
// TODO: Handle IP address
|
|
||||||
|
|
||||||
var key = this.makeKey(hn);
|
|
||||||
var bucket = this.dict[key];
|
|
||||||
if ( bucket === undefined ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( typeof bucket === 'object' ) {
|
|
||||||
bucket = this.dict[key] = this.freezeBucket(bucket);
|
|
||||||
}
|
|
||||||
if ( bucket.startsWith(' ') ) {
|
|
||||||
return bucket.indexOf(' ' + hn + ' ') !== -1;
|
|
||||||
}
|
|
||||||
// binary search
|
|
||||||
var len = hn.length;
|
|
||||||
var left = 0;
|
|
||||||
// http://jsperf.com/or-vs-floor/17
|
|
||||||
var right = (bucket.length / len + 0.5) | 0;
|
|
||||||
var i, needle;
|
|
||||||
while ( left < right ) {
|
|
||||||
i = left + right >> 1;
|
|
||||||
needle = bucket.substr( len * i, len );
|
|
||||||
if ( hn < needle ) {
|
|
||||||
right = i;
|
|
||||||
} else if ( hn > needle ) {
|
|
||||||
left = i + 1;
|
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
FilterHostnameDict.prototype.match = function() {
|
FilterHostnameDict.prototype.match = function() {
|
||||||
// TODO: mind IP addresses
|
// TODO: mind IP addresses
|
||||||
|
|
||||||
var pos,
|
var pos,
|
||||||
hostname = requestHostnameRegister;
|
hostname = requestHostnameRegister;
|
||||||
while ( this.matchesExactly(hostname) === false ) {
|
while ( this.dict.has(hostname) === false ) {
|
||||||
pos = hostname.indexOf('.');
|
pos = hostname.indexOf('.');
|
||||||
if ( pos === -1 ) {
|
if ( pos === -1 ) {
|
||||||
this.h = '';
|
this.h = '';
|
||||||
|
@ -1167,17 +1025,12 @@ FilterHostnameDict.prototype.rtCompile = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterHostnameDict.prototype.toSelfie = function() {
|
FilterHostnameDict.prototype.toSelfie = function() {
|
||||||
return JSON.stringify({
|
return JSON.stringify(µb.setToArray(this.dict));
|
||||||
count: this.count,
|
|
||||||
dict: this.dict
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
FilterHostnameDict.fromSelfie = function(s) {
|
FilterHostnameDict.fromSelfie = function(s) {
|
||||||
var f = new FilterHostnameDict();
|
var f = new FilterHostnameDict();
|
||||||
var o = JSON.parse(s);
|
f.dict = µb.setFromArray(JSON.parse(s));
|
||||||
f.count = o.count;
|
|
||||||
f.dict = o.dict;
|
|
||||||
return f;
|
return f;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1728,8 +1581,8 @@ FilterContainer.prototype.reset = function() {
|
||||||
this.allowFilterCount = 0;
|
this.allowFilterCount = 0;
|
||||||
this.blockFilterCount = 0;
|
this.blockFilterCount = 0;
|
||||||
this.discardedCount = 0;
|
this.discardedCount = 0;
|
||||||
this.duplicateBuster = {};
|
this.duplicateBuster = new Set();
|
||||||
this.categories = Object.create(null);
|
this.categories = new Map();
|
||||||
this.filterParser.reset();
|
this.filterParser.reset();
|
||||||
this.filterCounts = {};
|
this.filterCounts = {};
|
||||||
|
|
||||||
|
@ -1743,17 +1596,7 @@ FilterContainer.prototype.reset = function() {
|
||||||
|
|
||||||
FilterContainer.prototype.freeze = function() {
|
FilterContainer.prototype.freeze = function() {
|
||||||
histogram('allFilters', this.categories);
|
histogram('allFilters', this.categories);
|
||||||
this.duplicateBuster = {};
|
this.duplicateBuster = new Set();
|
||||||
|
|
||||||
var categories = this.categories;
|
|
||||||
var bucket;
|
|
||||||
for ( var k in categories ) {
|
|
||||||
bucket = categories[k]['.'];
|
|
||||||
if ( bucket !== undefined ) {
|
|
||||||
bucket.freeze();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.filterParser.reset();
|
this.filterParser.reset();
|
||||||
this.frozen = true;
|
this.frozen = true;
|
||||||
};
|
};
|
||||||
|
@ -1786,20 +1629,23 @@ FilterContainer.prototype.factories = {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.toSelfie = function() {
|
FilterContainer.prototype.toSelfie = function() {
|
||||||
var categoryToSelfie = function(dict) {
|
var categoryToSelfie = function(map) {
|
||||||
var selfie = [];
|
var selfie = [],
|
||||||
var bucket, ff, n, i, f;
|
iterator = map.entries(),
|
||||||
for ( var token in dict ) {
|
entry, bucket, ff, f;
|
||||||
// No need for hasOwnProperty() here: there is no prototype chain.
|
for (;;) {
|
||||||
selfie.push('k2\t' + token);
|
entry = iterator.next();
|
||||||
bucket = dict[token];
|
if ( entry.done ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
selfie.push('k2\t' + entry.value[0]);
|
||||||
|
bucket = entry.value[1];
|
||||||
selfie.push(bucket.fid + '\t' + bucket.toSelfie());
|
selfie.push(bucket.fid + '\t' + bucket.toSelfie());
|
||||||
if ( bucket.fid !== '[]' ) {
|
if ( bucket.fid !== '[]' ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ff = bucket.filters;
|
ff = bucket.filters;
|
||||||
n = ff.length;
|
for ( var i = 0, ni = ff.length; i < ni; i++ ) {
|
||||||
for ( i = 0; i < n; i++ ) {
|
|
||||||
f = ff[i];
|
f = ff[i];
|
||||||
selfie.push(f.fid + '\t' + f.toSelfie());
|
selfie.push(f.fid + '\t' + f.toSelfie());
|
||||||
}
|
}
|
||||||
|
@ -1807,12 +1653,17 @@ FilterContainer.prototype.toSelfie = function() {
|
||||||
return selfie.join('\n');
|
return selfie.join('\n');
|
||||||
};
|
};
|
||||||
|
|
||||||
var categoriesToSelfie = function(dict) {
|
var categoriesToSelfie = function(map) {
|
||||||
var selfie = [];
|
var selfie = [],
|
||||||
for ( var key in dict ) {
|
iterator = map.entries(),
|
||||||
// No need for hasOwnProperty() here: there is no prototype chain.
|
entry;
|
||||||
selfie.push('k1\t' + key);
|
for (;;) {
|
||||||
selfie.push(categoryToSelfie(dict[key]));
|
entry = iterator.next();
|
||||||
|
if ( entry.done ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
selfie.push('k1\t' + entry.value[0]);
|
||||||
|
selfie.push(categoryToSelfie(entry.value[1]));
|
||||||
}
|
}
|
||||||
return selfie.join('\n');
|
return selfie.join('\n');
|
||||||
};
|
};
|
||||||
|
@ -1840,7 +1691,7 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
|
||||||
this.discardedCount = selfie.discardedCount;
|
this.discardedCount = selfie.discardedCount;
|
||||||
|
|
||||||
var catKey, tokenKey;
|
var catKey, tokenKey;
|
||||||
var dict = this.categories, subdict;
|
var map = this.categories, submap;
|
||||||
var bucket = null;
|
var bucket = null;
|
||||||
var rawText = selfie.categories;
|
var rawText = selfie.categories;
|
||||||
var rawEnd = rawText.length;
|
var rawEnd = rawText.length;
|
||||||
|
@ -1857,7 +1708,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
|
||||||
what = line.slice(0, pos);
|
what = line.slice(0, pos);
|
||||||
if ( what === 'k1' ) {
|
if ( what === 'k1' ) {
|
||||||
catKey = line.slice(pos + 1);
|
catKey = line.slice(pos + 1);
|
||||||
subdict = dict[catKey] = Object.create(null);
|
submap = new Map();
|
||||||
|
map.set(catKey, submap);
|
||||||
bucket = null;
|
bucket = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1868,7 +1720,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
|
||||||
}
|
}
|
||||||
factory = this.factories[what];
|
factory = this.factories[what];
|
||||||
if ( bucket === null ) {
|
if ( bucket === null ) {
|
||||||
bucket = subdict[tokenKey] = factory.fromSelfie(line.slice(pos + 1));
|
bucket = factory.fromSelfie(line.slice(pos + 1));
|
||||||
|
submap.set(tokenKey, bucket);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// When token key is reused, it can't be anything
|
// When token key is reused, it can't be anything
|
||||||
|
@ -2088,66 +1941,69 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed,
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.fromCompiledContent = function(lineIter) {
|
FilterContainer.prototype.fromCompiledContent = function(lineIter) {
|
||||||
var line, fields, bucket, entry, factory, filter;
|
var line, hash, token, fclass, fdata,
|
||||||
|
bucket, entry, factory, filter,
|
||||||
|
fieldIter = new µb.FieldIterator('\v');
|
||||||
|
|
||||||
while ( lineIter.eot() === false ) {
|
while ( lineIter.eot() === false ) {
|
||||||
if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x6E /* 'n' */ ) {
|
if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x6E /* 'n' */ ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
line = lineIter.next().slice(2);
|
line = lineIter.next();
|
||||||
fields = line.split('\v');
|
|
||||||
|
fieldIter.first(line);
|
||||||
|
hash = fieldIter.next();
|
||||||
|
token = fieldIter.next();
|
||||||
|
fclass = fieldIter.next();
|
||||||
|
fdata = fieldIter.next();
|
||||||
|
|
||||||
// Special cases: delegate to more specialized engines.
|
// Special cases: delegate to more specialized engines.
|
||||||
// Redirect engine.
|
// Redirect engine.
|
||||||
if ( fields[2] === '=>' ) {
|
if ( fclass === '=>' ) {
|
||||||
µb.redirectEngine.fromCompiledRule(fields[3]);
|
µb.redirectEngine.fromCompiledRule(fdata);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plain static filters.
|
// Plain static filters.
|
||||||
this.acceptedCount += 1;
|
this.acceptedCount += 1;
|
||||||
|
|
||||||
bucket = this.categories[fields[0]];
|
bucket = this.categories.get(hash);
|
||||||
if ( bucket === undefined ) {
|
if ( bucket === undefined ) {
|
||||||
bucket = this.categories[fields[0]] = Object.create(null);
|
bucket = new Map();
|
||||||
|
this.categories.set(hash, bucket);
|
||||||
}
|
}
|
||||||
entry = bucket[fields[1]];
|
entry = bucket.get(token);
|
||||||
|
|
||||||
if ( fields[1] === '.' ) {
|
if ( token === '.' ) {
|
||||||
if ( entry === undefined ) {
|
if ( entry === undefined ) {
|
||||||
entry = bucket['.'] = new FilterHostnameDict();
|
entry = new FilterHostnameDict();
|
||||||
|
bucket.set('.', new FilterHostnameDict());
|
||||||
}
|
}
|
||||||
if ( entry.add(fields[2]) === false ) {
|
// 'fclass' is hostname
|
||||||
|
if ( entry.add(fclass) === false ) {
|
||||||
this.discardedCount += 1;
|
this.discardedCount += 1;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( this.duplicateBuster.hasOwnProperty(line) ) {
|
if ( this.duplicateBuster.has(line) ) {
|
||||||
this.discardedCount += 1;
|
this.discardedCount += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.duplicateBuster[line] = true;
|
this.duplicateBuster.add(line);
|
||||||
|
|
||||||
factory = this.factories[fields[2]];
|
factory = this.factories[fclass];
|
||||||
|
|
||||||
// For development purpose
|
filter = factory.fromSelfie(fdata);
|
||||||
//if ( this.filterCounts.hasOwnProperty(fields[2]) === false ) {
|
|
||||||
// this.filterCounts[fields[2]] = 1;
|
|
||||||
//} else {
|
|
||||||
// this.filterCounts[fields[2]]++;
|
|
||||||
//}
|
|
||||||
|
|
||||||
filter = factory.fromSelfie(fields[3]);
|
|
||||||
if ( entry === undefined ) {
|
if ( entry === undefined ) {
|
||||||
bucket[fields[1]] = filter;
|
bucket.set(token, filter);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ( entry.fid === '[]' ) {
|
if ( entry.fid === '[]' ) {
|
||||||
entry.add(filter);
|
entry.add(filter);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bucket[fields[1]] = new FilterBucket(entry, filter);
|
bucket.set(token, new FilterBucket(entry, filter));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2289,9 +2145,12 @@ FilterContainer.prototype.filterRegexFromCompiled = function(compiled, flags) {
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// bucket: Map
|
||||||
|
// url: string
|
||||||
|
|
||||||
FilterContainer.prototype.matchTokens = function(bucket, url) {
|
FilterContainer.prototype.matchTokens = function(bucket, url) {
|
||||||
// Hostname-only filters
|
// Hostname-only filters
|
||||||
var f = bucket['.'];
|
var f = bucket.get('.');
|
||||||
if ( f !== undefined && f.match() ) {
|
if ( f !== undefined && f.match() ) {
|
||||||
this.tokenRegister = '.';
|
this.tokenRegister = '.';
|
||||||
this.fRegister = f;
|
this.fRegister = f;
|
||||||
|
@ -2307,7 +2166,7 @@ FilterContainer.prototype.matchTokens = function(bucket, url) {
|
||||||
if ( token === '' ) {
|
if ( token === '' ) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
f = bucket[token];
|
f = bucket.get(token);
|
||||||
if ( f !== undefined && f.match(url, tokenEntry.beg) ) {
|
if ( f !== undefined && f.match(url, tokenEntry.beg) ) {
|
||||||
this.tokenRegister = token;
|
this.tokenRegister = token;
|
||||||
this.fRegister = f;
|
this.fRegister = f;
|
||||||
|
@ -2316,7 +2175,7 @@ FilterContainer.prototype.matchTokens = function(bucket, url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex-based filters
|
// Regex-based filters
|
||||||
f = bucket['*'];
|
f = bucket.get('*');
|
||||||
if ( f !== undefined && f.match(url) ) {
|
if ( f !== undefined && f.match(url) ) {
|
||||||
this.tokenRegister = '*';
|
this.tokenRegister = '*';
|
||||||
this.fRegister = f;
|
this.fRegister = f;
|
||||||
|
@ -2361,7 +2220,7 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
|
||||||
if ( requestType === 'elemhide' ) {
|
if ( requestType === 'elemhide' ) {
|
||||||
key = AllowAnyParty | type;
|
key = AllowAnyParty | type;
|
||||||
if (
|
if (
|
||||||
(bucket = categories[toHex(key)]) &&
|
(bucket = categories.get(toHex(key))) &&
|
||||||
this.matchTokens(bucket, url)
|
this.matchTokens(bucket, url)
|
||||||
) {
|
) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
|
@ -2373,14 +2232,14 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/139
|
// https://github.com/chrisaljoudi/uBlock/issues/139
|
||||||
// Test against important block filters
|
// Test against important block filters
|
||||||
key = BlockAnyParty | Important | type;
|
key = BlockAnyParty | Important | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = BlockAction | Important | type | party;
|
key = BlockAction | Important | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
|
@ -2389,14 +2248,14 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
|
||||||
|
|
||||||
// Test against block filters
|
// Test against block filters
|
||||||
key = BlockAnyParty | type;
|
key = BlockAnyParty | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.fRegister === null ) {
|
if ( this.fRegister === null ) {
|
||||||
key = BlockAction | type | party;
|
key = BlockAction | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
|
@ -2410,14 +2269,14 @@ FilterContainer.prototype.matchStringExactType = function(context, requestURL, r
|
||||||
|
|
||||||
// Test against allow filters
|
// Test against allow filters
|
||||||
key = AllowAnyParty | type;
|
key = AllowAnyParty | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = AllowAction | type | party;
|
key = AllowAction | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
|
@ -2480,28 +2339,28 @@ FilterContainer.prototype.matchString = function(context) {
|
||||||
// evaluation. Normally, it is "evaluate block then evaluate allow", with
|
// evaluation. Normally, it is "evaluate block then evaluate allow", with
|
||||||
// the `important` property it is "evaluate allow then evaluate block".
|
// the `important` property it is "evaluate allow then evaluate block".
|
||||||
key = BlockAnyTypeAnyParty | Important;
|
key = BlockAnyTypeAnyParty | Important;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = BlockAnyType | Important | party;
|
key = BlockAnyType | Important | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = BlockAnyParty | Important | type;
|
key = BlockAnyParty | Important | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = BlockAction | Important | type | party;
|
key = BlockAction | Important | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return true;
|
return true;
|
||||||
|
@ -2510,28 +2369,28 @@ FilterContainer.prototype.matchString = function(context) {
|
||||||
|
|
||||||
// Test against block filters
|
// Test against block filters
|
||||||
key = BlockAnyTypeAnyParty;
|
key = BlockAnyTypeAnyParty;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.fRegister === null ) {
|
if ( this.fRegister === null ) {
|
||||||
key = BlockAnyType | party;
|
key = BlockAnyType | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.fRegister === null ) {
|
if ( this.fRegister === null ) {
|
||||||
key = BlockAnyParty | type;
|
key = BlockAnyParty | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ( this.fRegister === null ) {
|
if ( this.fRegister === null ) {
|
||||||
key = BlockAction | type | party;
|
key = BlockAction | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
}
|
}
|
||||||
|
@ -2547,28 +2406,28 @@ FilterContainer.prototype.matchString = function(context) {
|
||||||
|
|
||||||
// Test against allow filters
|
// Test against allow filters
|
||||||
key = AllowAnyTypeAnyParty;
|
key = AllowAnyTypeAnyParty;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = AllowAnyType | party;
|
key = AllowAnyType | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = AllowAnyParty | type;
|
key = AllowAnyParty | type;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
key = AllowAction | type | party;
|
key = AllowAction | type | party;
|
||||||
if ( (bucket = categories[toHex(key)]) ) {
|
if ( (bucket = categories.get(toHex(key))) ) {
|
||||||
if ( this.matchTokens(bucket, url) ) {
|
if ( this.matchTokens(bucket, url) ) {
|
||||||
this.keyRegister = key;
|
this.keyRegister = key;
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -126,18 +126,16 @@
|
||||||
|
|
||||||
µBlock.LineIterator = function(text, offset) {
|
µBlock.LineIterator = function(text, offset) {
|
||||||
this.text = text;
|
this.text = text;
|
||||||
|
this.textLen = this.text.length;
|
||||||
this.offset = offset || 0;
|
this.offset = offset || 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.LineIterator.prototype.next = function() {
|
µBlock.LineIterator.prototype.next = function() {
|
||||||
if ( this.offset >= this.text.length ) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
var lineEnd = this.text.indexOf('\n', this.offset);
|
var lineEnd = this.text.indexOf('\n', this.offset);
|
||||||
if ( lineEnd === -1 ) {
|
if ( lineEnd === -1 ) {
|
||||||
lineEnd = this.text.indexOf('\r', this.offset);
|
lineEnd = this.text.indexOf('\r', this.offset);
|
||||||
if ( lineEnd === -1 ) {
|
if ( lineEnd === -1 ) {
|
||||||
lineEnd = this.text.length;
|
lineEnd = this.textLen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var line = this.text.slice(this.offset, lineEnd);
|
var line = this.text.slice(this.offset, lineEnd);
|
||||||
|
@ -146,7 +144,69 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.LineIterator.prototype.eot = function() {
|
µBlock.LineIterator.prototype.eot = function() {
|
||||||
return this.offset >= this.text.length;
|
return this.offset >= this.textLen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// The field iterator is less CPU-intensive than when using native
|
||||||
|
// String.split().
|
||||||
|
|
||||||
|
µBlock.FieldIterator = function(sep) {
|
||||||
|
this.text = '';
|
||||||
|
this.sep = sep;
|
||||||
|
this.sepLen = sep.length;
|
||||||
|
this.offset = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
µBlock.FieldIterator.prototype.first = function(text) {
|
||||||
|
this.text = text;
|
||||||
|
this.offset = 0;
|
||||||
|
return this.next();
|
||||||
|
};
|
||||||
|
|
||||||
|
µBlock.FieldIterator.prototype.next = function() {
|
||||||
|
var end = this.text.indexOf(this.sep, this.offset);
|
||||||
|
if ( end === -1 ) {
|
||||||
|
end = this.text.length;
|
||||||
|
}
|
||||||
|
var field = this.text.slice(this.offset, end);
|
||||||
|
this.offset = end + this.sepLen;
|
||||||
|
return field;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
µBlock.mapToArray = function(map) {
|
||||||
|
var out = [],
|
||||||
|
entries = map.entries(),
|
||||||
|
entry;
|
||||||
|
for (;;) {
|
||||||
|
entry = entries.next();
|
||||||
|
if ( entry.done ) { break; }
|
||||||
|
out.push([ entry.value[0], entry.value[1] ]);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
µBlock.mapFromArray = function(arr) {
|
||||||
|
return new Map(arr);
|
||||||
|
};
|
||||||
|
|
||||||
|
µBlock.setToArray = function(dict) {
|
||||||
|
var out = [],
|
||||||
|
entries = dict.values(),
|
||||||
|
entry;
|
||||||
|
for (;;) {
|
||||||
|
entry = entries.next();
|
||||||
|
if ( entry.done ) { break; }
|
||||||
|
out.push(entry.value);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
µBlock.setFromArray = function(arr) {
|
||||||
|
return new Set(arr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
|
@ -22,6 +22,7 @@ cat src/background.html | sed -e '/vapi-polyfill\.js/d' > $DES/background.html
|
||||||
|
|
||||||
mv $DES/img/icon_128.png $DES/icon.png
|
mv $DES/img/icon_128.png $DES/icon.png
|
||||||
cp platform/firefox/css/* $DES/css/
|
cp platform/firefox/css/* $DES/css/
|
||||||
|
cp platform/firefox/polyfill.js $DES/js/
|
||||||
cp platform/firefox/vapi-*.js $DES/js/
|
cp platform/firefox/vapi-*.js $DES/js/
|
||||||
cp platform/firefox/bootstrap.js $DES/
|
cp platform/firefox/bootstrap.js $DES/
|
||||||
cp platform/firefox/frame*.js $DES/
|
cp platform/firefox/frame*.js $DES/
|
||||||
|
|
Loading…
Reference in New Issue