From 93f49a61d7778ecc55fa014f409a8c9377162b17 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 5 Apr 2018 07:29:15 -0400 Subject: [PATCH] add pre-processor directives to filter list compiler (https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917) --- platform/chromium/vapi-background.js | 9 ++-- platform/chromium/vapi-common.js | 43 +++++++++++++++----- platform/webext/vapi-webrequest.js | 5 ++- src/js/assets.js | 41 ++++++++++--------- src/js/background.js | 9 ++++ src/js/start.js | 2 +- src/js/static-net-filtering.js | 5 ++- src/js/storage.js | 61 ++++++++++++++++++++++++++-- src/js/traffic.js | 3 +- 9 files changed, 135 insertions(+), 43 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index 698fd3654..dd628ce12 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -499,7 +499,10 @@ vAPI.tabs.open = function(details) { // https://github.com/gorhill/uBlock/issues/3053#issuecomment-332276818 // - Do not try to lookup uBO's own pages with FF 55 or less. - if ( /^Mozilla-Firefox-5[2-5]\./.test(vAPI.webextFlavor) ) { + if ( + vAPI.webextFlavor.soup.has('firefox') && + vAPI.webextFlavor.major < 56 + ) { wrapper(); return; } @@ -1139,7 +1142,7 @@ vAPI.cloud = (function() { var evalMaxChunkSize = function() { return Math.floor( (chrome.storage.sync.QUOTA_BYTES_PER_ITEM || 8192) * - (vAPI.webextFlavor.startsWith('Mozilla-Firefox-') ? 0.6 : 0.75) + (vAPI.webextFlavor.soup.has('firefox') ? 0.6 : 0.75) ); }; @@ -1247,7 +1250,7 @@ vAPI.cloud = (function() { // until such cases are reported for other browsers, we will // reset the (now corrupted) content of the cloud storage // only on Firefox. - if ( vAPI.webextFlavor.startsWith('Mozilla-Firefox-') ) { + if ( vAPI.webextFlavor.soup.has('firefox') ) { chunkCount = 0; } } diff --git a/platform/chromium/vapi-common.js b/platform/chromium/vapi-common.js index 36757a48b..ec65dfd80 100644 --- a/platform/chromium/vapi-common.js +++ b/platform/chromium/vapi-common.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2017 The uBlock Origin authors + Copyright (C) 2014-2018 The uBlock Origin authors 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 @@ -36,46 +36,67 @@ vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self); /******************************************************************************/ -vAPI.webextFlavor = (function() { +vAPI.webextFlavor = { + major: 0, + soup: new Set() +}; + +(function() { var ua = navigator.userAgent, - match, reEx; + match, reEx, + flavor = vAPI.webextFlavor; var dispatch = function() { window.dispatchEvent(new CustomEvent('webextFlavor')); }; // Order of tests is important! + if ( /\bMobile\b/.test(ua) ) { + flavor.soup.add('mobile'); + } + // Asynchronous if ( self.browser instanceof Object && typeof self.browser.runtime.getBrowserInfo === 'function' ) { self.browser.runtime.getBrowserInfo().then(function(info) { - vAPI.webextFlavor = - info.vendor + '-' + info.name + '-' + info.version; + flavor.major = parseInt(info.version, 10) || 0; + flavor.soup.add(info.vendor.toLowerCase()) + .add(info.name.toLowerCase()); dispatch(); }); match = /Firefox\/([\d.]+)/.exec(ua); - return match !== null ? 'Mozilla-Firefox-' + match[1] : ''; + if ( match !== null ) { + flavor.major = parseInt(match[1], 10) || 0; + flavor.soup.add('mozilla').add('firefox'); + } + return; } // Synchronous /* Don't starve potential listeners: */ vAPI.setTimeout(dispatch, 97); + match = /OPR\/([\d.]+)/.exec(ua); if ( match !== null ) { reEx = /Chrom(?:e|ium)\/([\d.]+)/; if ( reEx.test(ua) ) { match = reEx.exec(ua); } - return 'Opera-Chromium-' + match[1]; + flavor.major = parseInt(match[1], 10) || 0; + flavor.soup.add('opera').add('chromium'); + return; } match = /Chromium\/([\d.]+)/.exec(ua); if ( match !== null ) { - return 'Chromium-Chromium-' + match[1]; + flavor.major = parseInt(match[1], 10) || 0; + flavor.soup.add('chromium'); + return; } match = /Chrome\/([\d.]+)/.exec(ua); if ( match !== null ) { - return 'Google-Chromium-' + match[1]; + flavor.major = parseInt(match[1], 10) || 0; + flavor.soup.add('google').add('chromium'); + return; } - return ''; })(); /******************************************************************************/ @@ -121,7 +142,7 @@ setScriptDirection(vAPI.i18n('@@ui_locale')); // `window.open('', '_self').close()`. vAPI.closePopup = function() { - if ( /^Mozilla-Firefox-/.test(vAPI.webextFlavor) ) { + if ( vAPI.webextFlavor.soup.has('firefox') ) { window.close(); return; } diff --git a/platform/webext/vapi-webrequest.js b/platform/webext/vapi-webrequest.js index 81607498b..682fb5461 100644 --- a/platform/webext/vapi-webrequest.js +++ b/platform/webext/vapi-webrequest.js @@ -41,10 +41,11 @@ vAPI.net = { vAPI.net.registerListeners = function() { // https://github.com/gorhill/uBlock/issues/2950 - // Firefox 55 does not normalize URLs to ASCII, uBO must do this itself. + // Firefox 56 does not normalize URLs to ASCII, uBO must do this itself. // https://bugzilla.mozilla.org/show_bug.cgi?id=945240 let evalMustPunycode = function() { - return /^Mozilla-Firefox-5[0-6]/.test(vAPI.webextFlavor); + return vAPI.webextFlavor.soup.has('firefox') && + vAPI.webextFlavor.major < 57; }; let mustPunycode = evalMustPunycode(); diff --git a/src/js/assets.js b/src/js/assets.js index d940139c9..904885a3a 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -177,7 +177,24 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { pendingSublistURLs = new Set([ mainlistURL ]), loadedSublistURLs = new Set(), toParsedURL = api.fetchFilterList.toParsedURL, - parsedMainURL = toParsedURL(mainlistURL); + parsedURL = toParsedURL(mainlistURL); + + var processIncludeDirectives = function(details) { + var reInclude = /^!#include +(\S+)/gm; + for (;;) { + var match = reInclude.exec(details.content); + if ( match === null ) { break; } + if ( toParsedURL(match[1]) !== undefined ) { continue; } + if ( match[1].indexOf('..') !== -1 ) { continue; } + var subURL = + parsedURL.origin + + parsedURL.pathname.replace(/[^/]+$/, match[1]); + if ( pendingSublistURLs.has(subURL) ) { continue; } + if ( loadedSublistURLs.has(subURL) ) { continue; } + pendingSublistURLs.add(subURL); + api.fetchText(subURL, onLocalLoadSuccess, onLocalLoadError); + } + }; var onLocalLoadSuccess = function(details) { if ( errored ) { return; } @@ -189,24 +206,8 @@ api.fetchFilterList = function(mainlistURL, onLoad, onError) { if ( isSublist ) { content.push('\n! ' + '>>>>>>>> ' + details.url); } content.push(details.content.trim()); if ( isSublist ) { content.push('! <<<<<<<< ' + details.url); } - if ( - parsedMainURL !== undefined && - parsedMainURL.pathname.length > 0 - ) { - var reInclude = /^!#include +(\S+)/gm; - for (;;) { - var match = reInclude.exec(details.content); - if ( match === null ) { break; } - if ( toParsedURL(match[1]) !== undefined ) { continue; } - if ( match[1].indexOf('..') !== -1 ) { continue; } - var subURL = - parsedMainURL.origin + - parsedMainURL.pathname.replace(/[^/]+$/, match[1]); - if ( pendingSublistURLs.has(subURL) ) { continue; } - if ( loadedSublistURLs.has(subURL) ) { continue; } - pendingSublistURLs.add(subURL); - api.fetchText(subURL, onLocalLoadSuccess, onLocalLoadError); - } + if ( parsedURL !== undefined && parsedURL.pathname.length > 0 ) { + processIncludeDirectives(details); } if ( pendingSublistURLs.size !== 0 ) { return; } @@ -912,7 +913,7 @@ var updateFirst = function() { typeof browser === 'object' && browser.runtime.getManifest(); noRemoteResources = - /^Mozilla-Firefox-/.test(vAPI.webextFlavor) && + vAPI.webextFlavor.soup.has('firefox') && manifest instanceof Object && manifest.applications instanceof Object && manifest.applications.gecko instanceof Object && diff --git a/src/js/background.js b/src/js/background.js index 8ba758452..66c843845 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -26,6 +26,14 @@ /******************************************************************************/ +// Not all platforms may have properly declared vAPI.webextFlavor. + +if ( vAPI.webextFlavor === undefined ) { + vAPI.webextFlavor = { major: 0, soup: new Set() }; +} + +/******************************************************************************/ + var µBlock = (function() { // jshint ignore:line var oneSecond = 1000, @@ -111,6 +119,7 @@ var µBlock = (function() { // jshint ignore:line 'moz-extension-scheme', 'opera-scheme', 'vivaldi-scheme', + 'wyciwyg-scheme', // Firefox's "What-You-Cache-Is-What-You-Get" '' ].join('\n'), diff --git a/src/js/start.js b/src/js/start.js index ebecd1573..95444489c 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -109,7 +109,7 @@ var onVersionReady = function(lastVersion) { // new version is detected, as resources.txt may have changed since last // release. This will be done only for release versions of Firefox. if ( - /^Mozilla-Firefox-/.test(vAPI.webextFlavor) && + vAPI.webextFlavor.soup.has('firefox') && /(b|rc)\d+$/.test(vAPI.app.version) === false ) { µb.redirectEngine.invalidateResourcesSelfie(); diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js index 4a1fad2bb..46d37db33 100644 --- a/src/js/static-net-filtering.js +++ b/src/js/static-net-filtering.js @@ -1605,7 +1605,10 @@ FilterParser.prototype.translate = function() { this.dataStr = "connect-src https: http:"; // https://bugs.chromium.org/p/chromium/issues/detail?id=669086 // TODO: remove when most users are beyond Chromium v56 - if ( /-Chromium-(?:4|5[0-6])/.test(vAPI.webextFlavor) ) { + if ( + vAPI.webextFlavor.soup.has('chromium') && + vAPI.webextFlavor.major < 57 + ) { this.dataStr += '; frame-src *'; } return; diff --git a/src/js/storage.js b/src/js/storage.js index 0d51be9aa..b12f750be 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -778,15 +778,13 @@ reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)\b/, reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/, line, c, pos, - lineIter = new this.LineIterator(rawText); + lineIter = new this.LineIterator(this.processDirectives(rawText)); while ( lineIter.eot() === false ) { - line = lineIter.next().trim(); - // rhill 2014-04-18: The trim is important here, as without it there // could be a lingering `\r` which would cause problems in the // following parsing code. - + line = lineIter.next().trim(); if ( line.length === 0 ) { continue; } // Strip comments @@ -851,6 +849,61 @@ /******************************************************************************/ +// https://github.com/AdguardTeam/AdguardBrowserExtension/issues/917 + +µBlock.processDirectives = function(content) { + var reIf = /^!#(if|endif)\b([^\n]*)/gm, + parts = [], + beg = 0, depth = 0, discard = false; + while ( beg < content.length ) { + var match = reIf.exec(content); + if ( match === null ) { break; } + if ( match[1] === 'if' ) { + var expr = match[2].trim(); + var target = expr.startsWith('!'); + if ( target ) { expr = expr.slice(1); } + var token = this.processDirectives.tokens.get(expr); + if ( + depth === 0 && + discard === false && + token !== undefined && + vAPI.webextFlavor.soup.has(token) === target + ) { + parts.push(content.slice(beg, match.index)); + discard = true; + } + depth += 1; + continue; + } + depth -= 1; + if ( depth < 0 ) { break; } + if ( depth === 0 && discard ) { + beg = match.index + match[0].length + 1; + discard = false; + } + } + if ( depth === 0 && parts.length !== 0 ) { + parts.push(content.slice(beg)); + content = parts.join('\n'); + } + return content.trim(); +}; + +µBlock.processDirectives.tokens = new Map([ + [ 'ext_chromium', 'chromium' ], + [ 'ext_edge', 'edge' ], + [ 'ext_firefox', 'firefox' ], + [ 'ext_mobile', 'mobile' ], + [ 'ext_safari', 'safari' ], + [ 'adguard_ext_chromium', 'chromium' ], + [ 'adguard_ext_edge', 'edge' ], + [ 'adguard_ext_firefox', 'firefox' ], + [ 'adguard_ext_mobile', 'mobile' ], + [ 'adguard_ext_safari', 'safari' ], +]); + +/******************************************************************************/ + µBlock.loadRedirectResources = function(updatedContent) { var µb = this, content = ''; diff --git a/src/js/traffic.js b/src/js/traffic.js index 6ee787e67..4d148a233 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -1127,7 +1127,8 @@ var injectCSP = function(pageStore, details) { // https://github.com/gorhill/uMatrix/issues/967#issuecomment-373002011 // This can be removed once Firefox 60 ESR is released. var evalCantMergeCSPHeaders = function() { - return /^Mozilla-Firefox-5[2-8]/.test(vAPI.webextFlavor); + return vAPI.webextFlavor.soup.has('firefox') && + vAPI.webextFlavor.major < 59; }; var cantMergeCSPHeaders = evalCantMergeCSPHeaders();