diff --git a/platform/chromium/vapi-client.js b/platform/chromium/vapi-client.js index dd954e20c..0dd976fff 100644 --- a/platform/chromium/vapi-client.js +++ b/platform/chromium/vapi-client.js @@ -67,6 +67,114 @@ if ( vAPI.sessionId ) { /******************************************************************************/ +// Support minimally working Set() for legacy Chromium. + +if ( self.Set instanceof Function ) { + self.createSet = function() { + return new Set(); + }; +} else { + self.createSet = (function() { + //console.log('Polyfilling for ES6-like Set().'); + var PrimitiveSet = function() { + this.clear(); + }; + PrimitiveSet.prototype = { + add: function(k) { + if ( this._set[k] === undefined ) { + this._set[k] = true; + this.size += 1; + } + return this; + }, + clear: function() { + this._set = Object.create(null); + this.size = 0; + this._values = undefined; + this._i = undefined; + this.value = undefined; + this.done = true; + }, + delete: function(k) { + if ( this._set[k] === undefined ) { return false; } + delete this._set[k]; + this.size -= 1; + return true; + }, + has: function(k) { + return this._set[k] !== undefined; + }, + 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; + }, + polyfill: true, + values: function() { + this._values = Object.keys(this._set); + this._i = 0; + this.value = undefined; + this.done = false; + return this; + } + }; + var ReferenceSet = function() { + this.clear(); + }; + ReferenceSet.prototype = { + add: function(k) { + if ( this._set.indexOf(k) === -1 ) { + this._set.push(k); + } + }, + clear: function() { + this._set = []; + this._i = 0; + this.value = undefined; + this.done = true; + }, + delete: function(k) { + var pos = this._set.indexOf(k); + if ( pos === -1 ) { return false; } + this._set.splice(pos, 1); + return true; + }, + has: function(k) { + return this._set.indexOf(k) !== -1; + }, + next: function() { + if ( this._i === this._set.length ) { + this.value = undefined; + this.done = true; + } else { + this.value = this._set[this._i]; + this._i += 1; + } + return this; + }, + polyfill: true, + values: function() { + this._i = 0; + this.done = false; + return this; + } + }; + Object.defineProperty(ReferenceSet.prototype, 'size', { + get: function() { return this._set.length; } + }); + return function(type) { + return type === 'object' ? new ReferenceSet() : new PrimitiveSet(); + }; + })(); +} + +/******************************************************************************/ + var referenceCounter = 0; vAPI.lock = function() { diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 13025f672..c9f8ccf12 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -49,6 +49,46 @@ var vAPI = self.vAPI = self.vAPI || {}; /******************************************************************************/ +// Support minimally working Set() for legacy Firefox: iterator for legacy +// Set() does not work like the one for standard ES6 Set(). + +if ( self.Set.prototype.iterator instanceof Function === false ) { + self.createSet = function() { + return new Set(); + }; +} else { + self.createSet = (function() { + //console.log('Patching non-ES6 Set() to be more ES6-like.'); + var values = function() { + this._valueIter = this.prototype.values(); + this.value = undefined; + this.done = false; + return this; + }; + var next = function() { + try { + this.value = this._valueIter.next(); + } catch (ex) { + this._valueIter = undefined; + this.value = undefined; + this.done = true; + } + return this; + }; + return function() { + var r = new Set(); + r._valueIter = undefined; + r.value = undefined; + r.done = false; + r.values = values.bind(r); + r.next = next.bind(r); + return r; + }; + })(); +} + +/******************************************************************************/ + var referenceCounter = 0; vAPI.lock = function() { diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 9e20dbc5d..4e158cb0c 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -19,6 +19,8 @@ Home: https://github.com/gorhill/uBlock */ +/* global createSet */ + 'use strict'; /******************************************************************************* @@ -100,50 +102,6 @@ vAPI.domFilterer = (function() { /******************************************************************************/ -if ( typeof self.Set !== 'function' ) { - self.Set = function() { - this._set = []; - this._i = 0; - this.value = undefined; - }; - self.Set.prototype = { - polyfill: true, - clear: function() { - this._set = []; - }, - add: function(k) { - if ( this._set.indexOf(k) === -1 ) { - this._set.push(k); - } - }, - delete: function(k) { - var pos = this._set.indexOf(k); - if ( pos !== -1 ) { - this._set.splice(pos, 1); - return true; - } - return false; - }, - has: function(k) { - return this._set.indexOf(k) !== -1; - }, - values: function() { - this._i = 0; - return this; - }, - next: function() { - this.value = this._set[this._i]; - this._i += 1; - return this; - } - }; - Object.defineProperty(self.Set.prototype, 'size', { - get: function() { return this._set.length; } - }); -} - -/******************************************************************************/ - var shadowId = document.documentElement.shadowRoot !== undefined ? vAPI.randomToken(): undefined; @@ -157,8 +115,8 @@ var jobQueue = [ var reParserEx = /:(?:matches-css|has|style|xpath)\(.+?\)$/; -var allExceptions = Object.create(null), - allSelectors = Object.create(null), +var allExceptions = createSet(), + allSelectors = createSet(), commitTimer = null, stagedNodes = [], matchesProp = vAPI.matchesProp, @@ -168,7 +126,7 @@ var allExceptions = Object.create(null), // Set() is used to implement this functionality. var complexSelectorsOldResultSet, - complexSelectorsCurrentResultSet = new Set(); + complexSelectorsCurrentResultSet = createSet('object'); /******************************************************************************/ @@ -308,7 +266,7 @@ var domFilterer = { addExceptions: function(aa) { for ( var i = 0, n = aa.length; i < n; i++ ) { - allExceptions[aa[i]] = true; + allExceptions.add(aa[i]); } }, @@ -324,10 +282,10 @@ var domFilterer = { // xpath/hide addSelector: function(s) { - if ( allSelectors[s] || allExceptions[s] ) { + if ( allSelectors.has(s) || allExceptions.has(s) ) { return; } - allSelectors[s] = true; + allSelectors.add(s); var sel0 = s, sel1 = ''; if ( s.charCodeAt(s.length - 1) === 0x29 ) { var parts = reParserEx.exec(s); @@ -445,7 +403,7 @@ var domFilterer = { // Complex selectors: non-incremental. complexSelectorsOldResultSet = complexSelectorsCurrentResultSet; - complexSelectorsCurrentResultSet = new Set(); + complexSelectorsCurrentResultSet = createSet('object'); // Stock job 3 = complex css selectors/hide // The handling of these can be considered optional, since they are @@ -1193,7 +1151,7 @@ vAPI.domSurveyor = (function() { cosmeticSurveyingMissCount = 0, highGenerics = null, lowGenericSelectors = [], - queriedSelectors = Object.create(null), + queriedSelectors = createSet(), surveyCost = 0; // Handle main process' response. @@ -1454,28 +1412,19 @@ vAPI.domSurveyor = (function() { v = node.id; if ( v !== '' && typeof v === 'string' ) { v = '#' + v.trim(); - if ( v !== '#' && qq[v] === undefined ) { - ll.push(v); - qq[v] = true; - } + if ( v !== '#' && !qq.has(v) ) { ll.push(v); qq.add(v); } } vv = node.className; if ( vv === '' || typeof vv !== 'string' ) { continue; } if ( /\s/.test(vv) === false ) { v = '.' + vv; - if ( qq[v] === undefined ) { - ll.push(v); - qq[v] = true; - } + if ( !qq.has(v) ) { ll.push(v); qq.add(v); } } else { vv = node.classList; j = vv.length; while ( j-- ) { v = '.' + vv[j]; - if ( qq[v] === undefined ) { - ll.push(v); - qq[v] = true; - } + if ( !qq.has(v) ) { ll.push(v); qq.add(v); } } } }