From 2aadc2a969b4d21f7f52b5af556c4603252e1703 Mon Sep 17 00:00:00 2001 From: gorhill Date: Mon, 23 Nov 2015 07:52:50 -0500 Subject: [PATCH] new redirect engine: this fixes #949 (the video ad part) --- assets/checksums.txt | 1 + assets/ublock/redirect-rules.txt | 22 ++++ src/background.html | 1 + src/js/redirect-engine.js | 192 +++++++++++++++++++++++++++++++ src/js/start.js | 1 + src/js/storage.js | 20 ++++ src/js/traffic.js | 9 +- 7 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 assets/ublock/redirect-rules.txt create mode 100644 src/js/redirect-engine.js diff --git a/assets/checksums.txt b/assets/checksums.txt index 3b09c66df..4889a717e 100644 --- a/assets/checksums.txt +++ b/assets/checksums.txt @@ -4,6 +4,7 @@ b2dbf435507aa0262b289c67cbef2142 assets/ublock/filters.txt 146704ad1c0393e342afdb416762c183 assets/ublock/badware.txt c9c5cc56bec563bc1885847f925b9be2 assets/ublock/mirror-candidates.txt f9455a47b5024cc08ff3675ce79b58a9 assets/ublock/filter-lists.json +a37de4c1166ed23c273d44611142b5e4 assets/ublock/redirect-rules.txt 94c0a3eab74c42783855f07b22a429cf assets/thirdparties/home.fredfiber.no/langsholt/adblock.txt a82cb5ba5caf035ce00e97de81db5de7 assets/thirdparties/www.zoso.ro/pages/rolist.txt 72373316d0e7ad22604d307c2d93e7cc assets/thirdparties/adblock.gardar.net/is.abp.txt diff --git a/assets/ublock/redirect-rules.txt b/assets/ublock/redirect-rules.txt new file mode 100644 index 000000000..960f5eab9 --- /dev/null +++ b/assets/ublock/redirect-rules.txt @@ -0,0 +1,22 @@ +redirects: + + hd-main.js application/javascript + var L = (function(){ + var l = {}; + var fn = function(){}; + var props = ["pf","ed","Qe","fd","xh","Uc","ef","zd","Ad","Qc","Ri","Wc","Vc","Xc","Wg","rd","qd","sd","Pe","Id","Hd","Jd","fg","Fd","Ed","Gd","ek","Cd","Bd","Dd","Nj","Sc","Rc","Tc","wg","xd","wd","yd","fh","ld","md","nd","Re","cd","Pc","ke","Yc","Xg","jd","kd","oh","ad","bd","mi","gd","hd","ae","dd","fk","ij","ud","td","vd","ig","od","pd","Yd","$j","Oc","bf"]; + for ( var i = 0; i < props.length; i++ ) { + l[props[i]] = fn; + } + return l; + })(); + +rules: + + # Prevent difficult to block video ads. Examples: + # http://www.chip.de/news/Halbvoll-in-2-Minuten-Huawei-zeigt-neue-Superakkus_85752247.html + # Block filters to use: + # ||s3.amazonaws.com/homad-global-configs.schneevonmorgen.com/hd-main.js$script,domain=cdnapi.kaltura.com,important + # ||hgc.svonm.com/hd-main.js$script,domain=cdnapi.kaltura.com,important + cdnapi.kaltura.com s3.amazonaws.com script /\/hd-main.js$/ hd-main.js + cdnapi.kaltura.com hgc.svonm.com script /\/hd-main.js$/ hd-main.js diff --git a/src/background.html b/src/background.html index 43863d28d..1a6b5c9ae 100644 --- a/src/background.html +++ b/src/background.html @@ -15,6 +15,7 @@ + diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js new file mode 100644 index 000000000..d6d4a6127 --- /dev/null +++ b/src/js/redirect-engine.js @@ -0,0 +1,192 @@ +/******************************************************************************* + + 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.redirectEngine = (function(){ + +'use strict'; + +/******************************************************************************/ + +var toBroaderHostname = function(hostname) { + if ( hostname === '*' ) { + return ''; + } + var pos = hostname.indexOf('.'); + if ( pos === -1 ) { + return '*'; + } + return hostname.slice(pos + 1); +}; + +/******************************************************************************/ + +var RedirectEngine = function() { + this.reset(); +}; + +/******************************************************************************/ + +RedirectEngine.prototype.reset = function() { + this.redirects = Object.create(null); + this.rules = Object.create(null); +}; + +/******************************************************************************/ + +RedirectEngine.prototype.lookup = function(context) { + var typeEntry = this.rules[context.requestType]; + if ( typeEntry === undefined ) { + return; + } + var src, des = context.requestHostname, + srcHostname = context.pageHostname, + reqURL = context.requestURL, + desEntry, entries, i, entry; + for (;;) { + desEntry = typeEntry[des]; + if ( desEntry !== undefined ) { + src = srcHostname; + for (;;) { + entries = desEntry[src]; + if ( entries !== undefined ) { + i = entries.length; + while ( i-- ) { + entry = entries[i]; + if ( entry.c.test(reqURL) ) { + return this.redirects[entry.r]; + } + } + } + } + src = toBroaderHostname(src); + if ( src === '' ) { + break; + } + } + des = toBroaderHostname(des); + if ( des === '' ) { + break; + } + } +}; + +/******************************************************************************/ + +// TODO: combine same key-redirect pairs into a single regex. + +RedirectEngine.prototype.fromString = function(text) { + var textEnd = text.length; + var lineBeg = 0, lineEnd; + var mode, modeData, line, fields, encoded, data; + var reSource, typeEntry, desEntry, ruleEntries; + + this.reset(); + + while ( lineBeg < textEnd ) { + lineEnd = text.indexOf('\n', lineBeg); + if ( lineEnd < 0 ) { + lineEnd = text.indexOf('\r', lineBeg); + if ( lineEnd < 0 ) { + lineEnd = textEnd; + } + } + line = text.slice(lineBeg, lineEnd).trim(); + lineBeg = lineEnd + 1; + + if ( line.charAt(0) === '#' ) { + continue; + } + + if ( line.slice(-1) === ':' ) { + mode = line.slice(0, -1); + continue; + } + + if ( mode === 'redirects' ) { + fields = line.split(/\s+/); + if ( fields.length !== 2 ) { + continue; + } + mode = 'redirects/redirect'; + modeData = fields; + continue; + } + + if ( mode === 'redirects/redirect' ) { + if ( line !== '' ) { + modeData.push(line); + continue; + } + encoded = modeData[1].indexOf(';') !== -1; + data = modeData.slice(2).join(encoded ? '' : '\n'); + this.redirects[modeData[0]] = + 'data:' + + modeData[1] + + (encoded ? '' : ';base64') + + ',' + + (encoded ? data : btoa(data)); + mode = 'redirects'; + continue; + } + + if ( mode === 'rules' ) { + fields = line.split(/\s+/); + if ( fields.length !== 5 ) { + continue; + } + reSource = fields[3]; + if ( reSource.charAt(0) !== '/' || reSource.slice(-1) !== '/' ) { + reSource = reSource.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } else { + reSource = reSource.slice(1, -1); + } + typeEntry = this.rules[fields[2]]; + if ( typeEntry === undefined ) { + typeEntry = this.rules[fields[2]] = Object.create(null); + } + desEntry = typeEntry[fields[1]]; + if ( desEntry === undefined ) { + desEntry = typeEntry[fields[1]] = Object.create(null); + } + ruleEntries = desEntry[fields[0]]; + if ( ruleEntries === undefined ) { + ruleEntries = desEntry[fields[0]] = []; + } + ruleEntries.push({ + c: new RegExp(reSource), + r: fields[4] + }); + continue; + } + } +}; + +/******************************************************************************/ + +return new RedirectEngine(); + +/******************************************************************************/ + +})(); diff --git a/src/js/start.js b/src/js/start.js index 782cb1ff1..517131d8b 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -211,6 +211,7 @@ var onFirstFetchReady = function(fetched) { fromFetch(µb.restoreBackupSettings, fetched); onNetWhitelistReady(fetched.netWhitelist); onVersionReady(fetched.version); + µb.loadRedirectRules(); // If we have a selfie, skip loading PSL, filters if ( onSelfieReady(fetched.selfie) ) { diff --git a/src/js/storage.js b/src/js/storage.js index e14986dea..902e06a05 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -646,6 +646,26 @@ /******************************************************************************/ +// TODO: toSelfie/fromSelfie. + +µBlock.loadRedirectRules = function(callback) { + var µb = this; + + if ( typeof callback !== 'function' ) { + callback = this.noopFunc; + } + var onRulesLoaded = function(details) { + if ( details.content !== '' ) { + µb.redirectEngine.fromString(details.content); + } + callback(); + }; + + this.assets.get('assets/ublock/redirect-rules.txt', onRulesLoaded); +}; + +/******************************************************************************/ + µBlock.loadPublicSuffixList = function(callback) { var µb = this; var path = µb.pslPath; diff --git a/src/js/traffic.js b/src/js/traffic.js index 66e654803..348d65cbf 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -131,9 +131,12 @@ var onBeforeRequest = function(details) { µb.updateBadgeAsync(tabId); } - // https://github.com/chrisaljoudi/uBlock/issues/18 - // Do not use redirection, we need to block outright to be sure the request - // will not be made. There can be no such guarantee with redirection. + // https://github.com/gorhill/uBlock/issues/949 + // Redirect blocked request? + var url = µb.redirectEngine.lookup(requestContext); + if ( url !== undefined ) { + return { redirectUrl: url }; + } return { cancel: true }; };