Skip to content
This repository was archived by the owner on Sep 9, 2022. It is now read-only.

Commit 3e47b8a

Browse files
committed
code review + testing after fix to #131
1 parent 1c219ef commit 3e47b8a

File tree

4 files changed

+268
-126
lines changed

4 files changed

+268
-126
lines changed

js/abp-hide-filters.js

Lines changed: 155 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,23 @@ FilterHostname.prototype.retrieve = function(hostname, out) {
127127
}
128128
};
129129

130+
/******************************************************************************/
131+
132+
// Any selector specific to an entity
133+
// Examples:
134+
// google.*###cnt #center_col > #res > #topstuff > .ts
135+
136+
var FilterEntity = function(s, entity) {
137+
this.s = s;
138+
this.entity = entity;
139+
};
140+
141+
FilterEntity.prototype.retrieve = function(entity, out) {
142+
if ( entity.slice(-this.entity.length) === this.entity ) {
143+
out.push(this.s);
144+
}
145+
};
146+
130147
/******************************************************************************/
131148
/******************************************************************************/
132149

@@ -224,20 +241,64 @@ FilterParser.prototype.parse = function(s) {
224241
/******************************************************************************/
225242

226243
var SelectorCacheEntry = function() {
227-
this.selectors = [];
244+
this.cosmetic = {};
245+
this.net = {};
246+
this.netCount = 0;
228247
this.lastAccessTime = Date.now();
229248
};
230249

231-
SelectorCacheEntry.prototype.add = function(selectors) {
250+
SelectorCacheEntry.prototype.netLowWaterMark = 20;
251+
SelectorCacheEntry.prototype.netHighWaterMark = 30;
252+
253+
SelectorCacheEntry.prototype.addCosmetic = function(selectors) {
254+
var dict = this.cosmetic;
255+
var i = selectors.length || 0;
256+
while ( i-- ) {
257+
dict[selectors[i]] = true;
258+
}
259+
};
260+
261+
SelectorCacheEntry.prototype.addNet = function(selector) {
262+
if ( typeof selector !== 'string' || selector === '' ) {
263+
return;
264+
}
265+
// Net request-derived selectors: I limit the number of cached selectors,
266+
// as I expect cases where the blocked net-requests are never the
267+
// exact same URL.
268+
var dict = this.net;
269+
if ( dict[selector] !== undefined ) {
270+
dict[selector] = Date.now();
271+
return;
272+
}
273+
if ( this.netCount >= this.netHighWaterMark ) {
274+
var keys = Object.keys(dict).sort(function(a, b) {
275+
return dict[b] - dict[a];
276+
}).slice(this.netLowWaterMark);
277+
var i = keys.length;
278+
while ( i-- ) {
279+
delete dict[keys[i]];
280+
}
281+
}
282+
dict[selector] = Date.now();
283+
this.netCount += 1;
284+
};
285+
286+
SelectorCacheEntry.prototype.add = function(selectors, type) {
232287
this.lastAccessTime = Date.now();
233-
this.selectors.push(selectors);
288+
if ( type === 'cosmetic' ) {
289+
this.addCosmetic(selectors);
290+
} else {
291+
this.addNet(selectors);
292+
}
234293
};
235294

236-
SelectorCacheEntry.prototype.retrieve = function(out) {
295+
SelectorCacheEntry.prototype.retrieve = function(type, out) {
237296
this.lastAccessTime = Date.now();
238-
var i = this.selectors.length;
239-
while ( i-- ) {
240-
out.push(this.selectors[i]);
297+
var dict = type === 'cosmetic' ? this.cosmetic : this.net;
298+
for ( var selector in dict ) {
299+
if ( dict.hasOwnProperty(selector) ) {
300+
out.push(selector);
301+
}
241302
}
242303
};
243304

@@ -310,7 +371,7 @@ var makeHash = function(unhide, token, mask) {
310371
// High-high generic: everything else
311372
// Specific
312373
// Specfic hostname
313-
//
374+
// Specific entity
314375
// Generic filters can only be enforced once the main document is loaded.
315376
// Specific filers can be enforced before the main document is loaded.
316377

@@ -343,7 +404,8 @@ FilterContainer.prototype.reset = function() {
343404
this.highGenericDonthide = {};
344405
this.hostnameHide = {};
345406
this.hostnameDonthide = {};
346-
407+
this.entityHide = {};
408+
this.entityDonthide = {};
347409
// permanent
348410
// [class], [id]
349411
this.lowGenericFilters = {};
@@ -367,6 +429,7 @@ FilterContainer.prototype.reset = function() {
367429
this.highHighGenericDonthideCount = 0;
368430

369431
this.hostnameFilters = {};
432+
this.entityFilters = {};
370433
};
371434

372435
/******************************************************************************/
@@ -394,7 +457,7 @@ FilterContainer.prototype.add = function(s) {
394457
if ( hostname.charAt(0) !== '~' ) {
395458
applyGlobally = false;
396459
}
397-
this.addHostnameSelector(hostname, parsed);
460+
this.addSpecificSelector(hostname, parsed);
398461
}
399462
if ( applyGlobally ) {
400463
this.addGenericSelector(parsed);
@@ -427,6 +490,17 @@ FilterContainer.prototype.addGenericSelector = function(parsed) {
427490

428491
/******************************************************************************/
429492

493+
FilterContainer.prototype.addSpecificSelector = function(hostname, parsed) {
494+
// rhill 2014-07-13: new filter class: entity.
495+
if ( hostname.slice(-2) === '.*' ) {
496+
this.addEntitySelector(hostname, parsed);
497+
} else {
498+
this.addHostnameSelector(hostname, parsed);
499+
}
500+
};
501+
502+
/******************************************************************************/
503+
430504
FilterContainer.prototype.addHostnameSelector = function(hostname, parsed) {
431505
// https://github.com/gorhill/uBlock/issues/145
432506
var unhide = parsed.unhide;
@@ -452,6 +526,26 @@ FilterContainer.prototype.addHostnameSelector = function(hostname, parsed) {
452526

453527
/******************************************************************************/
454528

529+
FilterContainer.prototype.addEntitySelector = function(hostname, parsed) {
530+
var entries = parsed.unhide === 0 ?
531+
this.entityHide :
532+
this.entityDonthide;
533+
var entity = hostname.slice(0, -2);
534+
var entry = entries[entity];
535+
if ( entry === undefined ) {
536+
entry = entries[entity] = {};
537+
entry[parsed.suffix] = true;
538+
this.acceptedCount += 1;
539+
} else if ( entry[parsed.suffix] === undefined ) {
540+
entry[parsed.suffix] = true;
541+
this.acceptedCount += 1;
542+
} else {
543+
this.duplicateCount += 1;
544+
}
545+
};
546+
547+
/******************************************************************************/
548+
455549
FilterContainer.prototype.freezeLowGenerics = function(what, type) {
456550
var selectors = this[what];
457551
var matches, selectorPrefix, f, hash, bucket;
@@ -509,6 +603,30 @@ FilterContainer.prototype.freezeHostnameSpecifics = function(what, type) {
509603

510604
/******************************************************************************/
511605

606+
FilterContainer.prototype.freezeEntitySpecifics = function(what, type) {
607+
var entries = this[what];
608+
var filters = this.entityFilters;
609+
var f, hash, bucket;
610+
for ( var entity in entries ) {
611+
if ( entries.hasOwnProperty(entity) === false ) {
612+
continue;
613+
}
614+
f = new FilterEntity(Object.keys(entries[entity]).join(',\n'), entity);
615+
hash = makeHash(type, entity, this.domainHashMask);
616+
bucket = filters[hash];
617+
if ( bucket === undefined ) {
618+
filters[hash] = f;
619+
} else if ( bucket instanceof FilterBucket ) {
620+
bucket.add(f);
621+
} else {
622+
filters[hash] = new FilterBucket(bucket, f);
623+
}
624+
}
625+
this[what] = {};
626+
};
627+
628+
/******************************************************************************/
629+
512630
FilterContainer.prototype.freezeHighGenerics = function(what) {
513631
var selectors = this['highGeneric' + what];
514632

@@ -564,6 +682,8 @@ FilterContainer.prototype.freeze = function() {
564682
this.freezeHighGenerics('Donthide');
565683
this.freezeHostnameSpecifics('hostnameHide', 0);
566684
this.freezeHostnameSpecifics('hostnameDonthide', 1);
685+
this.freezeEntitySpecifics('entityHide', 0);
686+
this.freezeEntitySpecifics('entityDonthide', 1);
567687
this.filterParser.reset();
568688
this.frozen = true;
569689

@@ -573,11 +693,13 @@ FilterContainer.prototype.freeze = function() {
573693

574694
/******************************************************************************/
575695

576-
FilterContainer.prototype.addToSelectorCache = function(hostname, selectors) {
696+
FilterContainer.prototype.addToSelectorCache = function(details) {
697+
var hostname = details.hostname;
577698
if ( typeof hostname !== 'string' || hostname === '' ) {
578699
return;
579700
}
580-
if ( typeof selectors !== 'string' || selectors === '' ) {
701+
var selectors = details.selectors;
702+
if ( !selectors ) {
581703
return;
582704
}
583705
var entry = this.selectorCache[hostname];
@@ -588,17 +710,17 @@ FilterContainer.prototype.addToSelectorCache = function(hostname, selectors) {
588710
this.pruneSelectorCache();
589711
}
590712
}
591-
entry.add(selectors);
713+
entry.add(selectors, details.type);
592714
};
593715

594716
/******************************************************************************/
595717

596-
FilterContainer.prototype.retrieveFromSelectorCache = function(hostname, out) {
718+
FilterContainer.prototype.retrieveFromSelectorCache = function(hostname, type, out) {
597719
var entry = this.selectorCache[hostname];
598720
if ( entry === undefined ) {
599721
return;
600722
}
601-
entry.retrieve(out);
723+
entry.retrieve(type, out);
602724
};
603725

604726
/******************************************************************************/
@@ -691,30 +813,41 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
691813
//quickProfiler.start('FilterContainer.retrieve()');
692814

693815
var hostname = µb.URI.hostnameFromURI(request.locationURL);
816+
var domain = µb.URI.domainFromHostname(hostname);
817+
var pos = domain.indexOf('.');
818+
694819
var r = {
695-
domain: µb.URI.domainFromHostname(hostname),
696-
hide: [],
697-
donthide: []
820+
domain: domain,
821+
entity: pos === -1 ? domain : domain.slice(0, pos - domain.length),
822+
cosmeticHide: [],
823+
cosmeticDonthide: [],
824+
netHide: [],
825+
netCollapse: µb.userSettings.collapseBlocked
698826
};
699827

700828
var hash, bucket;
701829
hash = makeHash(0, r.domain, this.domainHashMask);
702830
if ( bucket = this.hostnameFilters[hash] ) {
703-
bucket.retrieve(hostname, r.hide);
831+
bucket.retrieve(hostname, r.cosmeticHide);
832+
}
833+
hash = makeHash(0, r.entity, this.domainHashMask);
834+
if ( bucket = this.entityFilters[hash] ) {
835+
bucket.retrieve(pos === -1 ? domain : hostname.slice(0, pos - domain.length), r.cosmeticHide);
704836
}
705837
hash = makeHash(1, r.domain, this.domainHashMask);
706838
if ( bucket = this.hostnameFilters[hash] ) {
707-
bucket.retrieve(hostname, r.donthide);
839+
bucket.retrieve(hostname, r.cosmeticDonthide);
708840
}
709841

710-
this.retrieveFromSelectorCache(hostname, r.hide);
842+
this.retrieveFromSelectorCache(hostname, 'cosmetic', r.cosmeticHide);
843+
this.retrieveFromSelectorCache(hostname, 'net', r.netHide);
711844

712845
//quickProfiler.stop();
713846

714847
//console.log(
715848
// 'µBlock> abp-hide-filters.js: "%s" => %d selectors out',
716849
// request.locationURL,
717-
// r.hide.length + r.donthide.length
850+
// r.cosmeticHide.length + r.cosmeticDonthide.length
718851
//);
719852

720853
return r;

0 commit comments

Comments
 (0)