From c0387835fab5547f4305c0457cf9bf3d70631c6b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Apr 2018 15:22:19 -0400 Subject: [PATCH] code review: modernize old code --- src/js/cosmetic-filtering.js | 4 +- src/js/storage.js | 11 ++- src/js/uritools.js | 56 ++++++++-------- src/lib/publicsuffixlist.js | 125 ++++++++++++----------------------- 4 files changed, 78 insertions(+), 118 deletions(-) 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 }; /******************************************************************************/