diff options
author | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-10-17 18:10:23 +0200 |
---|---|---|
committer | Wolfgang Wiedmeyer <wolfgit@wiedmeyer.de> | 2015-10-17 18:10:23 +0200 |
commit | ea0c3f8ecde7d6e418ddfc8c6f58cb2bbf1e84c7 (patch) | |
tree | f5f542e168df6b8da261ee0d95476a254fff9139 /src/js | |
parent | 2a260e13033aa80f474f3c7ca2f7f4ca4aec2f54 (diff) | |
parent | 230639d959468fc67c7ca5cf0249009eee0853b8 (diff) | |
download | uBlock-ea0c3f8ecde7d6e418ddfc8c6f58cb2bbf1e84c7.zip uBlock-ea0c3f8ecde7d6e418ddfc8c6f58cb2bbf1e84c7.tar.gz uBlock-ea0c3f8ecde7d6e418ddfc8c6f58cb2bbf1e84c7.tar.bz2 |
Merge branch 'upstream'
Diffstat (limited to 'src/js')
-rw-r--r-- | src/js/3p-filters.js | 14 | ||||
-rw-r--r-- | src/js/assets.js | 80 | ||||
-rw-r--r-- | src/js/background.js | 2 | ||||
-rw-r--r-- | src/js/contentscript-end.js | 12 | ||||
-rw-r--r-- | src/js/contentscript-start.js | 4 | ||||
-rw-r--r-- | src/js/cosmetic-filtering.js | 120 | ||||
-rw-r--r-- | src/js/document-blocked.js | 11 | ||||
-rw-r--r-- | src/js/messaging.js | 2 | ||||
-rw-r--r-- | src/js/reverselookup-worker.js | 32 | ||||
-rw-r--r-- | src/js/rpcreceiver.js | 53 | ||||
-rw-r--r-- | src/js/static-net-filtering.js | 5 |
11 files changed, 286 insertions, 49 deletions
diff --git a/src/js/3p-filters.js b/src/js/3p-filters.js index a3e81b8..b5fca1b 100644 --- a/src/js/3p-filters.js +++ b/src/js/3p-filters.js @@ -371,8 +371,22 @@ var onPurgeClicked = function() { if ( !href ) { return; } + messager.send({ what: 'purgeCache', path: href }); button.remove(); + + // If the cached version is purged, the installed version must be assumed + // to be obsolete. + var entry = listDetails.current && listDetails.current[href]; + if ( entry && entry.off !== true ) { + if ( typeof entry.homeURL !== 'string' || entry.homeURL === '' ) { + li.descendants('span.status.new').css('display', ''); + } else { + li.descendants('span.status.obsolete').css('display', ''); + } + needUpdate = true; + } + if ( li.descendants('input').first().prop('checked') ) { cacheWasPurged = true; renderWidgets(); diff --git a/src/js/assets.js b/src/js/assets.js index 8549cd8..08616c5 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -61,6 +61,7 @@ var lastRepoMetaTimestamp = 0; var lastRepoMetaIsRemote = false; var refreshRepoMetaPeriod = 5 * oneHour; var errorCantConnectTo = vAPI.i18n('errorCantConnectTo'); +var xhrTimeout = vAPI.localStorage.getItem('xhrTimeout') || 30000; var exports = { autoUpdate: true, @@ -275,8 +276,14 @@ var cachedAssetsManager = (function() { exports.remove(/./); }; + exports.exists = function(path) { + return entries !== null && entries.hasOwnProperty(path); + }; + exports.onRemovedListener = null; + getEntries(function(){}); + return exports; })(); @@ -285,6 +292,10 @@ var cachedAssetsManager = (function() { var getTextFileFromURL = function(url, onLoad, onError) { // console.log('µBlock.assets/getTextFileFromURL("%s"):', url); + if ( typeof onError !== 'function' ) { + onError = onLoad; + } + // https://github.com/gorhill/uMatrix/issues/15 var onResponseReceived = function() { this.onload = this.onerror = this.ontimeout = null; @@ -318,7 +329,7 @@ var getTextFileFromURL = function(url, onLoad, onError) { var xhr = new XMLHttpRequest(); try { xhr.open('get', url, true); - xhr.timeout = 30000; + xhr.timeout = xhrTimeout; xhr.onload = onResponseReceived; xhr.onerror = onErrorReceived; xhr.ontimeout = onErrorReceived; @@ -376,11 +387,16 @@ var getRepoMetadata = function(callback) { lastRepoMetaTimestamp = Date.now(); lastRepoMetaIsRemote = exports.remoteFetchBarrier === 0; + var defaultChecksums; var localChecksums; var repoChecksums; var checksumsReceived = function() { - if ( localChecksums === undefined || repoChecksums === undefined ) { + if ( + defaultChecksums === undefined || + localChecksums === undefined || + repoChecksums === undefined + ) { return; } // Remove from cache assets which no longer exist in the repo @@ -392,6 +408,17 @@ var getRepoMetadata = function(callback) { continue; } entry = entries[path]; + // https://github.com/gorhill/uBlock/issues/760 + // If the resource does not have a cached instance, we must reset + // the checksum to its value at install time. + if ( + stringIsNotEmpty(defaultChecksums[path]) && + entry.localChecksum !== defaultChecksums[path] && + cachedAssetsManager.exists(path) === false + ) { + entry.localChecksum = defaultChecksums[path]; + checksumsChanged = true; + } // If repo checksums could not be fetched, assume no change. // https://github.com/gorhill/uBlock/issues/602 // Added: if repo checksum is that of the empty string, @@ -450,41 +477,64 @@ var getRepoMetadata = function(callback) { return out.join('\n'); }; - var parseChecksums = function(text, which) { - var entries = repoMetadata.entries; + var parseChecksums = function(text, eachFn) { var lines = text.split(/\n+/); var i = lines.length; - var fields, assetPath; + var fields; while ( i-- ) { fields = lines[i].trim().split(/\s+/); if ( fields.length !== 2 ) { continue; } - assetPath = fields[1]; - if ( entries[assetPath] === undefined ) { - entries[assetPath] = new AssetEntry(); - } - entries[assetPath][which + 'Checksum'] = fields[0]; + eachFn(fields[1], fields[0]); } }; var onLocalChecksumsLoaded = function(details) { + var entries = repoMetadata.entries; + var processChecksum = function(path, checksum) { + if ( entries.hasOwnProperty(path) === false ) { + entries[path] = new AssetEntry(); + } + entries[path].localChecksum = checksum; + }; if ( (localChecksums = validateChecksums(details)) ) { - parseChecksums(localChecksums, 'local'); + parseChecksums(localChecksums, processChecksum); } checksumsReceived(); }; var onRepoChecksumsLoaded = function(details) { + var entries = repoMetadata.entries; + var processChecksum = function(path, checksum) { + if ( entries.hasOwnProperty(path) === false ) { + entries[path] = new AssetEntry(); + } + entries[path].repoChecksum = checksum; + }; if ( (repoChecksums = validateChecksums(details)) ) { - parseChecksums(repoChecksums, 'repo'); + parseChecksums(repoChecksums, processChecksum); } checksumsReceived(); }; + // https://github.com/gorhill/uBlock/issues/760 + // We need the checksum values at install time, because some resources + // may have been purged, in which case the checksum must be reset to the + // value at install time. + var onDefaultChecksumsLoaded = function() { + defaultChecksums = Object.create(null); + var processChecksum = function(path, checksum) { + defaultChecksums[path] = checksum; + }; + parseChecksums(this.responseText || '', processChecksum); + checksumsReceived(); + }; + repoMetadata = new RepoMetadata(); repoMetadata.waiting.push(callback); readRepoFile('assets/checksums.txt', onRepoChecksumsLoaded); + getTextFileFromURL(vAPI.getURL('assets/checksums.txt'), onDefaultChecksumsLoaded); readLocalFile('assets/checksums.txt', onLocalChecksumsLoaded); }; @@ -1166,8 +1216,14 @@ exports.purge = function(pattern, before) { cachedAssetsManager.remove(pattern, before); }; +exports.purgeCacheableAsset = function(pattern, before) { + cachedAssetsManager.remove(pattern, before); + lastRepoMetaTimestamp = 0; +}; + exports.purgeAll = function(callback) { cachedAssetsManager.removeAll(callback); + lastRepoMetaTimestamp = 0; }; /******************************************************************************/ diff --git a/src/js/background.js b/src/js/background.js index 1aa5d54..8ff3b2b 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -57,7 +57,7 @@ return { collapseBlocked: true, colorBlindFriendly: false, contextMenuEnabled: true, - dynamicFilteringEnabled: false, + dynamicFilteringEnabled: true, experimentalEnabled: false, externalLists: defaultExternalLists, firewallPaneMinimized: true, diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index 776862f..f2986b8 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -478,7 +478,7 @@ var uBlockCollapser = (function() { // https://www.chromestatus.com/features/4668884095336448 // "Multiple shadow roots is being deprecated." if ( shadow !== null ) { - if ( shadow.className !== sessionId ) { + if ( shadow.className !== sessionId ) { elem.style.setProperty('display', 'none', 'important'); } continue; @@ -486,9 +486,13 @@ var uBlockCollapser = (function() { // https://github.com/gorhill/uBlock/pull/555 // Not all nodes can be shadowed: // https://github.com/w3c/webcomponents/issues/102 + // https://github.com/gorhill/uBlock/issues/762 + // Remove display style that might get in the way of the shadow + // node doing its magic. try { shadow = elem.createShadowRoot(); shadow.className = sessionId; + elem.style.removeProperty('display'); } catch (ex) { elem.style.setProperty('display', 'none', 'important'); } @@ -921,11 +925,15 @@ var uBlockCollapser = (function() { return; } var onMouseClick = function(ev) { + var elem = ev.target; + while ( elem !== null && elem.localName !== 'a' ) { + elem = elem.parentElement; + } messager.send({ what: 'mouseClick', x: ev.clientX, y: ev.clientY, - url: ev.target && ev.target.localName === 'a' ? ev.target.href : '' + url: elem !== null ? elem.href : '' }); }; diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js index ac7d924..a625085 100644 --- a/src/js/contentscript-start.js +++ b/src/js/contentscript-start.js @@ -195,9 +195,13 @@ var hideElements = function(selectors) { // https://github.com/gorhill/uBlock/pull/555 // Not all nodes can be shadowed: // https://github.com/w3c/webcomponents/issues/102 + // https://github.com/gorhill/uBlock/issues/762 + // Remove display style that might get in the way of the shadow + // node doing its magic. try { shadow = elem.createShadowRoot(); shadow.className = sessionId; + elem.style.removeProperty('display'); } catch (ex) { elem.style.setProperty('display', 'none', 'important'); } diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 9a5d23f..3ef45d4 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -230,20 +230,19 @@ FilterEntity.fromSelfie = function(s) { /******************************************************************************/ var FilterParser = function() { - this.prefix = ''; - this.suffix = ''; + this.prefix = this.suffix = this.style = ''; this.unhide = 0; this.hostnames = []; this.invalid = false; this.cosmetic = true; - this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/; + this.reParser = /^([^#]*?)(##|#@#)(.+)$/; + this.reScriptContains = /^script:contains\(.+?\)$/; }; /******************************************************************************/ FilterParser.prototype.reset = function() { - this.prefix = ''; - this.suffix = ''; + this.prefix = this.suffix = this.style = ''; this.unhide = 0; this.hostnames.length = 0; this.invalid = false; @@ -262,10 +261,20 @@ FilterParser.prototype.parse = function(s) { this.cosmetic = false; return this; } - - // Remember original string - this.prefix = matches[1]; - this.suffix = matches[3]; + this.prefix = matches[1].trim(); + this.unhide = matches[2].charAt(1) === '@' ? 1 : 0; + this.suffix = matches[3].trim(); + + // Cosmetic filters with explicit style properties can apply only: + // - to specific cosmetic filters (those which apply to a specific site) + // - to block cosmetic filters (not exception cosmetic filters) + if ( this.suffix.slice(-1) === '}' ) { + // Not supported for now: this code will ensure some backward + // compatibility for when cosmetic filters with explicit style + // properties start to be in use. + this.invalid = true; + return this; + } // 2014-05-23: // https://github.com/gorhill/httpswitchboard/issues/260 @@ -283,10 +292,31 @@ FilterParser.prototype.parse = function(s) { this.suffix = this.suffix.slice(1); } - this.unhide = matches[2].charAt(1) === '@' ? 1 : 0; if ( this.prefix !== '' ) { this.hostnames = this.prefix.split(/\s*,\s*/); } + + // Script tag filters: pre-process them so that can be used with minimal + // overhead in the content script. + // Examples: + // focus.de##script:contains(/uabInject/) + // focus.de##script:contains(uabInject) + if ( this.suffix.charAt(0) === 's' && this.reScriptContains.test(this.suffix) ) { + // Currently supported only as non-generic selector. Also, exception + // script tag filter makes no sense, ignore. + if ( this.hostnames.length === 0 || this.unhide === 1 ) { + this.invalid = true; + return this; + } + var suffix = this.suffix; + this.suffix = 'script//:'; + if ( suffix.charAt(16) !== '/' || suffix.slice(-2) !== '/)' ) { + this.suffix += suffix.slice(16, -1).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } else { + this.suffix += suffix.slice(17, -2).replace(/\\/g, '\\'); + } + } + return this; }; @@ -554,6 +584,8 @@ FilterContainer.prototype.reset = function() { // hostname, entity-based filters this.hostnameFilters = {}; this.entityFilters = {}; + this.scriptTagFilters = {}; + this.scriptTagFilterCount = 0; }; /******************************************************************************/ @@ -576,11 +608,14 @@ FilterContainer.prototype.isValidSelector = (function() { try { // https://github.com/gorhill/uBlock/issues/693 div.matches(s + ',\n#foo'); + return true; } catch (e) { - console.error('uBlock> invalid cosmetic filter:', s); - return false; } - return true; + if ( s.lastIndexOf('script//:', 0) === 0 ) { + return true; + } + console.error('uBlock> invalid cosmetic filter:', s); + return false; }; })(); @@ -790,6 +825,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { // h ir twitter.com .promoted-tweet if ( fields[0] === 'h' ) { + // Special filter: script tags. Not a real CSS selector. + if ( fields[3].lastIndexOf('script//:', 0) === 0 ) { + this.createScriptTagFilter(fields[2], fields[3].slice(9)); + continue; + } filter = new FilterHostname(fields[3], fields[2]); bucket = this.hostnameFilters[fields[1]]; if ( bucket === undefined ) { @@ -821,6 +861,11 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg, skip) { // entity selector if ( fields[0] === 'e' ) { + // Special filter: script tags. Not a real CSS selector. + if ( fields[2].lastIndexOf('script//:', 0) === 0 ) { + this.createScriptTagFilter(fields[1], fields[2].slice(9)); + continue; + } bucket = this.entityFilters[fields[1]]; if ( bucket === undefined ) { this.entityFilters[fields[1]] = [fields[2]]; @@ -886,6 +931,49 @@ FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) { /******************************************************************************/ +FilterContainer.prototype.createScriptTagFilter = function(hostname, s) { + if ( this.scriptTagFilters.hasOwnProperty(hostname) ) { + this.scriptTagFilters[hostname] += '|' + s; + } else { + this.scriptTagFilters[hostname] = s; + } + this.scriptTagFilterCount += 1; +}; + +/******************************************************************************/ + +FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) { + if ( this.scriptTagFilterCount === 0 ) { + return; + } + var out = [], hn = hostname, pos; + for (;;) { + if ( this.scriptTagFilters.hasOwnProperty(hn) ) { + out.push(this.scriptTagFilters[hn]); + } + if ( hn === domain ) { + break; + } + pos = hn.indexOf('.'); + if ( pos === -1 ) { + break; + } + hn = hn.slice(pos + 1); + } + pos = domain.indexOf('.'); + if ( pos !== -1 ) { + hn = domain.slice(0, pos); + if ( this.scriptTagFilters.hasOwnProperty(hn) ) { + out.push(this.scriptTagFilters[hn]); + } + } + if ( out.length !== 0 ) { + return out.join('|'); + } +}; + +/******************************************************************************/ + FilterContainer.prototype.freeze = function() { this.duplicateBuster = {}; @@ -939,7 +1027,9 @@ FilterContainer.prototype.toSelfie = function() { highMediumGenericHideCount: this.highMediumGenericHideCount, highHighGenericHide: this.highHighGenericHide, highHighGenericHideCount: this.highHighGenericHideCount, - genericDonthide: this.genericDonthide + genericDonthide: this.genericDonthide, + scriptTagFilters: this.scriptTagFilters, + scriptTagFilterCount: this.scriptTagFilterCount }; }; @@ -1000,6 +1090,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) { this.highHighGenericHide = selfie.highHighGenericHide; this.highHighGenericHideCount = selfie.highHighGenericHideCount; this.genericDonthide = selfie.genericDonthide; + this.scriptTagFilters = selfie.scriptTagFilters; + this.scriptTagFilterCount = selfie.scriptTagFilterCount; this.frozen = true; }; diff --git a/src/js/document-blocked.js b/src/js/document-blocked.js index 5b1c469..c64954a 100644 --- a/src/js/document-blocked.js +++ b/src/js/document-blocked.js @@ -193,12 +193,9 @@ uDom.nodeFromId('why').textContent = details.fs; }; var renderParams = function(parentNode, rawURL) { - var url = null; - try { - url = new URL(rawURL); - } catch(ex) { - } - if ( url === null || url.search.length === 0 ) { + var a = document.createElement('a'); + a.href = rawURL; + if ( a.search.length === 0 ) { return false; } @@ -209,7 +206,7 @@ uDom.nodeFromId('why').textContent = details.fs; ); parentNode.appendChild(li); - var params = url.search.slice(1).split('&'); + var params = a.search.slice(1).split('&'); var param, name, value, ul; for ( var i = 0; i < params.length; i++ ) { param = params[i]; diff --git a/src/js/messaging.js b/src/js/messaging.js index 2841fef..6076f8d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -815,7 +815,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'purgeCache': - µb.assets.purge(request.path); + µb.assets.purgeCacheableAsset(request.path); break; default: diff --git a/src/js/reverselookup-worker.js b/src/js/reverselookup-worker.js index b82f9ea..946001c 100644 --- a/src/js/reverselookup-worker.js +++ b/src/js/reverselookup-worker.js @@ -31,25 +31,35 @@ var listEntries = Object.create(null); // Helpers -var rescape = function(s) { +var reEscape = function(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }; +var reSpecialChars = /[\*\^\t\v\n]/; + /******************************************************************************/ var fromNetFilter = function(details) { var lists = []; - var entry, pos; + var compiledFilter = details.compiledFilter; + var entry, content, pos, c; for ( var path in listEntries ) { entry = listEntries[path]; if ( entry === undefined ) { continue; } - pos = entry.content.indexOf(details.compiledFilter); + content = entry.content; + pos = content.indexOf(compiledFilter); if ( pos === -1 ) { continue; } + // https://github.com/gorhill/uBlock/issues/835 + // We need an exact match. + c = content.charAt(pos + compiledFilter.length); + if ( c !== '' && reSpecialChars.test(c) === false ) { + continue; + } lists.push({ title: entry.title, supportURL: entry.supportURL @@ -103,16 +113,16 @@ var fromCosmeticFilter = function(details) { var matches = rePlainSelector.exec(filter); if ( matches ) { if ( matches[0] === filter ) { // simple CSS selector - reStr = rescape('c\vlg\v' + filter); + reStr = reEscape('c\vlg\v' + filter); } else { // complex CSS selector - reStr = rescape('c\vlg+\v' + matches[0] + '\v' + filter); + reStr = reEscape('c\vlg+\v' + matches[0] + '\v' + filter); } } else if ( reHighLow.test(filter) ) { // [alt] or [title] - reStr = rescape('c\vhlg0\v' + filter); + reStr = reEscape('c\vhlg0\v' + filter); } else if ( reHighMedium.test(filter) ) { // [href^="..."] - reStr = rescape('c\vhmg0\v') + '[a-z.-]+' + rescape('\v') + '[a-z]*' + rescape(filter); + reStr = reEscape('c\vhmg0\v') + '[a-z.-]+' + reEscape('\v') + '[a-z]*' + reEscape(filter); } else { // all else - reStr = rescape('c\vhhg0\v' + filter); + reStr = reEscape('c\vhhg0\v' + filter); } candidates[details.rawFilter] = new RegExp(reStr + '(?:\\n|$)'); @@ -125,9 +135,9 @@ var fromCosmeticFilter = function(details) { if ( hostname !== '' ) { for ( ;; ) { candidates[hostname + '##' + filter] = new RegExp( - rescape('c\vh\v') + + reEscape('c\vh\v') + '\\w+' + - rescape('\v' + hostname + '\v' + filter) + + reEscape('\v' + hostname + '\v' + filter) + '(?:\\n|$)' ); // If there is no valid domain, there won't be any other @@ -152,7 +162,7 @@ var fromCosmeticFilter = function(details) { if ( pos !== -1 ) { var entity = domain.slice(0, pos); candidates[entity + '.*##' + filter] = new RegExp( - rescape('c\ve\v' + entity + '\v' + filter) + + reEscape('c\ve\v' + entity + '\v' + filter) + '(?:\\n|$)' ); } diff --git a/src/js/rpcreceiver.js b/src/js/rpcreceiver.js new file mode 100644 index 0000000..995fd76 --- /dev/null +++ b/src/js/rpcreceiver.js @@ -0,0 +1,53 @@ +/******************************************************************************* + + 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 vAPI, µBlock */ + +/******************************************************************************/ + +(function() { + +'use strict'; + +/******************************************************************************/ + +if ( typeof vAPI.rpcReceiver !== 'object' ) { + return; +} + +/******************************************************************************/ + +vAPI.rpcReceiver.getScriptTagFilters = function(details) { + var µb = µBlock; + var cfe = µb.cosmeticFilteringEngine; + if ( !cfe ) { return; } + var hostname = details.hostname; + return cfe.retrieveScriptTagRegex( + µb.URI.domainFromHostname(hostname) || hostname, + hostname + ); +}; + +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 92cc7af..6bbf13a 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1454,7 +1454,10 @@ FilterParser.prototype.parseOptions = function(s) { this.parseOptParty(false, not); continue; } - if ( opt === 'elemhide' ) { + // https://issues.adblockplus.org/ticket/616 + // `generichide` concept already supported, just a matter of + // adding support for the new keyword. + if ( opt === 'elemhide' || opt === 'generichide' ) { if ( this.action === AllowAction ) { this.parseOptType('elemhide', false); this.action = BlockAction; |