From e9157bafb7446cdca4ba16ee67e576c39c18174d Mon Sep 17 00:00:00 2001 From: gorhill Date: Sat, 13 Aug 2016 16:42:58 -0400 Subject: [PATCH] fix #1892, #1891 --- platform/firefox/vapi-background.js | 4 + src/3p-filters.html | 9 +- src/js/3p-filters.js | 46 +++--- src/js/background.js | 3 +- src/js/contentscript.js | 210 ++++++++++++++-------------- src/js/cosmetic-filtering.js | 201 +++++++++++++++++++------- src/js/dashboard-common.js | 1 + src/js/messaging.js | 25 ++-- src/js/start.js | 17 ++- src/js/static-net-filtering.js | 18 +-- src/js/storage.js | 16 +-- src/js/utils.js | 29 +++- 12 files changed, 360 insertions(+), 219 deletions(-) diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 324651d97..52485c6fd 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -42,6 +42,10 @@ vAPI.firefox = true; vAPI.fennec = Services.appinfo.ID === '{aa3c5121-dab2-40e2-81ca-7ea25febc110}'; vAPI.thunderbird = Services.appinfo.ID === '{3550f703-e582-4d05-9a08-453d09bdfdc6}'; +if ( vAPI.fennec ) { + vAPI.battery = true; +} + /******************************************************************************/ var deferUntil = function(testFn, mainFn, details) { diff --git a/src/3p-filters.html b/src/3p-filters.html index 5d378d4e5..8bce9d01a 100644 --- a/src/3p-filters.html +++ b/src/3p-filters.html @@ -19,9 +19,12 @@
  • -
  • - -
    +
  • +
    +
  • diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index 03dc77548..54bfda204 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -31,7 +31,8 @@ var userListName = vAPI.i18n('1pPageName'); var listDetails = {}; -var cosmeticSwitch = true; +var parseCosmeticFilters = true; +var ignoreGenericCosmeticFilters = false; var externalLists = ''; var cacheWasPurged = false; var needUpdate = false; @@ -216,7 +217,8 @@ var renderFilterLists = function() { var onListsReceived = function(details) { // Before all, set context vars listDetails = details; - cosmeticSwitch = details.cosmetic; + parseCosmeticFilters = details.parseCosmeticFilters; + ignoreGenericCosmeticFilters = details.ignoreGenericCosmeticFilters; needUpdate = false; hasCachedContent = false; @@ -257,7 +259,8 @@ var renderFilterLists = function() { .replace('{{cosmeticFilterCount}}', renderNumber(details.cosmeticFilterCount)) ); uDom('#autoUpdate').prop('checked', listDetails.autoUpdate === true); - uDom('#parseCosmeticFilters').prop('checked', listDetails.cosmetic === true); + uDom('#parseCosmeticFilters').prop('checked', listDetails.parseCosmeticFilters === true); + uDom('#ignoreGenericCosmeticFilters').prop('checked', listDetails.ignoreGenericCosmeticFilters === true); renderWidgets(); renderBusyOverlay(details.manualUpdate, details.manualUpdateProgress); @@ -302,7 +305,10 @@ var renderWidgets = function() { // Return whether selection of lists changed. var listsSelectionChanged = function() { - if ( listDetails.cosmetic !== cosmeticSwitch ) { + if ( + listDetails.parseCosmeticFilters !== parseCosmeticFilters || + listDetails.parseCosmeticFilters && listDetails.ignoreGenericCosmeticFilters !== ignoreGenericCosmeticFilters + ) { return true; } @@ -401,14 +407,16 @@ var onPurgeClicked = function() { var selectFilterLists = function(callback) { // Cosmetic filtering switch - messaging.send( - 'dashboard', - { - what: 'userSettings', - name: 'parseAllABPHideFilters', - value: listDetails.cosmetic - } - ); + messaging.send('dashboard', { + what: 'userSettings', + name: 'parseAllABPHideFilters', + value: listDetails.parseCosmeticFilters + }); + messaging.send('dashboard', { + what: 'userSettings', + name: 'ignoreGenericCosmeticFilters', + value: listDetails.ignoreGenericCosmeticFilters + }); // Filter lists var switches = []; @@ -497,7 +505,8 @@ var autoUpdateCheckboxChanged = function() { /******************************************************************************/ var cosmeticSwitchChanged = function() { - listDetails.cosmetic = this.checked; + listDetails.parseCosmeticFilters = uDom.nodeFromId('parseCosmeticFilters').checked; + listDetails.ignoreGenericCosmeticFilters = uDom.nodeFromId('ignoreGenericCosmeticFilters').checked; renderWidgets(); }; @@ -556,6 +565,7 @@ var groupEntryClickHandler = function() { var getCloudData = function() { var bin = { parseCosmeticFilters: uDom.nodeFromId('parseCosmeticFilters').checked, + ignoreGenericCosmeticFilters: uDom.nodeFromId('ignoreGenericCosmeticFilters').checked, selectedLists: [], externalLists: externalLists }; @@ -580,9 +590,12 @@ var setCloudData = function(data, append) { var elem, checked; elem = uDom.nodeFromId('parseCosmeticFilters'); - checked = data.parseCosmeticFilters === true || - append && elem.checked; - elem.checked = listDetails.cosmetic = checked; + checked = data.parseCosmeticFilters === true || append && elem.checked; + elem.checked = listDetails.parseCosmeticFilters = checked; + + elem = uDom.nodeFromId('ignoreGenericCosmeticFilters'); + checked = data.ignoreGenericCosmeticFilters === true || append && elem.checked; + elem.checked = listDetails.ignoreGenericCosmeticFilters = checked; var lis = uDom('#lists .listEntry'), li, listKey; var i = lis.length; @@ -613,6 +626,7 @@ self.cloud.onPull = setCloudData; uDom('#autoUpdate').on('change', autoUpdateCheckboxChanged); uDom('#parseCosmeticFilters').on('change', cosmeticSwitchChanged); +uDom('#ignoreGenericCosmeticFilters').on('change', cosmeticSwitchChanged); uDom('#buttonApply').on('click', buttonApplyHandler); uDom('#buttonUpdate').on('click', buttonUpdateHandler); uDom('#buttonPurgeAll').on('click', buttonPurgeAllHandler); diff --git a/src/js/background.js b/src/js/background.js index d0d92f754..bd5d07003 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -59,6 +59,7 @@ return { externalLists: defaultExternalLists, firewallPaneMinimized: true, hyperlinkAuditingDisabled: true, + ignoreGenericCosmeticFilters: false, largeMediaSize: 50, parseAllABPHideFilters: true, prefetchingDisabled: true, @@ -92,7 +93,7 @@ return { // read-only systemSettings: { compiledMagic: 'splsmclwnvoj', - selfieMagic: 'splsmclwnvoj' + selfieMagic: 'rkzqonintytj' }, restoreBackupSettings: { diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 4fa8a34a4..ae813bc7f 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -280,7 +280,8 @@ var runXpathJob = function(job, fn) { /******************************************************************************/ var domFilterer = { - commitMissCount: 0, + addedNodesHandlerMissCount: 0, + removedNodesHandlerMissCount: 0, disabledId: vAPI.randomToken(), enabled: true, hiddenId: vAPI.randomToken(), @@ -387,7 +388,7 @@ var domFilterer = { mustCommit = true; } if ( mustCommit && commitIfNeeded ) { - this.commit(); + this.commit('all'); } return mustCommit; }, @@ -397,6 +398,8 @@ var domFilterer = { return; } + vAPI.executionCost.start(); + var beforeHiddenNodeCount = this.hiddenNodeCount, styleText = '', i, n; @@ -452,9 +455,9 @@ var domFilterer = { var commitHit = this.hiddenNodeCount !== beforeHiddenNodeCount; if ( commitHit ) { - this.commitMissCount = 0; + this.addedNodesHandlerMissCount = 0; } else { - this.commitMissCount += 1; + this.addedNodesHandlerMissCount += 1; } // Un-hide nodes previously hidden. @@ -478,11 +481,13 @@ var domFilterer = { 503 ); } + + vAPI.executionCost.stop('domFilterer.commit_'); }, commit: function(nodes, commitNow) { var firstCommit = stagedNodes.length === 0; - if ( nodes === undefined ) { + if ( nodes === 'all' ) { stagedNodes = [ document.documentElement ]; } else if ( stagedNodes[0] !== document.documentElement ) { stagedNodes = stagedNodes.concat(nodes); @@ -601,6 +606,29 @@ var domFilterer = { if ( shadow && shadow[shadowId] && shadow.firstElementChild !== null ) { shadow.removeChild(shadow.firstElementChild); } + }, + + domChangedHandler: function(addedNodes, removedNodes) { + if ( addedNodes.length !== 0 ) { + this.commit(addedNodes); + } + // https://github.com/gorhill/uBlock/issues/873 + // This will ensure our style elements will stay in the DOM. + if ( removedNodes && this.removedNodesHandlerMissCount < 16 ) { + if ( domFilterer.checkStyleTags(true) ) { + this.removedNodesHandlerMissCount = 0; + } else { + this.removedNodesHandlerMissCount += 1; + } + } + }, + + start: function() { + var domChangedHandler = this.domChangedHandler.bind(this); + vAPI.domWatcher.addListener(domChangedHandler); + vAPI.shutdown.add(function() { + vAPI.domWatcher.removeListener(domChangedHandler); + }); } }; @@ -660,7 +688,7 @@ return domFilterer; if ( cfeDetails.cosmeticHide.length !== 0 || cfeDetails.cosmeticDonthide.length !== 0 ) { domFilterer.addExceptions(cfeDetails.cosmeticDonthide); domFilterer.addSelectors(cfeDetails.cosmeticHide); - domFilterer.commit(undefined, true); + domFilterer.commit('all', true); } } @@ -759,7 +787,7 @@ vAPI.domWatcher = (function() { } } addedNodeLists.length = 0; - if ( addedNodes.length !== 0 ) { + if ( addedNodes.length !== 0 || removedNodes ) { listeners[0](addedNodes, removedNodes); if ( listeners[1] ) { listeners[1](addedNodes, removedNodes); @@ -825,26 +853,16 @@ vAPI.domWatcher = (function() { domLayoutObserver = null; }; - var stop = function() { - if ( domLayoutObserver !== null ) { - domLayoutObserver.disconnect(); - domLayoutObserver = null; - } - if ( safeObserverHandlerTimer !== null ) { - window.cancelAnimationFrame(safeObserverHandlerTimer); - } - }; - var start = function() { - // Observe changes in the DOM only if... - // - there is a document.body - // - there is at least one `script` tag - if ( document.body === null || document.querySelector('script') === null ) { - vAPI.domWatcher = null; - return; - } - // https://github.com/gorhill/uMatrix/issues/144 - vAPI.shutdown.add(stop); + vAPI.shutdown.add(function() { + if ( domLayoutObserver !== null ) { + domLayoutObserver.disconnect(); + domLayoutObserver = null; + } + if ( safeObserverHandlerTimer !== null ) { + window.cancelAnimationFrame(safeObserverHandlerTimer); + } + }); }; return { @@ -887,16 +905,11 @@ vAPI.domCollapser = (function() { if ( !response ) { return; } - // https://github.com/gorhill/uMatrix/issues/144 - if ( response.shutdown ) { - vAPI.shutdown.exec(); - return; - } - var requests = response.result; if ( requests === null || Array.isArray(requests) === false ) { return; } + vAPI.executionCost.start(); var selectors = [], netSelectorCacheCountMax = response.netSelectorCacheCountMax, aa = [ null ], @@ -947,9 +960,11 @@ vAPI.domCollapser = (function() { } ); } + vAPI.executionCost.stop('domCollapser/onProcessed'); }; var send = function() { + vAPI.executionCost.start(); timer = null; messaging.send( 'contentscript', @@ -961,6 +976,7 @@ vAPI.domCollapser = (function() { }, onProcessed ); roundtripRequests = []; + vAPI.executionCost.stop('domCollapser/send'); }; var process = function(delay) { @@ -1084,6 +1100,13 @@ vAPI.domCollapser = (function() { } }; + var onResourceFailed = function(ev) { + vAPI.executionCost.start(); + vAPI.domCollapser.add(ev.target); + vAPI.domCollapser.process(); + vAPI.executionCost.stop('domCollapser/onResourceFailed'); + }; + var domChangedHandler = function(nodes) { var node; for ( var i = 0, ni = nodes.length; i < ni; i++ ) { @@ -1101,13 +1124,6 @@ vAPI.domCollapser = (function() { process(); }; - var onResourceFailed = function(ev) { - vAPI.executionCost.start(); - vAPI.domCollapser.add(ev.target); - vAPI.domCollapser.process(); - vAPI.executionCost.stop('domIsLoaded/onResourceFailed'); - }; - var start = function() { // Listener to collapse blocked resources. // - Future requests not blocked yet @@ -1131,15 +1147,13 @@ vAPI.domCollapser = (function() { process(0); document.addEventListener('error', onResourceFailed, true); - if ( vAPI.domWatcher ) { - vAPI.domWatcher.addListener(domChangedHandler); - } + vAPI.domWatcher.addListener(domChangedHandler); - // https://github.com/gorhill/uMatrix/issues/144 vAPI.shutdown.add(function() { document.removeEventListener('error', onResourceFailed, true); - if ( vAPI.domWatcher ) { - vAPI.domWatcher.removeListener(domChangedHandler); + vAPI.domWatcher.removeListener(domChangedHandler); + if ( timer !== null ) { + clearTimeout(timer); } }); }; @@ -1159,29 +1173,17 @@ vAPI.domCollapser = (function() { /******************************************************************************/ vAPI.domSurveyor = (function() { - // https://github.com/chrisaljoudi/uBlock/issues/789 - // https://github.com/gorhill/uBlock/issues/873 - // Be sure that our style tags used for cosmetic filtering are still - // applied. - var domFilterer = null, messaging = vAPI.messaging, surveyPhase3Nodes = [], cosmeticSurveyingMissCount = 0, highGenerics = null, lowGenericSelectors = [], - queriedSelectors = Object.create(null), - removedNodesHandlerMissCount = 0; + queriedSelectors = Object.create(null); // Handle main process' response. var surveyPhase3 = function(response) { - // https://github.com/gorhill/uMatrix/issues/144 - if ( response && response.shutdown ) { - vAPI.shutdown.exec(); - return; - } - vAPI.executionCost.start(); var result = response && response.result, @@ -1232,7 +1234,7 @@ vAPI.domSurveyor = (function() { domFilterer.commit(surveyPhase3Nodes); surveyPhase3Nodes = []; - vAPI.executionCost.stop('domIsLoaded/surveyPhase2'); + vAPI.executionCost.stop('domSurveyor/surveyPhase3'); }; // Query main process. @@ -1465,31 +1467,27 @@ vAPI.domSurveyor = (function() { }; var domChangedHandler = function(addedNodes, removedNodes) { - if ( cosmeticSurveyingMissCount < 256 ) { - surveyPhase1(addedNodes); - } else { - domFilterer.commit(addedNodes); + if ( cosmeticSurveyingMissCount > 255 ) { + vAPI.domWatcher.removeListener(domChangedHandler); + vAPI.domSurveyor = null; + domFilterer.domChangedHandler(addedNodes, removedNodes); + domFilterer.start(); + return; } - // https://github.com/gorhill/uBlock/issues/873 - // This will ensure our style elements will stay in the DOM. - if ( removedNodes && removedNodesHandlerMissCount < 16 ) { - if ( domFilterer.checkStyleTags(true) === false ) { - removedNodesHandlerMissCount += 1; - } + + surveyPhase1(addedNodes); + if ( removedNodes ) { + domFilterer.domChangedHandler([], true); } }; var start = function() { domFilterer = vAPI.domFilterer; - if ( domFilterer === null ) { - return; - } - domFilterer.checkStyleTags(false); - domFilterer.commit(); domChangedHandler([ document.documentElement ]); - if ( vAPI.domWatcher ) { - vAPI.domWatcher.addListener(domChangedHandler); - } + vAPI.domWatcher.addListener(domChangedHandler); + vAPI.shutdown.add(function() { + vAPI.domWatcher.removeListener(domChangedHandler); + }); }; return { @@ -1514,16 +1512,20 @@ vAPI.domIsLoaded = function(ev) { vAPI.executionCost.start(); - if ( vAPI.domWatcher ) { - vAPI.domWatcher.start(); - } + vAPI.domWatcher.start(); + vAPI.domCollapser.start(); - if ( vAPI.domCollapser ) { - vAPI.domCollapser.start(); - } - - if ( vAPI.domFilterer && vAPI.domSurveyor ) { - vAPI.domSurveyor.start(); + if ( vAPI.domFilterer ) { + // https://github.com/chrisaljoudi/uBlock/issues/789 + // https://github.com/gorhill/uBlock/issues/873 + // Be sure our style tags used for cosmetic filtering are still applied. + vAPI.domFilterer.checkStyleTags(false); + vAPI.domFilterer.commit('all'); + if ( vAPI.domSurveyor ) { + vAPI.domSurveyor.start(); + } else { + vAPI.domFilterer.start(); + } } // To send mouse coordinates to main process, as the chrome API fails @@ -1533,30 +1535,26 @@ vAPI.domIsLoaded = function(ev) { // as nuisance popups. // Ref.: https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu + var onMouseClick = function(ev) { + var elem = ev.target; + while ( elem !== null && elem.localName !== 'a' ) { + elem = elem.parentElement; + } + vAPI.messaging.send( + 'contentscript', + { + what: 'mouseClick', + x: ev.clientX, + y: ev.clientY, + url: elem !== null ? elem.href : '' + } + ); + }; + (function() { if ( window !== window.top || !vAPI.domFilterer ) { return; } - - var messaging = vAPI.messaging; - - var onMouseClick = function(ev) { - vAPI.executionCost.start(); - var elem = ev.target; - while ( elem !== null && elem.localName !== 'a' ) { - elem = elem.parentElement; - } - messaging.send( - 'contentscript', - { - what: 'mouseClick', - x: ev.clientX, - y: ev.clientY, - url: elem !== null ? elem.href : '' - }); - vAPI.executionCost.stop('domIsLoaded/onMouseClick'); - }; - document.addEventListener('mousedown', onMouseClick, true); // https://github.com/gorhill/uMatrix/issues/144 diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 598bfa65f..6b85a12b8 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -690,9 +690,12 @@ FilterContainer.prototype.reset = function() { this.selectorCacheTimer = null; } - // permanent + // generic filters + this.hasGenericHide = false; + // [class], [id] this.lowGenericHide = {}; + this.lowGenericHideCount = 0; // [alt="..."], [title="..."] this.highLowGenericHide = {}; @@ -726,6 +729,33 @@ FilterContainer.prototype.reset = function() { /******************************************************************************/ +FilterContainer.prototype.freeze = function() { + this.duplicateBuster = {}; + + if ( this.highHighSimpleGenericHide !== '' ) { + this.highHighSimpleGenericHideArray.unshift(this.highHighSimpleGenericHide); + } + this.highHighSimpleGenericHide = this.highHighSimpleGenericHideArray.join(',\n'); + this.highHighSimpleGenericHideArray = []; + + if ( this.highHighComplexGenericHide !== '' ) { + this.highHighComplexGenericHideArray.unshift(this.highHighComplexGenericHide); + } + this.highHighComplexGenericHide = this.highHighComplexGenericHideArray.join(',\n'); + this.highHighComplexGenericHideArray = []; + + this.hasGenericHide = this.lowGenericHideCount !== 0 || + this.highLowGenericHideCount !== 0 || + this.highMediumGenericHideCount !== 0 || + this.highHighSimpleGenericHideCount !== 0 || + this.highHighComplexGenericHideCount !== 0; + + this.parser.reset(); + this.frozen = true; +}; + +/******************************************************************************/ + // https://github.com/chrisaljoudi/uBlock/issues/1004 // Detect and report invalid CSS selectors. @@ -1006,26 +1036,23 @@ FilterContainer.prototype.compileEntitySelector = function(hostname, parsed, out /******************************************************************************/ -FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { - if ( skip ) { - return this.skipCompiledContent(text, lineBeg); +FilterContainer.prototype.fromCompiledContent = function(lineIter, skipGenericCosmetic, skipCosmetic) { + if ( skipCosmetic ) { + this.skipCompiledContent(lineIter); + return; + } + if ( skipGenericCosmetic ) { + this.skipGenericCompiledContent(lineIter); + return; } - var lineEnd; - var textEnd = text.length; var line, fields, filter, key, bucket; - while ( lineBeg < textEnd ) { - if ( text.charCodeAt(lineBeg) !== 0x63 /* 'c' */ ) { - return lineBeg; + while ( lineIter.eot() === false ) { + if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + return; } - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd === -1 ) { - lineEnd = textEnd; - } - line = text.slice(lineBeg + 2, lineEnd); - lineBeg = lineEnd + 1; - + line = lineIter.next().slice(2); this.acceptedCount += 1; if ( this.duplicateBuster.hasOwnProperty(line) ) { @@ -1069,6 +1096,7 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { } else { this.lowGenericHide[fields[1]] = new FilterBucket(bucket, filter); } + this.lowGenericHideCount += 1; continue; } @@ -1124,30 +1152,114 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { // Generic exception filters: expected to be a rare occurrence. if ( fields[0] === 'g1' ) { this.genericDonthide.push(fields[1]); + continue; } + + this.discardedCount += 1; } - return textEnd; }; /******************************************************************************/ -FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) { - var lineEnd; - var textEnd = text.length; +FilterContainer.prototype.skipGenericCompiledContent = function(lineIter) { + var line, fields, filter, bucket; - while ( lineBeg < textEnd ) { - if ( text.charCodeAt(lineBeg) !== 0x63 /* 'c' */ ) { - return lineBeg; - } - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd === -1 ) { - lineEnd = textEnd; + while ( lineIter.eot() === false ) { + if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + return; } + line = lineIter.next().slice(2); + this.acceptedCount += 1; + if ( this.duplicateBuster.hasOwnProperty(line) ) { + this.discardedCount += 1; + continue; + } + + fields = line.split('\v'); + + // h [\t] ir [\t] twitter.com [\t] .promoted-tweet + if ( fields[0] === 'h' ) { + this.duplicateBuster[line] = true; + // Special filter: script tags. Not a real CSS selector. + if ( fields[3].startsWith('script') ) { + this.createScriptFilter(fields[2], fields[3].slice(6)); + continue; + } + filter = new FilterHostname(fields[3], fields[2]); + bucket = this.hostnameFilters[fields[1]]; + if ( bucket === undefined ) { + this.hostnameFilters[fields[1]] = filter; + } else if ( bucket instanceof FilterBucket ) { + bucket.add(filter); + } else { + this.hostnameFilters[fields[1]] = new FilterBucket(bucket, filter); + } + continue; + } + + // entity [\t] selector + if ( fields[0] === 'e' ) { + this.duplicateBuster[line] = true; + // Special filter: script tags. Not a real CSS selector. + if ( fields[2].startsWith('script') ) { + this.createScriptFilter(fields[1], fields[2].slice(6)); + continue; + } + bucket = this.entityFilters[fields[1]]; + if ( bucket === undefined ) { + this.entityFilters[fields[1]] = [fields[2]]; + } else { + bucket.push(fields[2]); + } + continue; + } + + // https://github.com/chrisaljoudi/uBlock/issues/497 + // Generic exception filters: expected to be a rare occurrence. + if ( fields[0] === 'g1' ) { + this.duplicateBuster[line] = true; + this.genericDonthide.push(fields[1]); + continue; + } + + this.discardedCount += 1; + } +}; + +/******************************************************************************/ + +FilterContainer.prototype.skipCompiledContent = function(lineIter) { + var line, fields; + + while ( lineIter.eot() === false ) { + if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x63 /* 'c' */ ) { + return; + } + line = lineIter.next().slice(2); + + this.acceptedCount += 1; + if ( this.duplicateBuster.hasOwnProperty(line) ) { + this.discardedCount += 1; + continue; + } + + fields = line.split('\v'); + + if ( fields[0] === 'h' && fields[3].startsWith('script') ) { + this.duplicateBuster[line] = true; + this.createScriptFilter(fields[2], fields[3].slice(6)); + continue; + } + + if ( fields[0] === 'e' && fields[2].startsWith('script') ) { + this.duplicateBuster[line] = true; + this.createScriptFilter(fields[1], fields[2].slice(6)); + continue; + } + this.discardedCount += 1; - lineBeg = lineEnd + 1; } - return textEnd; }; /******************************************************************************/ @@ -1260,27 +1372,6 @@ FilterContainer.prototype.retrieveScriptTags = function(domain, hostname) { /******************************************************************************/ -FilterContainer.prototype.freeze = function() { - this.duplicateBuster = {}; - - if ( this.highHighSimpleGenericHide !== '' ) { - this.highHighSimpleGenericHideArray.unshift(this.highHighSimpleGenericHide); - } - this.highHighSimpleGenericHide = this.highHighSimpleGenericHideArray.join(',\n'); - this.highHighSimpleGenericHideArray = []; - - if ( this.highHighComplexGenericHide !== '' ) { - this.highHighComplexGenericHideArray.unshift(this.highHighComplexGenericHide); - } - this.highHighComplexGenericHide = this.highHighComplexGenericHideArray.join(',\n'); - this.highHighComplexGenericHideArray = []; - - this.parser.reset(); - this.frozen = true; -}; - -/******************************************************************************/ - FilterContainer.prototype.toSelfie = function() { var selfieFromDict = function(dict) { var selfie = []; @@ -1312,7 +1403,9 @@ FilterContainer.prototype.toSelfie = function() { discardedCount: this.discardedCount, hostnameSpecificFilters: selfieFromDict(this.hostnameFilters), entitySpecificFilters: this.entityFilters, + hasGenericHide: this.hasGenericHide, lowGenericHide: selfieFromDict(this.lowGenericHide), + lowGenericHideCount: this.lowGenericHideCount, highLowGenericHide: this.highLowGenericHide, highLowGenericHideCount: this.highLowGenericHideCount, highMediumGenericHide: this.highMediumGenericHide, @@ -1378,7 +1471,9 @@ FilterContainer.prototype.fromSelfie = function(selfie) { this.discardedCount = selfie.discardedCount; this.hostnameFilters = dictFromSelfie(selfie.hostnameSpecificFilters); this.entityFilters = selfie.entitySpecificFilters; + this.hasGenericHide = selfie.hasGenericHide; this.lowGenericHide = dictFromSelfie(selfie.lowGenericHide); + this.lowGenericHideCount = selfie.lowGenericHideCount; this.highLowGenericHide = selfie.highLowGenericHide; this.highLowGenericHideCount = selfie.highLowGenericHideCount; this.highMediumGenericHide = selfie.highMediumGenericHide; @@ -1573,7 +1668,7 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request, noCosmetic ready: this.frozen, domain: domain, entity: pos === -1 ? domain : domain.slice(0, pos - domain.length), - noDOMSurveying: false, + noDOMSurveying: this.hasGenericHide === false, cosmeticHide: [], cosmeticDonthide: [], netHide: [], @@ -1600,7 +1695,9 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request, noCosmetic // cached cosmetic filters. if ( cacheEntry ) { cacheEntry.retrieve('cosmetic', r.cosmeticHide); - r.noDOMSurveying = cacheEntry.cosmeticSurveyingMissCount > cosmeticSurveyingMissCountMax; + if ( r.noDOMSurveying === false ) { + r.noDOMSurveying = cacheEntry.cosmeticSurveyingMissCount > cosmeticSurveyingMissCountMax; + } } // Exception cosmetic filters. diff --git a/src/js/dashboard-common.js b/src/js/dashboard-common.js index ba7660edf..7cef4f68a 100644 --- a/src/js/dashboard-common.js +++ b/src/js/dashboard-common.js @@ -110,5 +110,6 @@ uDom('.whatisthis').on('click', function() { uDom(this) .parent() .descendants('.whatisthis-expandable') + .first() .toggleClass('whatisthis-expanded'); }); diff --git a/src/js/messaging.js b/src/js/messaging.js index dc5c9c011..317f1356c 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -534,23 +534,19 @@ var onMessage = function(request, sender, callback) { break; case 'retrieveGenericCosmeticSelectors': - response = { - shutdown: !pageStore || !pageStore.getNetFilteringSwitch(), - result: null - }; - if ( !response.shutdown && pageStore.getGenericCosmeticFilteringSwitch() ) { - response.result = µb.cosmeticFilteringEngine.retrieveGenericSelectors(request); + if ( pageStore && pageStore.getGenericCosmeticFilteringSwitch() ) { + response = { + result: µb.cosmeticFilteringEngine.retrieveGenericSelectors(request) + }; } break; case 'filterRequests': - response = { - shutdown: !pageStore || !pageStore.getNetFilteringSwitch(), - result: null, - netSelectorCacheCountMax: µb.cosmeticFilteringEngine.netSelectorCacheCountMax - }; - if ( !response.shutdown ) { - response.result = filterRequests(pageStore, request); + if ( pageStore && pageStore.getNetFilteringSwitch() ) { + response = { + result: filterRequests(pageStore, request), + netSelectorCacheCountMax: µb.cosmeticFilteringEngine.netSelectorCacheCountMax + }; } break; @@ -855,9 +851,10 @@ var getLists = function(callback) { autoUpdate: µb.userSettings.autoUpdate, available: null, cache: null, - cosmetic: µb.userSettings.parseAllABPHideFilters, + parseCosmeticFilters: µb.userSettings.parseAllABPHideFilters, cosmeticFilterCount: µb.cosmeticFilteringEngine.getFilterCount(), current: µb.remoteBlacklists, + ignoreGenericCosmeticFilters: µb.userSettings.ignoreGenericCosmeticFilters, manualUpdate: false, netFilterCount: µb.staticNetFilteringEngine.getFilterCount(), userFiltersPath: µb.userFiltersPath diff --git a/src/js/start.js b/src/js/start.js index b8f364532..b5550fb4e 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -1,7 +1,7 @@ /******************************************************************************* - uBlock - a browser extension to block requests. - Copyright (C) 2014-2015 Raymond Hill + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-2016 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 @@ -19,7 +19,9 @@ Home: https://github.com/gorhill/uBlock */ -/* global publicSuffixList, vAPI, µBlock */ +/* global publicSuffixList */ + +'use strict'; /******************************************************************************/ @@ -27,8 +29,6 @@ µBlock.restart = (function() { -'use strict'; - //quickProfiler.start('start.js'); /******************************************************************************/ @@ -178,6 +178,13 @@ var onUserSettingsReady = function(fetched) { µb.sessionURLFiltering.assign(µb.permanentURLFiltering); µb.hnSwitches.fromString(fetched.hostnameSwitchesString); + // https://github.com/gorhill/uBlock/issues/1892 + // For first installation on a battery-powered device, disable generic + // cosmetic filtering. + if ( µb.firstInstall && vAPI.battery ) { + userSettings.ignoreGenericCosmeticFilters = true; + } + // Remove obsolete setting delete userSettings.logRequests; vAPI.storage.remove('logRequests'); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index bb4b1dba9..bdca79f3a 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -2055,22 +2055,15 @@ FilterContainer.prototype.compileToAtomicFilter = function(filterClass, parsed, /******************************************************************************/ -FilterContainer.prototype.fromCompiledContent = function(text, lineBeg) { - var lineEnd; - var textEnd = text.length; +FilterContainer.prototype.fromCompiledContent = function(lineIter) { var line, fields, bucket, entry, factory, filter; - while ( lineBeg < textEnd ) { - if ( text.charCodeAt(lineBeg) !== 0x6E /* 'n' */ ) { - return lineBeg; + while ( lineIter.eot() === false ) { + if ( lineIter.text.charCodeAt(lineIter.offset) !== 0x6E /* 'n' */ ) { + return; } - lineEnd = text.indexOf('\n', lineBeg); - if ( lineEnd === -1 ) { - lineEnd = textEnd; - } - line = text.slice(lineBeg + 2, lineEnd); + line = lineIter.next().slice(2); fields = line.split('\v'); - lineBeg = lineEnd + 1; // Special cases: delegate to more specialized engines. // Redirect engine. @@ -2124,7 +2117,6 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg) { } bucket[fields[1]] = new FilterBucket(entry, filter); } - return textEnd; }; /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index 96b0e5d03..163409439 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -632,14 +632,14 @@ // applying 1st-party filters. µBlock.applyCompiledFilters = function(rawText, firstparty) { - var skipCosmetic = !firstparty && !this.userSettings.parseAllABPHideFilters; - var staticNetFilteringEngine = this.staticNetFilteringEngine; - var cosmeticFilteringEngine = this.cosmeticFilteringEngine; - var lineBeg = 0; - var rawEnd = rawText.length; - while ( lineBeg < rawEnd ) { - lineBeg = cosmeticFilteringEngine.fromCompiledContent(rawText, lineBeg, skipCosmetic); - lineBeg = staticNetFilteringEngine.fromCompiledContent(rawText, lineBeg); + var skipCosmetic = !firstparty && !this.userSettings.parseAllABPHideFilters, + skipGenericCosmetic = this.userSettings.ignoreGenericCosmeticFilters, + staticNetFilteringEngine = this.staticNetFilteringEngine, + cosmeticFilteringEngine = this.cosmeticFilteringEngine, + lineIter = new this.LineIterator(rawText); + while ( lineIter.eot() === false ) { + cosmeticFilteringEngine.fromCompiledContent(lineIter, skipGenericCosmetic, skipCosmetic); + staticNetFilteringEngine.fromCompiledContent(lineIter); } }; diff --git a/src/js/utils.js b/src/js/utils.js index 14fcb2893..de1f5380e 100644 --- a/src/js/utils.js +++ b/src/js/utils.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2015 Raymond Hill + Copyright (C) 2014-2016 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 @@ -123,3 +123,30 @@ // https://www.youtube.com/watch?v=DyvzfyqYm_s /******************************************************************************/ + +µBlock.LineIterator = function(text, offset) { + this.text = text; + this.offset = offset || 0; +}; + +µBlock.LineIterator.prototype.next = function() { + if ( this.offset >= this.text.length ) { + return undefined; + } + var lineEnd = this.text.indexOf('\n', this.offset); + if ( lineEnd === -1 ) { + lineEnd = this.text.indexOf('\r', this.offset); + if ( lineEnd === -1 ) { + lineEnd = this.text.length; + } + } + var line = this.text.slice(this.offset, lineEnd); + this.offset = lineEnd + 1; + return line; +}; + +µBlock.LineIterator.prototype.eot = function() { + return this.offset >= this.text.length; +}; + +/******************************************************************************/