Skip to content

Commit 41685f4

Browse files
committed
Replace exec with transpose in procedural filters
The purpose is to avoid having to iterate through all input nodes at each operator implementation level. The `transpose` method deals with only one input node, and the iteration is performed by the main procedural filtering entry points. Additionally: - Add `:spath` to HTML filtering - Rename `:watch-attrs` to `:watch-attr` - `:watch=attrs` is deprecated and will be kept around until it is safe to remove it completely
1 parent 4956a16 commit 41685f4

File tree

3 files changed

+140
-169
lines changed

3 files changed

+140
-169
lines changed

src/js/contentscript.js

+68-104
Original file line numberDiff line numberDiff line change
@@ -469,39 +469,28 @@ vAPI.DOMFilterer = (function() {
469469
}
470470
this.needle = new RegExp(arg0, arg1);
471471
}
472-
exec(input) {
473-
const output = [];
474-
for ( const node of input ) {
475-
if ( this.needle.test(node.textContent) ) {
476-
output.push(node);
477-
}
472+
transpose(node, output) {
473+
if ( this.needle.test(node.textContent) ) {
474+
output.push(node);
478475
}
479-
return output;
480476
}
481477
};
482478

483479
const PSelectorIfTask = class {
484480
constructor(task) {
485481
this.pselector = new PSelector(task[1]);
486482
}
487-
exec(input) {
488-
const output = [];
489-
for ( const node of input ) {
490-
if ( this.pselector.test(node) === this.target ) {
491-
output.push(node);
492-
}
483+
transpose(node, output) {
484+
if ( this.pselector.test(node) === this.target ) {
485+
output.push(node);
493486
}
494-
return output;
495487
}
496488
};
497489
PSelectorIfTask.prototype.target = true;
498490

499491
const PSelectorIfNotTask = class extends PSelectorIfTask {
500-
constructor(task) {
501-
super(task);
502-
this.target = false;
503-
}
504492
};
493+
PSelectorIfNotTask.prototype.target = false;
505494

506495
const PSelectorMatchesCSSTask = class {
507496
constructor(task) {
@@ -512,93 +501,69 @@ vAPI.DOMFilterer = (function() {
512501
}
513502
this.value = new RegExp(arg0, arg1);
514503
}
515-
exec(input) {
516-
const output = [];
517-
for ( const node of input ) {
518-
const style = window.getComputedStyle(node, this.pseudo);
519-
if ( style === null ) { return null; } /* FF */
520-
if ( this.value.test(style[this.name]) ) {
521-
output.push(node);
522-
}
504+
transpose(node, output) {
505+
const style = window.getComputedStyle(node, this.pseudo);
506+
if ( style !== null && this.value.test(style[this.name]) ) {
507+
output.push(node);
523508
}
524-
return output;
525509
}
526510
};
527511
PSelectorMatchesCSSTask.prototype.pseudo = null;
528512

529513
const PSelectorMatchesCSSAfterTask = class extends PSelectorMatchesCSSTask {
530-
constructor(task) {
531-
super(task);
532-
this.pseudo = ':after';
533-
}
534514
};
515+
PSelectorMatchesCSSAfterTask.prototype.pseudo = ':after';
535516

536517
const PSelectorMatchesCSSBeforeTask = class extends PSelectorMatchesCSSTask {
537-
constructor(task) {
538-
super(task);
539-
this.pseudo = ':before';
540-
}
541518
};
519+
PSelectorMatchesCSSBeforeTask.prototype.pseudo = ':before';
542520

543521
const PSelectorMinTextLengthTask = class {
544522
constructor(task) {
545523
this.min = task[1];
546524
}
547-
exec(input) {
548-
const output = [];
549-
for ( const node of input ) {
550-
if ( node.textContent.length >= this.min ) {
551-
output.push(node);
552-
}
525+
transpose(node, output) {
526+
if ( node.textContent.length >= this.min ) {
527+
output.push(node);
553528
}
554-
return output;
555529
}
556530
};
557531

558532
const PSelectorNthAncestorTask = class {
559533
constructor(task) {
560534
this.nth = task[1];
561535
}
562-
exec(input) {
563-
const output = [];
564-
for ( let node of input ) {
565-
let nth = this.nth;
566-
for (;;) {
567-
node = node.parentElement;
568-
if ( node === null ) { break; }
569-
nth -= 1;
570-
if ( nth !== 0 ) { continue; }
571-
output.push(node);
572-
break;
573-
}
536+
transpose(node, output) {
537+
let nth = this.nth;
538+
for (;;) {
539+
node = node.parentElement;
540+
if ( node === null ) { return; }
541+
nth -= 1;
542+
if ( nth === 0 ) { break; }
574543
}
575-
return output;
544+
output.push(node);
576545
}
577546
};
578547

579548
const PSelectorSpathTask = class {
580549
constructor(task) {
581550
this.spath = task[1];
582551
}
583-
exec(input) {
584-
const output = [];
585-
for ( let node of input ) {
586-
const parent = node.parentElement;
587-
if ( parent === null ) { continue; }
588-
let pos = 1;
589-
for (;;) {
590-
node = node.previousElementSibling;
591-
if ( node === null ) { break; }
592-
pos += 1;
593-
}
594-
const nodes = parent.querySelectorAll(
595-
':scope > :nth-child(' + pos + ')' + this.spath
596-
);
597-
for ( const node of nodes ) {
598-
output.push(node);
599-
}
552+
transpose(node, output) {
553+
const parent = node.parentElement;
554+
if ( parent === null ) { return; }
555+
let pos = 1;
556+
for (;;) {
557+
node = node.previousElementSibling;
558+
if ( node === null ) { break; }
559+
pos += 1;
560+
}
561+
const nodes = parent.querySelectorAll(
562+
`:scope > :nth-child(${pos})${this.spath}`
563+
);
564+
for ( const node of nodes ) {
565+
output.push(node);
600566
}
601-
return output;
602567
}
603568
};
604569

@@ -623,17 +588,14 @@ vAPI.DOMFilterer = (function() {
623588
filterer.onDOMChanged([ null ]);
624589
}
625590
}
626-
exec(input) {
627-
if ( input.length === 0 ) { return input; }
591+
transpose(node, output) {
592+
output.push(node);
593+
if ( this.observed.has(node) ) { return; }
628594
if ( this.observer === null ) {
629595
this.observer = new MutationObserver(this.handler);
630596
}
631-
for ( const node of input ) {
632-
if ( this.observed.has(node) ) { continue; }
633-
this.observer.observe(node, this.observerOptions);
634-
this.observed.add(node);
635-
}
636-
return input;
597+
this.observer.observe(node, this.observerOptions);
598+
this.observed.add(node);
637599
}
638600
};
639601

@@ -642,23 +604,19 @@ vAPI.DOMFilterer = (function() {
642604
this.xpe = document.createExpression(task[1], null);
643605
this.xpr = null;
644606
}
645-
exec(input) {
646-
const output = [];
647-
for ( const node of input ) {
648-
this.xpr = this.xpe.evaluate(
649-
node,
650-
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
651-
this.xpr
652-
);
653-
let j = this.xpr.snapshotLength;
654-
while ( j-- ) {
655-
const node = this.xpr.snapshotItem(j);
656-
if ( node.nodeType === 1 ) {
657-
output.push(node);
658-
}
607+
transpose(node, output) {
608+
this.xpr = this.xpe.evaluate(
609+
node,
610+
XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
611+
this.xpr
612+
);
613+
let j = this.xpr.snapshotLength;
614+
while ( j-- ) {
615+
const node = this.xpr.snapshotItem(j);
616+
if ( node.nodeType === 1 ) {
617+
output.push(node);
659618
}
660619
}
661-
return output;
662620
}
663621
};
664622

@@ -677,7 +635,7 @@ vAPI.DOMFilterer = (function() {
677635
[ ':not', PSelectorIfNotTask ],
678636
[ ':nth-ancestor', PSelectorNthAncestorTask ],
679637
[ ':spath', PSelectorSpathTask ],
680-
[ ':watch-attrs', PSelectorWatchAttrs ],
638+
[ ':watch-attr', PSelectorWatchAttrs ],
681639
[ ':xpath', PSelectorXpathTask ],
682640
]);
683641
}
@@ -704,21 +662,27 @@ vAPI.DOMFilterer = (function() {
704662
let nodes = this.prime(input);
705663
for ( const task of this.tasks ) {
706664
if ( nodes.length === 0 ) { break; }
707-
nodes = task.exec(nodes);
665+
const transposed = [];
666+
for ( const node of nodes ) {
667+
task.transpose(node, transposed);
668+
}
669+
nodes = transposed;
708670
}
709671
return nodes;
710672
}
711673
test(input) {
712674
const nodes = this.prime(input);
713-
const AA = [ null ];
714675
for ( const node of nodes ) {
715-
AA[0] = node;
716-
let aa = AA;
676+
let output = [ node ];
717677
for ( const task of this.tasks ) {
718-
aa = task.exec(aa);
719-
if ( aa.length === 0 ) { break; }
678+
const transposed = [];
679+
for ( const node of output ) {
680+
task.transpose(node, transposed);
681+
}
682+
output = transposed;
683+
if ( output.length === 0 ) { break; }
720684
}
721-
if ( aa.length !== 0 ) { return true; }
685+
if ( output.length !== 0 ) { return true; }
722686
}
723687
return false;
724688
}

0 commit comments

Comments
 (0)