diff --git a/assets/checksums.txt b/assets/checksums.txt index 59d64ceb0..e00dfe155 100644 --- a/assets/checksums.txt +++ b/assets/checksums.txt @@ -1,60 +1,60 @@ -e42e13e07117589d46671e876c787cc2 assets/ublock/filter-lists.json -7ef50e668ccfdd4de56af0003f575e93 assets/ublock/filters.txt -c9c5cc56bec563bc1885847f925b9be2 assets/ublock/mirror-candidates.txt ba85c7991d208fe26c8f74f57bb5cfe1 assets/ublock/privacy.txt -0d707b2cee4a38988662107971137d13 assets/thirdparties/abp.mozilla-hispano.org/nauscopio/filtros.txt -36a285b3ef9aadad4529d40153364013 assets/thirdparties/adblock-chinalist.googlecode.com/svn/trunk/adblock.txt -7c0d09fbccd87b5252d3bab3e822f2e4 assets/thirdparties/adblock-czechoslovaklist.googlecode.com/svn/filters.txt +bd73f55fa11330ec115e41d7194935f2 assets/ublock/filters.txt +c9c5cc56bec563bc1885847f925b9be2 assets/ublock/mirror-candidates.txt +e42e13e07117589d46671e876c787cc2 assets/ublock/filter-lists.json 132b3ecc9da8a68c3faf740c00af734b assets/thirdparties/adblock-plus-japanese-filter.googlecode.com/hg/abp_jp.txt +94c0a3eab74c42783855f07b22a429cf assets/thirdparties/home.fredfiber.no/langsholt/adblock.txt +1888a9521b2a138699ec01c654e35118 assets/thirdparties/www.zoso.ro/pages/rolist.txt 72373316d0e7ad22604d307c2d93e7cc assets/thirdparties/adblock.gardar.net/is.abp.txt -81fb33375814d8fb1653ba721ffd6a01 assets/thirdparties/adblock.schack.dk/block.txt +041f42feff1f0046a350506207d83a4c assets/thirdparties/raw.githubusercontent.com/k2jp/abp-japanese-filters/master/abp_jp.txt +eef36569f78b6a2401c1e228f7761bc7 assets/thirdparties/raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt +0b1e52e8e0bb5f0458e2d315f0fd96de assets/thirdparties/raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt +aaaefbe31ac8c7d2c32e684ffdca2bff assets/thirdparties/raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt +24231e63eeab50ad6116218aa1dc3b5b assets/thirdparties/raw.githubusercontent.com/wiltteri/wiltteri.txt/master/wiltteri.txt +5471b40cfefa128a133b993e5ef2f555 assets/thirdparties/raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt +068ceb3b1d8ce5ef155d6bdd30ec9d53 assets/thirdparties/raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt +cbde6559e6100aca9690fd01f2a93ad3 assets/thirdparties/raw.githubusercontent.com/adblockpolska/Adblock_PL_List/master/adblock_polska.txt d8c58f5bb8072907ae361bc14dbbd28f assets/thirdparties/cjxlist1.googlecode.com/svn/cjxlist.txt -b2823c07be0286782cc72c2355425a67 assets/thirdparties/dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt -8d87a4fd68102ba59da4989cfd65ed05 assets/thirdparties/easylist-downloads.adblockplus.org/advblock.txt -e0fcb343a05b399f0b0824f573c5015c assets/thirdparties/easylist-downloads.adblockplus.org/bitblock.txt -674e58227219e0e477dc5b6f664388ed assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt -f58ca4633056194a1350ebfa90a7f9d3 assets/thirdparties/easylist-downloads.adblockplus.org/easylist_noelemhide.txt +7720856eb1b1d15d0ab415b1f19d2345 assets/thirdparties/www.hufilter.hu/hufilter.txt +295bde39b70e1b3df7fc281d2d369ac7 assets/thirdparties/gnuzilla.gnu.org/filters/blacklist.txt +7755619a576f2479dcf26ef1c2753182 assets/thirdparties/spam404bl.com/spam404scamlist.txt 2ed9fcfc35ac135db1e2fda3cda8fcbc assets/thirdparties/easylist-downloads.adblockplus.org/easylistchina.txt -06ed68d31cf9460e58c0fa8ff8a61326 assets/thirdparties/easylist-downloads.adblockplus.org/easylistdutch.txt -4f2cb90acac064a6dec053a5c809c0a0 assets/thirdparties/easylist-downloads.adblockplus.org/easylistgermany.txt -3c4d23e31a12b822fd86975ab6f67e16 assets/thirdparties/easylist-downloads.adblockplus.org/easylistitaly.txt -3e07d4c7ee50251b950c392c8060b12d assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt 44497cd06bf45dc738b7716ca1fa66b6 assets/thirdparties/easylist-downloads.adblockplus.org/fanboy-annoyance.txt +e0fcb343a05b399f0b0824f573c5015c assets/thirdparties/easylist-downloads.adblockplus.org/bitblock.txt +06ed68d31cf9460e58c0fa8ff8a61326 assets/thirdparties/easylist-downloads.adblockplus.org/easylistdutch.txt +3e07d4c7ee50251b950c392c8060b12d assets/thirdparties/easylist-downloads.adblockplus.org/easyprivacy.txt +f58ca4633056194a1350ebfa90a7f9d3 assets/thirdparties/easylist-downloads.adblockplus.org/easylist_noelemhide.txt +8d87a4fd68102ba59da4989cfd65ed05 assets/thirdparties/easylist-downloads.adblockplus.org/advblock.txt def19717429aaeee2331cb797d5e240e assets/thirdparties/easylist-downloads.adblockplus.org/fanboy-social.txt d7a5ce9f655e43cd6026fa31356b8161 assets/thirdparties/easylist-downloads.adblockplus.org/liste_fr.txt -c95fd7804f199ddc7aa5cd56a61a32ec assets/thirdparties/gitorious.org/adblock-latvian/adblock-latvian/raw/master_lists/latvian-list.txt -295bde39b70e1b3df7fc281d2d369ac7 assets/thirdparties/gnuzilla.gnu.org/filters/blacklist.txt -94c0a3eab74c42783855f07b22a429cf assets/thirdparties/home.fredfiber.no/langsholt/adblock.txt -c0b6983add2393e18defcbda28fecec1 assets/thirdparties/hosts-file.net/ad-servers -d12fbce40c55bdea5d8c7ef6b0d0202d assets/thirdparties/indonesianadblockrules.googlecode.com/hg/subscriptions/abpindo.txt -cb26265deac870beef7133660bff9704 assets/thirdparties/liste-ar-adblock.googlecode.com/hg/Liste_AR.txt -dea183551bca4d87e6c5ecee3bbe0c82 assets/thirdparties/margevicius.lt/easylistlithuania.txt +3c4d23e31a12b822fd86975ab6f67e16 assets/thirdparties/easylist-downloads.adblockplus.org/easylistitaly.txt +4f2cb90acac064a6dec053a5c809c0a0 assets/thirdparties/easylist-downloads.adblockplus.org/easylistgermany.txt +674e58227219e0e477dc5b6f664388ed assets/thirdparties/easylist-downloads.adblockplus.org/easylist.txt +36a285b3ef9aadad4529d40153364013 assets/thirdparties/adblock-chinalist.googlecode.com/svn/trunk/adblock.txt +b2823c07be0286782cc72c2355425a67 assets/thirdparties/dl.dropboxusercontent.com/u/1289327/abpxfiles/filtri.txt adc945b82aec20f53ea31c36468b3759 assets/thirdparties/mirror1.malwaredomains.com/files/immortal_domains.txt -8732c6da425f67017af76a40f33ef0b4 assets/thirdparties/mirror1.malwaredomains.com/files/justdomains 7f0443f3dcc9abfd47cfbc95ce824ddb assets/thirdparties/mirror1.malwaredomains.com/files/README.md -5b8e13b618c68293430913029118781a assets/thirdparties/pgl.yoyo.org/as/README.md -b68842b8073833ff04414d8141986ac7 assets/thirdparties/pgl.yoyo.org/as/serverlist +8732c6da425f67017af76a40f33ef0b4 assets/thirdparties/mirror1.malwaredomains.com/files/justdomains e63f51b30304ddca0337c8eba3bff101 assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat -eef36569f78b6a2401c1e228f7761bc7 assets/thirdparties/raw.githubusercontent.com/AdBlockPlusIsrael/EasyListHebrew/master/EasyListHebrew.txt -cbde6559e6100aca9690fd01f2a93ad3 assets/thirdparties/raw.githubusercontent.com/adblockpolska/Adblock_PL_List/master/adblock_polska.txt -0b1e52e8e0bb5f0458e2d315f0fd96de assets/thirdparties/raw.githubusercontent.com/cjx82630/cjxlist/master/cjxlist.txt -041f42feff1f0046a350506207d83a4c assets/thirdparties/raw.githubusercontent.com/k2jp/abp-japanese-filters/master/abp_jp.txt -aaaefbe31ac8c7d2c32e684ffdca2bff assets/thirdparties/raw.githubusercontent.com/reek/anti-adblock-killer/master/anti-adblock-killer-filters.txt -068ceb3b1d8ce5ef155d6bdd30ec9d53 assets/thirdparties/raw.githubusercontent.com/szpeter80/hufilter/master/hufilter.txt -5471b40cfefa128a133b993e5ef2f555 assets/thirdparties/raw.githubusercontent.com/tomasko126/easylistczechandslovak/master/filters.txt -24231e63eeab50ad6116218aa1dc3b5b assets/thirdparties/raw.githubusercontent.com/wiltteri/wiltteri.txt/master/wiltteri.txt +0d707b2cee4a38988662107971137d13 assets/thirdparties/abp.mozilla-hispano.org/nauscopio/filtros.txt +dea183551bca4d87e6c5ecee3bbe0c82 assets/thirdparties/margevicius.lt/easylistlithuania.txt 5933a3c9059d10ef13544013e0d6e658 assets/thirdparties/someonewhocares.org/hosts/hosts -7755619a576f2479dcf26ef1c2753182 assets/thirdparties/spam404bl.com/spam404scamlist.txt -72ae2048c0201c940978d27cda361891 assets/thirdparties/stanev.org/abp/adblock_bg.txt d960765d2489db4f6038d3032cbd988c assets/thirdparties/winhelp2002.mvps.org/hosts.txt +81fb33375814d8fb1653ba721ffd6a01 assets/thirdparties/adblock.schack.dk/block.txt +7c0d09fbccd87b5252d3bab3e822f2e4 assets/thirdparties/adblock-czechoslovaklist.googlecode.com/svn/filters.txt +cb26265deac870beef7133660bff9704 assets/thirdparties/liste-ar-adblock.googlecode.com/hg/Liste_AR.txt +e68acbf09f22a8ac0650907fdd45a669 assets/thirdparties/www.fanboy.co.nz/fanboy-korean.txt 38517bc04606b74d18cd2b48cfb74c62 assets/thirdparties/www.fanboy.co.nz/enhancedstats.txt 2a9d105b544c47c55a3a22bb536db2bc assets/thirdparties/www.fanboy.co.nz/fanboy-antifacebook.txt -e68acbf09f22a8ac0650907fdd45a669 assets/thirdparties/www.fanboy.co.nz/fanboy-korean.txt -5de29853cdd54c919c283c9fdb0f3998 assets/thirdparties/www.fanboy.co.nz/fanboy-swedish.txt -dbdc8f71338ca741a6d1e23440960965 assets/thirdparties/www.fanboy.co.nz/fanboy-ultimate.txt faf479000090920441f1f01a664e0aca assets/thirdparties/www.fanboy.co.nz/fanboy-vietnam.txt -7720856eb1b1d15d0ab415b1f19d2345 assets/thirdparties/www.hufilter.hu/hufilter.txt -01ef1940551cff40fd1011f08f990d04 assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt +dbdc8f71338ca741a6d1e23440960965 assets/thirdparties/www.fanboy.co.nz/fanboy-ultimate.txt +5de29853cdd54c919c283c9fdb0f3998 assets/thirdparties/www.fanboy.co.nz/fanboy-swedish.txt 042419405031f0fcfac3735bf4f05e21 assets/thirdparties/www.malwaredomainlist.com/hostslist/README.md +01ef1940551cff40fd1011f08f990d04 assets/thirdparties/www.malwaredomainlist.com/hostslist/hosts.txt +c95fd7804f199ddc7aa5cd56a61a32ec assets/thirdparties/gitorious.org/adblock-latvian/adblock-latvian/raw/master_lists/latvian-list.txt +d12fbce40c55bdea5d8c7ef6b0d0202d assets/thirdparties/indonesianadblockrules.googlecode.com/hg/subscriptions/abpindo.txt +72ae2048c0201c940978d27cda361891 assets/thirdparties/stanev.org/abp/adblock_bg.txt +c0b6983add2393e18defcbda28fecec1 assets/thirdparties/hosts-file.net/ad-servers +b68842b8073833ff04414d8141986ac7 assets/thirdparties/pgl.yoyo.org/as/serverlist +5b8e13b618c68293430913029118781a assets/thirdparties/pgl.yoyo.org/as/README.md 342eccafdb7e6ef7b9aacd33d1442ed1 assets/thirdparties/www.void.gr/kargig/void-gr-filters.txt -1888a9521b2a138699ec01c654e35118 assets/thirdparties/www.zoso.ro/pages/rolist.txt diff --git a/assets/ublock/filters.txt b/assets/ublock/filters.txt index 2086721ce..22dd8c6ef 100644 --- a/assets/ublock/filters.txt +++ b/assets/ublock/filters.txt @@ -313,3 +313,7 @@ deviantart.com##.dp-ad-chrome.dp-ad-visible # To deal with https://github.com/chrisaljoudi/uBlock/issues/1013 ||mac-system-alert.com^ + +# Video playback broken on lastminutegoals.org +# To counter `intergi.com` in Peter Lowe's +@@||cdn.phoenix.intergi.com^$domain=lastminutegoals.org diff --git a/src/background.html b/src/background.html index 22de5f3f0..41f12374d 100644 --- a/src/background.html +++ b/src/background.html @@ -22,6 +22,7 @@ + diff --git a/src/css/devtool-log.css b/src/css/devtool-log.css index 4f1f28f1b..5ddf9c9d8 100644 --- a/src/css/devtool-log.css +++ b/src/css/devtool-log.css @@ -68,18 +68,32 @@ input:focus { direction: ltr; width: 100%; } +#content table tr.docBoundary { + background-color: #666; + color: white; + text-align: center; + } +#content table tr.docBoundary > td:first-child { + padding: 1em 0; + white-space: normal; + word-break: break-all; + word-wrap: break-word; + } #content table tr.blocked { - background-color: rgba(192, 0, 0, 0.1) + background-color: rgba(192, 0, 0, 0.1); } #content table tr.allowed { - background-color: rgba(0, 160, 0, 0.1) + background-color: rgba(0, 160, 0, 0.1); } #content table tr.allowed.mirrored { - background-color: rgba(255, 255, 0, 0.3) + background-color: rgba(255, 255, 0, 0.3); } #content table tr.maindoc { background-color: #eee; } +#content table tr.cosmetic { + background-color: rgba(255, 255, 0, 0.1); + } body:not(.filterOff) #content table tr.hidden { display: none; } diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index e5df12d4d..db1af9cb8 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -55,6 +55,7 @@ if ( vAPI.contentscriptEndInjected ) { return; } vAPI.contentscriptEndInjected = true; +vAPI.styles = vAPI.styles || []; /******************************************************************************/ /******************************************************************************/ @@ -187,7 +188,7 @@ var uBlockCollapser = (function() { } if ( selectors.length !== 0 ) { messager.send({ - what: 'injectedSelectors', + what: 'cosmeticFiltersInjected', type: 'net', hostname: window.location.hostname, selectors: selectors @@ -385,7 +386,7 @@ var uBlockCollapser = (function() { processHighHighGenericsAsync(); } } - if ( hideSelectors.length ) { + if ( hideSelectors.length !== 0 ) { addStyleTag(hideSelectors); } contextNodes.length = 0; @@ -400,7 +401,7 @@ var uBlockCollapser = (function() { // - Injecting a style tag var addStyleTag = function(selectors) { - var selectorStr = selectors.toString(); + var selectorStr = selectors.join(',\n'); hideElements(selectorStr); var style = document.createElement('style'); // The linefeed before the style block is very important: do no remove! @@ -408,9 +409,10 @@ var uBlockCollapser = (function() { var parent = document.body || document.documentElement; if ( parent ) { parent.appendChild(style); + vAPI.styles.push(style); } messager.send({ - what: 'injectedSelectors', + what: 'cosmeticFiltersInjected', type: 'cosmetic', hostname: window.location.hostname, selectors: selectors @@ -701,6 +703,7 @@ var uBlockCollapser = (function() { idsFromNodeList(selectNodes('[id]')); classesFromNodeList(selectNodes('[class]')); retrieveGenericSelectors(); + messager.send({ what: 'cosmeticFiltersActivated' }); } }; diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index ad6161d79..9e5210dfb 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -53,6 +53,7 @@ if ( vAPI.contentscriptStartInjected ) { return; } vAPI.contentscriptStartInjected = true; +vAPI.styles = vAPI.styles || []; /******************************************************************************/ @@ -99,6 +100,7 @@ var cosmeticFilters = function(details) { var parent = document.head || document.documentElement; if ( parent ) { parent.appendChild(style); + vAPI.styles.push(style); } } vAPI.donthideCosmeticFilters = donthideCosmeticFilters; @@ -121,6 +123,8 @@ var netFilters = function(details) { }; var filteringHandler = function(details) { + var styleTagCount = vAPI.styles.length; + vAPI.skipCosmeticFiltering = !details || details.skipCosmeticFiltering; if ( details ) { if ( details.cosmeticHide.length !== 0 || details.cosmeticDonthide.length !== 0 ) { @@ -129,8 +133,12 @@ var filteringHandler = function(details) { if ( details.netHide.length !== 0 ) { netFilters(details); } - // The port will never be used again at this point, disconnecting allows - // the browser to flush this script from memory. + } + + // This is just to inform the background process that cosmetic filters were + // actually injected. + if ( vAPI.styles.length !== styleTagCount ) { + localMessager.send({ what: 'cosmeticFiltersActivated' }); } // https://github.com/chrisaljoudi/uBlock/issues/587 @@ -139,7 +147,8 @@ var filteringHandler = function(details) { // cleaned right after browser launch. vAPI.contentscriptStartInjected = details && details.ready; - // Cleanup before leaving + // The port will never be used again at this point, disconnecting allows + // the browser to flush this script from memory. localMessager.close(); }; diff --git a/src/js/cosmetic-logger.js b/src/js/cosmetic-logger.js new file mode 100644 index 000000000..d038d1af6 --- /dev/null +++ b/src/js/cosmetic-logger.js @@ -0,0 +1,96 @@ +/******************************************************************************* + + uBlock - a browser extension to block requests. + Copyright (C) 2015 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* global vAPI, HTMLDocument */ + +/******************************************************************************/ + +(function() { + +'use strict'; + +/******************************************************************************/ + +// https://github.com/gorhill/uBlock/issues/464 +if ( document instanceof HTMLDocument === false ) { + //console.debug('cosmetic-logger.js > not a HTLMDocument'); + return; +} + +// This can happen +if ( !vAPI ) { + //console.debug('cosmetic-logger.js > vAPI not found'); + return; +} + +/******************************************************************************/ + +var loggedSelectors = vAPI.loggedSelectors || {}; + +var injectedSelectors = []; +var reProperties = /\s*\{[^}]+\}\s*/; +var i; +var styles = vAPI.styles || []; + +i = styles.length; +while ( i-- ) { + injectedSelectors = injectedSelectors.concat(styles[i].textContent.replace(reProperties, '').split(/\s*,\n\s*/)); +} + +if ( injectedSelectors.length === 0 ) { + return; +} + +var matchedSelectors = []; +var selector; + +i = injectedSelectors.length; +while ( i-- ) { + selector = injectedSelectors[i]; + if ( loggedSelectors.hasOwnProperty(selector) ) { + continue; + } + if ( document.querySelector(selector) === null ) { + continue; + } + loggedSelectors[selector] = true; + matchedSelectors.push(selector); +} + +vAPI.loggedSelectors = loggedSelectors; + +/******************************************************************************/ + +var localMessager = vAPI.messaging.channel('cosmetic-*.js'); + +localMessager.send({ + what: 'logCosmeticFilteringData', + pageURL: window.location.href, + matchedSelectors: matchedSelectors +}, function() { + localMessager.close(); +}); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/devtool-log.js b/src/js/devtool-log.js index 2720e1bd0..7eb48781c 100644 --- a/src/js/devtool-log.js +++ b/src/js/devtool-log.js @@ -37,7 +37,8 @@ var inspectedTabId = ''; var doc = document; var body = doc.body; var tbody = doc.querySelector('#content tbody'); -var rowJunkyard = []; +var row1Junkyard = []; +var row4Junkyard = []; var reFilter = null; var filterTargetTestResult = true; var maxEntries = 0; @@ -106,7 +107,7 @@ var renderURL = function(url, filter) { /******************************************************************************/ var createRow = function() { - var tr = rowJunkyard.pop(); + var tr = row4Junkyard.pop(); if ( tr ) { tr.className = ''; return tr; @@ -121,8 +122,35 @@ var createRow = function() { /******************************************************************************/ +var createGap = function(url) { + var tr = row1Junkyard.pop(); + if ( !tr ) { + tr = doc.createElement('tr'); + tr.classList.add('docBoundary'); + tr.appendChild(doc.createElement('td')); + tr.cells[0].setAttribute('colspan', '4'); + } + tr.cells[0].textContent = url; + tbody.insertBefore(tr, tbody.firstChild); +}; + +/******************************************************************************/ + var renderLogEntry = function(entry) { var tr = createRow(); + + // If the request is that of a root frame, insert a gap in the table + // in order to visually separate entries for different documents. + if ( entry.type === 'main_frame' ) { + createGap(entry.url); + tr.classList.add('maindoc'); + } + + // Cosmetic filter? + if ( entry.result.charAt(0) === 'c' ) { + tr.classList.add('cosmetic'); + } + if ( entry.result.charAt(1) === 'b' ) { tr.classList.add('blocked'); tr.cells[0].textContent = ' -\u00A0'; @@ -135,9 +163,11 @@ var renderLogEntry = function(entry) { } else { tr.cells[0].textContent = ''; } + if ( entry.type === 'main_frame' ) { tr.classList.add('maindoc'); } + var filterText = entry.result.slice(3); if ( entry.result.lastIndexOf('sa', 0) === 0 ) { filterText = '@@' + filterText; @@ -198,8 +228,17 @@ var truncateLog = function(size) { size = 25000; } size = Math.min(size, 25000); + var tr; while ( tbody.childElementCount > size ) { - rowJunkyard.push(tbody.removeChild(tbody.lastElementChild)); + tr = tbody.lastElementChild; + // https://github.com/gorhill/uBlock/issues/123 + // Triage according to row type. + if ( tr.cells.length === 1 ) { + row1Junkyard.push(tr); + } else { + row4Junkyard.push(tr); + } + tbody.removeChild(tr); } }; @@ -225,8 +264,17 @@ var readLogBuffer = function() { /******************************************************************************/ var clearBuffer = function() { + var tr; while ( tbody.firstChild !== null ) { - rowJunkyard.push(tbody.removeChild(tbody.firstChild)); + tr = tbody.lastElementChild; + // https://github.com/gorhill/uBlock/issues/123 + // Triage according to row type. + if ( tr.cells.length === 1 ) { + row1Junkyard.push(tr); + } else { + row4Junkyard.push(tr); + } + tbody.removeChild(tr); } }; diff --git a/src/js/logger.js b/src/js/logger.js new file mode 100644 index 000000000..dc9e3a3c8 --- /dev/null +++ b/src/js/logger.js @@ -0,0 +1,214 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* global µBlock */ + +/******************************************************************************/ +/******************************************************************************/ + +µBlock.logger = (function() { + +'use strict'; + +/******************************************************************************/ +/******************************************************************************/ + +var LogEntry = function(details, result) { + this.init(details, result); +}; + +/******************************************************************************/ + +var logEntryFactory = function(details, result) { + var entry = logEntryJunkyard.pop(); + if ( entry ) { + return entry.init(details, result); + } + return new LogEntry(details, result); +}; + +var logEntryJunkyard = []; +var logEntryJunkyardMax = 100; + +/******************************************************************************/ + +LogEntry.prototype.init = function(details, result) { + this.tstamp = Date.now(); + this.url = details.requestURL; + this.hostname = details.requestHostname; + this.type = details.requestType; + this.result = result; + return this; +}; + +/******************************************************************************/ + +LogEntry.prototype.dispose = function() { + this.url = this.hostname = this.type = this.result = ''; + if ( logEntryJunkyard.length < logEntryJunkyardMax ) { + logEntryJunkyard.push(this); + } +}; + +/******************************************************************************/ +/******************************************************************************/ + +var LogBuffer = function() { + this.lastReadTime = 0; + this.size = 50; + this.buffer = new Array(this.size); + this.readPtr = 0; + this.writePtr = 0; +}; + +/******************************************************************************/ + +LogBuffer.prototype.dispose = function() { + var entry; + var i = this.buffer.length; + while ( i-- ) { + entry = this.buffer[i]; + if ( entry instanceof LogEntry ) { + entry.dispose(); + } + } + this.buffer = null; + return null; +}; + +/******************************************************************************/ + +LogBuffer.prototype.writeOne = function(details, result) { + // Reusing log entry = less memory churning + var entry = this.buffer[this.writePtr]; + if ( entry instanceof LogEntry === false ) { + this.buffer[this.writePtr] = logEntryFactory(details, result); + } else { + entry.init(details, result); + } + this.writePtr += 1; + if ( this.writePtr === this.size ) { + this.writePtr = 0; + } + // Grow the buffer between 1.5x-2x the current size + if ( this.writePtr === this.readPtr ) { + var toMove = this.buffer.slice(0, this.writePtr); + var minSize = Math.ceil(this.size * 1.5); + this.size += toMove.length; + if ( this.size < minSize ) { + this.buffer = this.buffer.concat(toMove, new Array(minSize - this.size)); + this.writePtr = this.size; + } else { + this.buffer = this.buffer.concat(toMove); + this.writePtr = 0; + } + this.size = this.buffer.length; + } +}; + +/******************************************************************************/ + +LogBuffer.prototype.readAll = function() { + var out; + if ( this.readPtr < this.writePtr ) { + out = this.buffer.slice(this.readPtr, this.writePtr); + } else if ( this.writePtr < this.readPtr ) { + out = this.buffer.slice(this.readPtr).concat(this.buffer.slice(0, this.writePtr)); + } else { + out = []; + } + this.readPtr = this.writePtr; + this.lastReadTime = Date.now(); + return out; +}; + +/******************************************************************************/ +/******************************************************************************/ + +// Tab id to log buffer instances +var logBuffers = {}; + +// After 30 seconds without being read, a buffer will be considered unused, and +// thus removed from memory. +var logBufferObsoleteAfter = 30 * 1000; + +/******************************************************************************/ + +var writeOne = function(tabId, details, result) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + return; + } + var logBuffer = logBuffers[tabId]; + logBuffer.writeOne(details, result); +}; + +/******************************************************************************/ + +var readAll = function(tabId) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + logBuffers[tabId] = new LogBuffer(); + } + return logBuffers[tabId].readAll(); +}; + +/******************************************************************************/ + +var isObserved = function(tabId) { + return logBuffers.hasOwnProperty(tabId); +}; + +/******************************************************************************/ + +var loggerJanitor = function() { + var logBuffer; + var obsolete = Date.now() - logBufferObsoleteAfter; + for ( var tabId in logBuffers ) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + continue; + } + logBuffer = logBuffers[tabId]; + if ( logBuffer.lastReadTime < obsolete ) { + logBuffer.dispose(); + delete logBuffers[tabId]; + } + } + setTimeout(loggerJanitor, loggerJanitorPeriod); +}; + +// The janitor will look for stale log buffer every 2 minutes. +var loggerJanitorPeriod = 2 * 60 * 1000; + +setTimeout(loggerJanitor, loggerJanitorPeriod); + +/******************************************************************************/ + +return { + writeOne: writeOne, + readAll: readAll, + isObserved: isObserved +}; + +/******************************************************************************/ +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 595e8e5b2..492aedf69 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -50,6 +50,8 @@ var onMessage = function(request, sender, callback) { break; } + var tabId = sender && sender.tab ? sender.tab.id : 0; + // Sync var response; @@ -59,6 +61,17 @@ var onMessage = function(request, sender, callback) { µb.contextMenuClientY = request.clientY; break; + case 'cosmeticFiltersInjected': + // Is this a request to cache selectors? + µb.cosmeticFilteringEngine.addToSelectorCache(request); + /* falls through */ + case 'cosmeticFiltersActivated': + // Net-based cosmetic filters are of no interest for logging purpose. + if ( µb.logger.isObserved(tabId) && request.type !== 'net' ) { + µb.logCosmeticFilters(tabId); + } + break; + case 'forceUpdateAssets': µb.assetUpdater.force(); break; @@ -463,9 +476,9 @@ var filterRequests = function(pageStore, details) { /******************************************************************************/ -var onMessage = function(details, sender, callback) { +var onMessage = function(request, sender, callback) { // Async - switch ( details.what ) { + switch ( request.what ) { default: break; } @@ -478,21 +491,17 @@ var onMessage = function(details, sender, callback) { pageStore = µb.pageStoreFromTabId(sender.tab.id); } - switch ( details.what ) { + switch ( request.what ) { case 'retrieveGenericCosmeticSelectors': response = { shutdown: !pageStore || !pageStore.getNetFilteringSwitch(), result: null }; if ( !response.shutdown && pageStore.getGenericCosmeticFilteringSwitch() ) { - response.result = µb.cosmeticFilteringEngine.retrieveGenericSelectors(details); + response.result = µb.cosmeticFilteringEngine.retrieveGenericSelectors(request); } break; - case 'injectedSelectors': - µb.cosmeticFilteringEngine.addToSelectorCache(details); - break; - // Evaluate many requests case 'filterRequests': response = { @@ -500,7 +509,7 @@ var onMessage = function(details, sender, callback) { result: null }; if(!response.shutdown) { - response.result = filterRequests(pageStore, details); + response.result = filterRequests(pageStore, request); } break; @@ -520,6 +529,73 @@ vAPI.messaging.listen('contentscript-end.js', onMessage); /******************************************************************************/ /******************************************************************************/ +// cosmetic-*.js + +(function() { + +'use strict'; + +/******************************************************************************/ + +var µb = µBlock; + +/******************************************************************************/ + +var logCosmeticFilters = function(tabId, details) { + if ( µb.logger.isObserved(tabId) === false ) { + return; + } + + var context = { + requestURL: details.pageURL, + requestHostname: µb.URI.hostnameFromURI(details.pageURL), + requestType: 'dom' + }; + + var selectors = details.matchedSelectors; + + selectors.sort(); + + for ( var i = 0; i < selectors.length; i++ ) { + µb.logger.writeOne(tabId, context, 'cb:##' + selectors[i]); + } +}; + +/******************************************************************************/ + +var onMessage = function(request, sender, callback) { + // Async + switch ( request.what ) { + default: + break; + } + + // Sync + var response; + + var tabId = sender && sender.tab ? sender.tab.id : 0; + + switch ( request.what ) { + case 'logCosmeticFilteringData': + logCosmeticFilters(tabId, request); + break; + + default: + return vAPI.messaging.UNHANDLED; + } + + callback(response); +}; + +vAPI.messaging.listen('cosmetic-*.js', onMessage); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ +/******************************************************************************/ + // element-picker.js (function() { @@ -1134,10 +1210,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'readLogBuffer': - var pageStore = µb.pageStoreFromTabId(request.tabId); - if ( pageStore ) { - response = pageStore.logBuffer.readAll(); - } + response = µb.logger.readAll(request.tabId); break; default: diff --git a/src/js/pagestore.js b/src/js/pagestore.js index f0793c4c8..657e76115 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -45,178 +45,6 @@ var µb = µBlock; /******************************************************************************/ /******************************************************************************/ -var LogEntry = function(details, result) { - this.init(details, result); -}; - -/******************************************************************************/ - -var logEntryFactory = function(details, result) { - var entry = logEntryJunkyard.pop(); - if ( entry ) { - return entry.init(details, result); - } - return new LogEntry(details, result); -}; - -var logEntryJunkyard = []; -var logEntryJunkyardMax = 100; - -/******************************************************************************/ - -LogEntry.prototype.init = function(details, result) { - this.tstamp = Date.now(); - this.url = details.requestURL; - this.hostname = details.requestHostname; - this.type = details.requestType; - this.result = result; - return this; -}; - -/******************************************************************************/ - -LogEntry.prototype.dispose = function() { - this.url = this.hostname = this.type = this.result = ''; - if ( logEntryJunkyard.length < logEntryJunkyardMax ) { - logEntryJunkyard.push(this); - } -}; - -/******************************************************************************/ - -var LogBuffer = function() { - this.lastReadTime = 0; - this.size = 50; - this.buffer = null; - this.readPtr = 0; - this.writePtr = 0; -}; - -/******************************************************************************/ - -var logBufferFactory = function() { - return new LogBuffer(); -}; - -var liveLogBuffers = []; - -/******************************************************************************/ - -LogBuffer.prototype.dispose = function() { - if ( this.buffer === null ) { - return null; - } - var entry; - var i = this.buffer.length; - while ( i-- ) { - entry = this.buffer[i]; - if ( entry instanceof LogEntry ) { - entry.dispose(); - } - } - this.buffer = null; - return null; -}; - -/******************************************************************************/ - -LogBuffer.prototype.start = function() { - if ( this.buffer === null ) { - this.buffer = new Array(this.size); - this.readPtr = 0; - this.writePtr = 0; - liveLogBuffers.push(this); - } -}; - -/******************************************************************************/ - -LogBuffer.prototype.stop = function() { - this.dispose(); - this.buffer = null; - // The janitor will remove us from the live pool eventually. -}; - -/******************************************************************************/ - -LogBuffer.prototype.writeOne = function(details, result) { - if ( this.buffer === null ) { - return; - } - // Reusing log entry = less memory churning - var entry = this.buffer[this.writePtr]; - if ( entry instanceof LogEntry === false ) { - this.buffer[this.writePtr] = logEntryFactory(details, result); - } else { - entry.init(details, result); - } - this.writePtr += 1; - if ( this.writePtr === this.size ) { - this.writePtr = 0; - } - // Grow the buffer between 1.5x-2x the current size - if ( this.writePtr === this.readPtr ) { - var toMove = this.buffer.slice(0, this.writePtr); - var minSize = Math.ceil(this.size * 1.5); - this.size += toMove.length; - if ( this.size < minSize ) { - this.buffer = this.buffer.concat(toMove, new Array(minSize - this.size)); - this.writePtr = this.size; - } else { - this.buffer = this.buffer.concat(toMove); - this.writePtr = 0; - } - this.size = this.buffer.length; - } -}; - -/******************************************************************************/ - -LogBuffer.prototype.readAll = function() { - var out; - if ( this.buffer === null ) { - this.start(); - out = []; - } else if ( this.readPtr < this.writePtr ) { - out = this.buffer.slice(this.readPtr, this.writePtr); - } else if ( this.writePtr < this.readPtr ) { - out = this.buffer.slice(this.readPtr).concat(this.buffer.slice(0, this.writePtr)); - } else { - out = []; - } - this.readPtr = this.writePtr; - this.lastReadTime = Date.now(); - return out; -}; - -/******************************************************************************/ - -var logBufferJanitor = function() { - var logBuffer; - var obsolete = Date.now() - logBufferObsoleteAfter; - var i = liveLogBuffers.length; - while ( i-- ) { - logBuffer = liveLogBuffers[i]; - if ( logBuffer.lastReadTime < obsolete ) { - logBuffer.stop(); - liveLogBuffers.splice(i, 1); - } - } - setTimeout(logBufferJanitor, logBufferJanitorPeriod); -}; - -// The janitor will look for stale log buffer every 2 minutes. -var logBufferJanitorPeriod = 2 * 60 * 1000; - -// After 30 seconds without being read, a buffer will be considered unused, and -// thus removed from memory. -var logBufferObsoleteAfter = 30 * 1000; - -setTimeout(logBufferJanitor, logBufferJanitorPeriod); - -/******************************************************************************/ -/******************************************************************************/ - // To mitigate memory churning var netFilteringResultCacheEntryJunkyard = []; var netFilteringResultCacheEntryJunkyardMax = 200; @@ -483,12 +311,6 @@ PageStore.prototype.init = function(tabId) { .matchStringExactType(context, tabContext.normalURL, 'cosmetic-filtering') .charAt(1) === 'b'; - // Preserve old buffer if there is one already, it may be in use, and - // overwritting it would required another read to restart it. - if ( this.logBuffer instanceof LogBuffer === false ) { - this.logBuffer = logBufferFactory(); - } - return this; }; @@ -538,7 +360,6 @@ PageStore.prototype.dispose = function() { this.hostnameToCountMap = null; this.disposeFrameStores(); this.netFilteringCache = this.netFilteringCache.dispose(); - this.logBuffer = this.logBuffer.dispose(); if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { pageStoreJunkyard.push(this); } @@ -703,7 +524,6 @@ PageStore.prototype.filterRequest = function(context) { return result; }; -// Cache only what is worth it if logging is disabled // http://jsperf.com/string-indexof-vs-object var collapsibleRequestTypes = 'image sub_frame object'; @@ -766,7 +586,6 @@ PageStore.prototype.logRequest = function(context, result) { µb.localSettings.blockedRequestCount++; } µb.localSettingsModifyTime = now; - this.logBuffer.writeOne(context, result); }; /******************************************************************************/ diff --git a/src/js/storage.js b/src/js/storage.js index 618761170..71e0737f6 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -159,7 +159,7 @@ var µb = this; - var onCompiledListLoaded = function() { + var onSaved = function() { var compiledFilters = µb.compileFilters(filters); var snfe = µb.staticNetFilteringEngine; var cfe = µb.cosmeticFilteringEngine; @@ -176,13 +176,6 @@ µb.cosmeticFilteringEngine.freeze(); }; - var onSaved = function(details) { - if ( details.error ) { - return; - } - µb.getCompiledFilterList(µb.userFiltersPath, onCompiledListLoaded); - }; - var onLoaded = function(details) { if ( details.error ) { return; diff --git a/src/js/tab.js b/src/js/tab.js index 1dda0a6c5..c0a5a3a3d 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -470,6 +470,7 @@ vAPI.tabs.onPopup = function(details) { if ( pageStore ) { pageStore.logRequest(context, result); } + µb.logger.writeOne(details.openerTabId, context, result); // Not blocked if ( µb.isAllowResult(result) ) { diff --git a/src/js/traffic.js b/src/js/traffic.js index 41db50732..763a44542 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -92,6 +92,7 @@ var onBeforeRequest = function(details) { // Possible outcomes: blocked, allowed-passthru, allowed-mirror pageStore.logRequest(requestContext, result); + µb.logger.writeOne(tabId, requestContext, result); // Not blocked if ( µb.isAllowResult(result) ) { @@ -152,7 +153,7 @@ var onBeforeRootFrameRequest = function(details) { if ( pageStore ) { pageStore.logRequest(context, result); } - return; + µb.logger.writeOne(tabId, context, result); }; /******************************************************************************/ @@ -225,6 +226,7 @@ var onBeforeBehindTheSceneRequest = function(details) { } pageStore.logRequest(context, result); + µb.logger.writeOne(vAPI.noTabId, context, result); // Not blocked if ( µb.isAllowResult(result) ) { @@ -272,6 +274,7 @@ var onHeadersReceived = function(details) { var result = pageStore.filterRequestNoCache(context); pageStore.logRequest(context, result); + µb.logger.writeOne(tabId, context, result); // Don't block if ( µb.isAllowResult(result) ) { @@ -318,6 +321,7 @@ var onRootFrameHeadersReceived = function(details) { var result = pageStore.filterRequest(context); pageStore.logRequest(context, result); + µb.logger.writeOne(tabId, context, result); // Don't block if ( µb.isAllowResult(result) ) { diff --git a/src/js/ublock.js b/src/js/ublock.js index 0fd81e495..b713df96d 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -318,4 +318,27 @@ var matchWhitelistDirective = function(url, hostname, directive) { /******************************************************************************/ +µBlock.logCosmeticFilters = (function() { + var tabIdToTimerMap = {}; + + var injectNow = function(tabId) { + delete tabIdToTimerMap[tabId]; + vAPI.tabs.injectScript(tabId, { file: 'js/cosmetic-logger.js' }); + }; + + var injectAsync = function(tabId) { + if ( tabIdToTimerMap.hasOwnProperty(tabId) ) { + return; + } + tabIdToTimerMap[tabId] = setTimeout( + injectNow.bind(null, tabId), + 100 + ); + }; + + return injectAsync; +})(); + +/******************************************************************************/ + })();