this fixes #1100: ability to inject directly a resource from redirection library

This commit is contained in:
gorhill 2015-12-22 16:32:09 -05:00
parent 520e251112
commit 1fe5a16c32
6 changed files with 163 additions and 35 deletions

View File

@ -1,5 +1,5 @@
4d0e777a82576a2ec771742abb91925b assets/ublock/unbreak.txt
f2624cc77fae3f1be5a6b5d5b8027bdc assets/ublock/redirect-resources.txt
62111a29f0a5cb361ba8dbae92054adb assets/ublock/redirect-resources.txt
6c077d6d5b39e8a5a407966ad62c9c32 assets/ublock/privacy.txt
9bcc718383fec8b2ce0f9c379f45da9a assets/ublock/filters.txt
146704ad1c0393e342afdb416762c183 assets/ublock/badware.txt

View File

@ -228,3 +228,10 @@ yavli-defuser.js application/javascript
} catch (ex) {
}
})();
# Addefend defuser
uabinject-defuser.js application/javascript
(function() {
window.uabpdl = window.uabInject = window.uabDetect = true;
})();

View File

@ -90,8 +90,8 @@ return {
// read-only
systemSettings: {
compiledMagic: 'cxubjrcfrnrq',
selfieMagic: 'mnigwksyvgkv'
compiledMagic: 'xtsldiywhvgc',
selfieMagic: 'xtsldiywhvgc'
},
restoreBackupSettings: {

View File

@ -116,6 +116,8 @@ var cosmeticFilters = function(details) {
vAPI.hideCosmeticFilters = hideCosmeticFilters;
};
/******************************************************************************/
var netFilters = function(details) {
var parent = document.head || document.documentElement;
if ( !parent ) {
@ -131,6 +133,29 @@ var netFilters = function(details) {
//console.debug('document.querySelectorAll("%s") = %o', text, document.querySelectorAll(text));
};
/******************************************************************************/
// Create script tags and assign data URIs looked up from our library of
// redirection resources: Sometimes it is useful to use these resources as
// standalone scriptlets.
// Library of redirection resources:
// https://github.com/gorhill/uBlock/blob/master/assets/ublock/redirect-resources.txt
var injectScripts = function(dataURIs) {
var parent = document.head || document.documentElement;
if ( !parent ) {
return;
}
var i = dataURIs.length, scriptTag;
while ( i-- ) {
scriptTag = document.createElement('script');
scriptTag.src = dataURIs[i];
parent.appendChild(scriptTag);
}
};
/******************************************************************************/
var filteringHandler = function(details) {
var styleTagCount = vAPI.styles.length;
@ -144,6 +169,9 @@ var filteringHandler = function(details) {
if ( details.netHide.length !== 0 ) {
netFilters(details);
}
if ( details.scripts !== 0 ) {
injectScripts(details.scripts);
}
// The port will never be used again at this point, disconnecting allows
// the browser to flush this script from memory.
}
@ -164,6 +192,8 @@ var filteringHandler = function(details) {
localMessager.close();
};
/******************************************************************************/
var hideElements = function(selectors) {
if ( document.body === null ) {
return;
@ -213,6 +243,8 @@ var hideElements = function(selectors) {
}
};
/******************************************************************************/
var url = window.location.href;
localMessager.send(
{

View File

@ -244,6 +244,7 @@ var FilterParser = function() {
this.hostnames = [];
this.invalid = false;
this.cosmetic = true;
this.reScriptTagFilter = /^script:(contains|inject)\((.+?)\)$/;
};
/******************************************************************************/
@ -353,12 +354,10 @@ FilterParser.prototype.parse = function(raw) {
// Examples:
// focus.de##script:contains(/uabInject/)
// focus.de##script:contains(uabInject)
// focus.de##script:inject(uabinject-defuser.js)
// Inline script tag filter?
if (
this.suffix.startsWith('script:contains(') === false ||
this.suffix.endsWith(')') === false
) {
var matches = this.reScriptTagFilter.exec(this.suffix);
if ( matches === null ) {
return this;
}
@ -369,27 +368,32 @@ FilterParser.prototype.parse = function(raw) {
return this;
}
var suffix = this.suffix.slice(16, -1);
this.suffix = 'script//:';
var token = matches[2];
// Plain string-based?
if ( suffix.startsWith('/') === false || suffix.endsWith('/') === false ) {
this.suffix += suffix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
return this;
}
// Regex-based
this.suffix += suffix.slice(1, -1);
// Valid regex?
if ( isBadRegex(this.suffix) ) {
console.error(
"uBlock Origin> discarding bad regular expression-based cosmetic filter '%s': '%s'",
raw,
isBadRegex.message
);
switch ( matches[1] ) {
case 'contains':
this.suffix = 'script?';
// Plain string- or regex-based?
if ( token.startsWith('/') === false || token.endsWith('/') === false ) {
this.suffix += token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
} else {
this.suffix += token.slice(1, -1);
if ( isBadRegex(this.suffix) ) {
console.error(
"uBlock Origin> discarding bad regular expression-based cosmetic filter '%s': '%s'",
raw,
isBadRegex.message
);
this.invalid = true;
}
}
break;
case 'inject':
this.suffix = 'script+' + token;
break;
default:
this.invalid = true;
return this;
break;
}
return this;
@ -661,6 +665,8 @@ FilterContainer.prototype.reset = function() {
this.entityFilters = {};
this.scriptTagFilters = {};
this.scriptTagFilterCount = 0;
this.scriptTags = {};
this.scriptTagCount = 0;
};
/******************************************************************************/
@ -686,8 +692,11 @@ FilterContainer.prototype.isValidSelector = (function() {
return true;
} catch (e) {
}
if ( s.startsWith('script//:') ) {
return true;
// We reach this point very rarely.
if ( s.startsWith('script') ) {
if ( s.startsWith('?', 6) || s.startsWith('+', 6) ) {
return true;
}
}
console.error('uBlock> invalid cosmetic filter:', s);
return false;
@ -914,8 +923,8 @@ 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].startsWith('script//:') ) {
this.createScriptTagFilter(fields[2], fields[3].slice(9));
if ( fields[3].startsWith('script') ) {
this.createScriptFilter(fields[2], fields[3].slice(6));
continue;
}
filter = new FilterHostname(fields[3], fields[2]);
@ -950,8 +959,8 @@ 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].startsWith('script//:') ) {
this.createScriptTagFilter(fields[1], fields[2].slice(9));
if ( fields[2].startsWith('script?') ) {
this.createScriptFilter(fields[1], fields[2].slice(6));
continue;
}
bucket = this.entityFilters[fields[1]];
@ -1019,6 +1028,17 @@ FilterContainer.prototype.skipCompiledContent = function(text, lineBeg) {
/******************************************************************************/
FilterContainer.prototype.createScriptFilter = function(hostname, s) {
if ( s.charAt(0) === '?' ) {
return this.createScriptTagFilter(hostname, s.slice(1));
}
if ( s.charAt(0) === '+' ) {
return this.createScriptTagInjector(hostname, s.slice(1));
}
};
/******************************************************************************/
FilterContainer.prototype.createScriptTagFilter = function(hostname, s) {
if ( this.scriptTagFilters.hasOwnProperty(hostname) ) {
this.scriptTagFilters[hostname] += '|' + s;
@ -1062,6 +1082,61 @@ FilterContainer.prototype.retrieveScriptTagRegex = function(domain, hostname) {
/******************************************************************************/
FilterContainer.prototype.createScriptTagInjector = function(hostname, s) {
if ( this.scriptTags.hasOwnProperty(hostname) ) {
this.scriptTags[hostname].push(s);
} else {
this.scriptTags[hostname] = [s];
}
this.scriptTagCount += 1;
};
/******************************************************************************/
FilterContainer.prototype.retrieveScriptTags = function(domain, hostname) {
if ( this.scriptTagCount === 0 ) {
return;
}
var reng = µb.redirectEngine;
if ( !reng ) {
return;
}
var out = [],
hn = hostname, pos, rnames, i, dataURI;
for (;;) {
rnames = this.scriptTags[hn];
i = rnames && rnames.length || 0;
while ( i-- ) {
if ( (dataURI = reng.resourceFromName(rnames[i], 'application/javascript')) ) {
out.push(dataURI);
}
}
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);
rnames = this.scriptTags[hn];
i = rnames && rnames.length || 0;
while ( i-- ) {
if ( (dataURI = reng.resourceFromName(rnames[i], 'application/javascript')) ) {
out.push(dataURI);
}
}
}
return out;
};
/******************************************************************************/
FilterContainer.prototype.freeze = function() {
this.duplicateBuster = {};
@ -1117,7 +1192,9 @@ FilterContainer.prototype.toSelfie = function() {
highHighGenericHideCount: this.highHighGenericHideCount,
genericDonthide: this.genericDonthide,
scriptTagFilters: this.scriptTagFilters,
scriptTagFilterCount: this.scriptTagFilterCount
scriptTagFilterCount: this.scriptTagFilterCount,
scriptTags: this.scriptTags,
scriptTagCount: this.scriptTagCount
};
};
@ -1180,6 +1257,8 @@ FilterContainer.prototype.fromSelfie = function(selfie) {
this.genericDonthide = selfie.genericDonthide;
this.scriptTagFilters = selfie.scriptTagFilters;
this.scriptTagFilterCount = selfie.scriptTagFilterCount;
this.scriptTags = selfie.scriptTags;
this.scriptTagCount = selfie.scriptTagCount;
this.frozen = true;
};
@ -1361,7 +1440,8 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
cosmeticHide: [],
cosmeticDonthide: [],
netHide: [],
netCollapse: µb.userSettings.collapseBlocked
netCollapse: µb.userSettings.collapseBlocked,
scripts: this.retrieveScriptTags(domain, hostname)
};
var hash, bucket;

View File

@ -359,6 +359,15 @@ RedirectEngine.prototype.fromSelfie = function(selfie) {
/******************************************************************************/
RedirectEngine.prototype.resourceFromName = function(name, mime) {
var entry = this.resources[name];
if ( entry && (mime === undefined || entry.mime.startsWith(mime)) ) {
return entry.toURL();
}
};
/******************************************************************************/
// TODO: combine same key-redirect pairs into a single regex.
RedirectEngine.prototype.resourcesFromString = function(text) {