diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js
index 9fb2d762d..a5590b00d 100644
--- a/src/js/cosmetic-filtering.js
+++ b/src/js/cosmetic-filtering.js
@@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
- Copyright (C) 2014-2017 Raymond Hill
+ Copyright (C) 2014-2018 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
@@ -755,7 +755,7 @@ FilterContainer.prototype.compileHostnameSelector = function(
var compiled = µb.staticExtFilteringEngine.compileSelector(parsed.suffix);
if ( compiled === undefined ) { return; }
- var domain = this.µburi.domainFromHostname(hostname),
+ var domain = this.µburi.domainFromHostnameNoCache(hostname),
hash;
// https://github.com/chrisaljoudi/uBlock/issues/188
diff --git a/src/js/storage.js b/src/js/storage.js
index b12f750be..312dd7bb6 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -960,11 +960,18 @@
};
var onCompiledListLoaded = function(details) {
- if ( details.content === '' ) {
+ var selfie;
+ try {
+ selfie = JSON.parse(details.content);
+ } catch (ex) {
+ }
+ if (
+ selfie === undefined ||
+ publicSuffixList.fromSelfie(selfie) === false
+ ) {
µb.assets.get(assetKey, onRawListLoaded);
return;
}
- publicSuffixList.fromSelfie(JSON.parse(details.content));
callback();
};
diff --git a/src/js/uritools.js b/src/js/uritools.js
index a988af4e0..4e118bd47 100644
--- a/src/js/uritools.js
+++ b/src/js/uritools.js
@@ -1,7 +1,7 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
- Copyright (C) 2014-2017 Raymond Hill
+ Copyright (C) 2014-2018 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
@@ -299,6 +299,10 @@ URI.domainFromHostname = function(hostname) {
return domainCacheAdd(hostname, hostname);
};
+URI.domainFromHostnameNoCache = function(hostname) {
+ return reIPAddressNaive.test(hostname) ? hostname : psl.getDomain(hostname);
+};
+
URI.domain = function() {
return this.domainFromHostname(this.hostname);
};
@@ -328,11 +332,11 @@ URI.pathFromURI = function(uri) {
// specific set of hostnames within a narrow time span -- in other words, I
// believe probability of cache hit are high in uBlock.
-var domainCache = Object.create(null);
-var domainCacheCount = 0;
-var domainCacheCountLowWaterMark = 35;
-var domainCacheCountHighWaterMark = 50;
-var domainCacheEntryJunkyardMax = domainCacheCountHighWaterMark - domainCacheCountLowWaterMark;
+var domainCache = new Map();
+var domainCacheCountLowWaterMark = 40;
+var domainCacheCountHighWaterMark = 60;
+var domainCacheEntryJunkyardMax =
+ domainCacheCountHighWaterMark - domainCacheCountLowWaterMark;
var DomainCacheEntry = function(domain) {
this.init(domain);
@@ -352,23 +356,20 @@ DomainCacheEntry.prototype.dispose = function() {
};
var domainCacheEntryFactory = function(domain) {
- var entry = domainCacheEntryJunkyard.pop();
- if ( entry ) {
- return entry.init(domain);
- }
- return new DomainCacheEntry(domain);
+ return domainCacheEntryJunkyard.length !== 0 ?
+ domainCacheEntryJunkyard.pop().init(domain) :
+ new DomainCacheEntry(domain);
};
var domainCacheEntryJunkyard = [];
var domainCacheAdd = function(hostname, domain) {
- var entry = domainCache[hostname];
+ var entry = domainCache.get(hostname);
if ( entry !== undefined ) {
entry.tstamp = Date.now();
} else {
- domainCache[hostname] = domainCacheEntryFactory(domain);
- domainCacheCount += 1;
- if ( domainCacheCount === domainCacheCountHighWaterMark ) {
+ domainCache.set(hostname, domainCacheEntryFactory(domain));
+ if ( domainCache.size === domainCacheCountHighWaterMark ) {
domainCachePrune();
}
}
@@ -376,29 +377,24 @@ var domainCacheAdd = function(hostname, domain) {
};
var domainCacheEntrySort = function(a, b) {
- return domainCache[b].tstamp - domainCache[a].tstamp;
+ return domainCache.get(b).tstamp - domainCache.get(a).tstamp;
};
var domainCachePrune = function() {
- var hostnames = Object.keys(domainCache)
- .sort(domainCacheEntrySort)
- .slice(domainCacheCountLowWaterMark);
+ var hostnames = Array.from(domainCache.keys())
+ .sort(domainCacheEntrySort)
+ .slice(domainCacheCountLowWaterMark);
var i = hostnames.length;
- domainCacheCount -= i;
- var hostname;
while ( i-- ) {
- hostname = hostnames[i];
- domainCache[hostname].dispose();
- delete domainCache[hostname];
+ var hostname = hostnames[i];
+ domainCache.get(hostname).dispose();
+ domainCache.delete(hostname);
}
};
-var domainCacheReset = function() {
- domainCache = Object.create(null);
- domainCacheCount = 0;
-};
-
-psl.onChanged.addListener(domainCacheReset);
+window.addEventListener('publicSuffixList', function() {
+ domainCache.clear();
+});
/******************************************************************************/
diff --git a/src/lib/publicsuffixlist.js b/src/lib/publicsuffixlist.js
index 89749867c..1a04d78b9 100644
--- a/src/lib/publicsuffixlist.js
+++ b/src/lib/publicsuffixlist.js
@@ -2,7 +2,7 @@
publicsuffixlist.js - an efficient javascript implementation to deal with
Mozilla Foundation's Public Suffix List
- Copyright (C) 2013 Raymond Hill
+ Copyright (C) 2013-2018 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
@@ -37,9 +37,8 @@
/******************************************************************************/
-var exceptions = {};
-var rules = {};
-var selfieMagic = 'iscjsfsaolnm';
+var exceptions = new Map();
+var rules = new Map();
// This value dictate how the search will be performed:
// < this.cutoffLength = indexOf()
@@ -47,8 +46,6 @@ var selfieMagic = 'iscjsfsaolnm';
var cutoffLength = 256;
var mustPunycode = /[^a-z0-9.-]/;
-var onChangedListeners = [];
-
/******************************************************************************/
// In the context of this code, a domain is defined as:
@@ -127,20 +124,17 @@ function search(store, hostname) {
tld = hostname.slice(pos + 1);
remainder = hostname.slice(0, pos);
}
- var substore = store[tld];
- if ( !substore ) {
- return false;
- }
+ var substore = store.get(tld);
+ if ( substore === undefined ) { return false; }
// If substore is a string, use indexOf()
if ( typeof substore === 'string' ) {
return substore.indexOf(' ' + remainder + ' ') >= 0;
}
// It is an array: use binary search.
var l = remainder.length;
+ if ( l >= substore.length ) { return false; }
var haystack = substore[l];
- if ( !haystack ) {
- return false;
- }
+ if ( haystack.length === 0 ) { return false; }
var left = 0;
var right = Math.floor(haystack.length / l + 0.5);
var i, needle;
@@ -168,8 +162,8 @@ function search(store, hostname) {
// Suggestion: use it's quite good.
function parse(text, toAscii) {
- exceptions = {};
- rules = {};
+ exceptions = new Map();
+ rules = new Map();
// http://publicsuffix.org/list/:
// "... all rules must be canonicalized in the normal way
@@ -229,16 +223,18 @@ function parse(text, toAscii) {
}
// Store suffix using tld as key
- if ( !store.hasOwnProperty(tld) ) {
- store[tld] = [];
+ var substore = store.get(tld);
+ if ( substore === undefined ) {
+ store.set(tld, (substore = []));
}
if ( line ) {
- store[tld].push(line);
+ substore.push(line);
}
}
crystallize(exceptions);
crystallize(rules);
- callListeners(onChangedListeners);
+
+ window.dispatchEvent(new CustomEvent('publicSuffixList'));
}
/******************************************************************************/
@@ -247,107 +243,69 @@ function parse(text, toAscii) {
// for future look up.
function crystallize(store) {
- var suffixes, suffix, i, l;
-
- for ( var tld in store ) {
- if ( !store.hasOwnProperty(tld) ) {
- continue;
- }
- suffixes = store[tld].join(' ');
+ for ( var entry of store ) {
+ var tld = entry[0];
+ var suffixes = entry[1];
// No suffix
- if ( !suffixes ) {
- store[tld] = '';
+ if ( suffixes.length === 0 ) {
+ store.set(tld, '');
continue;
}
// Concatenated list of suffixes less than cutoff length:
// Store as string, lookup using indexOf()
- if ( suffixes.length < cutoffLength ) {
- store[tld] = ' ' + suffixes + ' ';
+ var s = suffixes.join(' ');
+ if ( s.length < cutoffLength ) {
+ store.set(tld, ' ' + s + ' ');
continue;
}
// Concatenated list of suffixes greater or equal to cutoff length
// Store as array keyed on suffix length, lookup using binary search.
// I borrowed the idea to key on string length here:
// http://ejohn.org/blog/dictionary-lookups-in-javascript/#comment-392072
-
- i = store[tld].length;
- suffixes = [];
+ var i = suffixes.length, l;
+ var aa = [];
while ( i-- ) {
- suffix = store[tld][i];
+ var suffix = suffixes[i];
+ var j = aa.length;
l = suffix.length;
- if ( !suffixes[l] ) {
- suffixes[l] = [];
+ while ( j <= l ) {
+ aa[j] = []; j += 1;
}
- suffixes[l].push(suffix);
+ aa[l].push(suffix);
}
- l = suffixes.length;
+ l = aa.length;
while ( l-- ) {
- if ( suffixes[l] ) {
- suffixes[l] = suffixes[l].sort().join('');
- }
+ aa[l] = aa[l].sort().join('');
}
- store[tld] = suffixes;
+ store.set(tld, aa);
}
return store;
}
/******************************************************************************/
+var selfieMagic = 1;
+
function toSelfie() {
return {
magic: selfieMagic,
- rules: rules,
- exceptions: exceptions
+ rules: Array.from(rules),
+ exceptions: Array.from(exceptions)
};
}
function fromSelfie(selfie) {
- if ( typeof selfie !== 'object' || typeof selfie.magic !== 'string' || selfie.magic !== selfieMagic ) {
+ if ( typeof selfie !== 'object' || selfie.magic !== selfieMagic ) {
return false;
}
- rules = selfie.rules;
- exceptions = selfie.exceptions;
- callListeners(onChangedListeners);
+ rules = new Map(selfie.rules);
+ exceptions = new Map(selfie.exceptions);
+ window.dispatchEvent(new CustomEvent('publicSuffixList'));
return true;
}
/******************************************************************************/
-var addListener = function(listeners, callback) {
- if ( typeof callback !== 'function' ) {
- return;
- }
- if ( listeners.indexOf(callback) === -1 ) {
- listeners.push(callback);
- }
-};
-
-var removeListener = function(listeners, callback) {
- var pos = listeners.indexOf(callback);
- if ( pos !== -1 ) {
- listeners.splice(pos, 1);
- }
-};
-
-var callListeners = function(listeners) {
- for ( var i = 0; i < listeners.length; i++ ) {
- listeners[i]();
- }
-};
-
-/******************************************************************************/
-
-var onChanged = {
- addListener: function(callback) {
- addListener(onChangedListeners, callback);
- },
- removeListener: function(callback) {
- removeListener(onChangedListeners, callback);
- }
-};
-
-/******************************************************************************/
-
// Public API
root = root || window;
@@ -359,7 +317,6 @@ root.publicSuffixList = {
'getPublicSuffix': getPublicSuffix,
'toSelfie': toSelfie,
'fromSelfie': fromSelfie,
- 'onChanged': onChanged
};
/******************************************************************************/