2015-11-23 13:52:50 +01:00
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
uBlock Origin - a browser extension to block requests.
|
2018-02-15 23:25:38 +01:00
|
|
|
Copyright (C) 2015-2018 Raymond Hill
|
2015-11-23 13:52:50 +01:00
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-10-10 15:01:05 +02:00
|
|
|
µBlock.redirectEngine = (function(){
|
2015-11-23 13:52:50 +01:00
|
|
|
|
2015-11-26 23:56:30 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2018-02-15 23:25:38 +01:00
|
|
|
var warResolve = (function() {
|
|
|
|
var timer;
|
|
|
|
var toResolve = new Set();
|
|
|
|
var toProcess = new Set();
|
|
|
|
var reMimeParser = /^[^/]+\/([^\s;]+)/;
|
|
|
|
|
|
|
|
var replacer = function(s) {
|
|
|
|
if ( s === '+' ) { return '-'; }
|
|
|
|
if ( s === '/' ) { return '_'; }
|
|
|
|
if ( s === '=' ) { return ''; }
|
|
|
|
return s;
|
|
|
|
};
|
|
|
|
|
|
|
|
var filenameFromToken = function(token, mime) {
|
|
|
|
var name = btoa(token).replace(/[+/=]/g, replacer);
|
|
|
|
var match = reMimeParser.exec(mime);
|
|
|
|
if ( match !== null ) {
|
|
|
|
name += '.' + match[1];
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
};
|
|
|
|
|
|
|
|
var onResolved = function(success, token, url) {
|
|
|
|
var reng = µBlock.redirectEngine;
|
|
|
|
this.onload = this.onerror = null;
|
|
|
|
var resource = reng.resources.get(token);
|
|
|
|
if ( resource !== undefined && success ) {
|
|
|
|
resource.warURL = url;
|
|
|
|
}
|
|
|
|
toProcess.delete(token);
|
|
|
|
if ( toResolve.size === 0 && toProcess.size === 0 ) {
|
|
|
|
reng.selfieFromResources();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var resolvePending = function() {
|
|
|
|
timer = undefined;
|
|
|
|
var reng = µBlock.redirectEngine,
|
|
|
|
resources = reng.resources,
|
|
|
|
n = 8; // max number of xhr at once
|
|
|
|
for ( var token of toResolve ) {
|
|
|
|
var resource = resources.get(token);
|
|
|
|
toResolve.delete(token);
|
|
|
|
if ( resource === undefined ) { continue; }
|
|
|
|
toProcess.add(token);
|
|
|
|
var url = vAPI.getURL(
|
|
|
|
'/web_accessible_resources/' +
|
|
|
|
filenameFromToken(token, resource.mime)
|
|
|
|
);
|
|
|
|
var xhr = new XMLHttpRequest();
|
|
|
|
xhr.timeout = 1000;
|
|
|
|
xhr.open('head', url + '?secret=' + vAPI.warSecret);
|
|
|
|
xhr.onload = onResolved.bind(this, true, token, url);
|
|
|
|
xhr.onerror = onResolved.bind(this, false, token, url);
|
|
|
|
xhr.responseType = 'text';
|
|
|
|
xhr.send();
|
|
|
|
n -= 1;
|
|
|
|
if ( n === 0 ) { break; }
|
|
|
|
}
|
|
|
|
if ( toResolve.size !== 0 ) {
|
|
|
|
timer = vAPI.setTimeout(resolvePending, 5);
|
|
|
|
} else if ( toProcess.size === 0 ) {
|
|
|
|
reng.selfieFromResources();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
return function(token) {
|
|
|
|
if ( vAPI.warSecret !== undefined ) {
|
|
|
|
toResolve.add(token);
|
|
|
|
}
|
|
|
|
if ( timer === undefined ) {
|
|
|
|
timer = vAPI.setTimeout(resolvePending, 1);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-29 17:04:42 +01:00
|
|
|
var RedirectEntry = function() {
|
|
|
|
this.mime = '';
|
|
|
|
this.data = '';
|
2018-02-15 23:25:38 +01:00
|
|
|
this.warURL = undefined;
|
2015-11-26 23:56:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-12-23 17:02:36 +01:00
|
|
|
RedirectEntry.prototype.toURL = function() {
|
2018-02-15 23:25:38 +01:00
|
|
|
if ( this.warURL !== undefined ) {
|
|
|
|
return this.warURL + '?secret=' + vAPI.warSecret;
|
|
|
|
}
|
2015-12-23 17:02:36 +01:00
|
|
|
if ( this.data.startsWith('data:') === false ) {
|
|
|
|
if ( this.mime.indexOf(';') === -1 ) {
|
|
|
|
this.data = 'data:' + this.mime + ';base64,' + btoa(this.data);
|
|
|
|
} else {
|
|
|
|
this.data = 'data:' + this.mime + ',' + this.data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this.data;
|
|
|
|
};
|
2015-11-26 23:56:30 +01:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-12-23 17:02:36 +01:00
|
|
|
RedirectEntry.prototype.toContent = function() {
|
|
|
|
if ( this.data.startsWith('data:') ) {
|
|
|
|
var pos = this.data.indexOf(',');
|
|
|
|
var base64 = this.data.endsWith(';base64', pos);
|
|
|
|
this.data = this.data.slice(pos + 1);
|
|
|
|
if ( base64 ) {
|
|
|
|
this.data = atob(this.data);
|
|
|
|
}
|
2015-11-26 23:56:30 +01:00
|
|
|
}
|
2015-12-23 17:02:36 +01:00
|
|
|
return this.data;
|
2015-11-29 17:04:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEntry.fromFields = function(mime, lines) {
|
|
|
|
var r = new RedirectEntry();
|
|
|
|
r.mime = mime;
|
2015-12-23 17:02:36 +01:00
|
|
|
r.data = lines.join(mime.indexOf(';') !== -1 ? '' : '\n');
|
2015-11-29 17:04:42 +01:00
|
|
|
return r;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEntry.fromSelfie = function(selfie) {
|
|
|
|
var r = new RedirectEntry();
|
|
|
|
r.mime = selfie.mime;
|
|
|
|
r.data = selfie.data;
|
2018-02-15 23:25:38 +01:00
|
|
|
r.warURL = selfie.warURL;
|
2015-11-29 17:04:42 +01:00
|
|
|
return r;
|
2015-11-26 23:56:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
2015-11-23 13:52:50 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var RedirectEngine = function() {
|
2016-10-10 15:01:05 +02:00
|
|
|
this.resources = new Map();
|
2015-11-23 13:52:50 +01:00
|
|
|
this.reset();
|
2016-01-07 23:30:56 +01:00
|
|
|
this.resourceNameRegister = '';
|
2016-10-10 15:01:05 +02:00
|
|
|
this._desAll = []; // re-use better than re-allocate
|
2015-11-23 13:52:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEngine.prototype.reset = function() {
|
2016-10-10 15:01:05 +02:00
|
|
|
this.rules = new Map();
|
|
|
|
this.ruleTypes = new Set();
|
|
|
|
this.ruleSources = new Set();
|
|
|
|
this.ruleDestinations = new Set();
|
2017-12-21 23:05:25 +01:00
|
|
|
this.modifyTime = Date.now();
|
2015-11-23 13:52:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-24 01:18:25 +01:00
|
|
|
RedirectEngine.prototype.freeze = function() {
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-10-10 15:01:05 +02:00
|
|
|
RedirectEngine.prototype.toBroaderHostname = function(hostname) {
|
|
|
|
var pos = hostname.indexOf('.');
|
|
|
|
if ( pos !== -1 ) {
|
|
|
|
return hostname.slice(pos + 1);
|
|
|
|
}
|
|
|
|
return hostname !== '*' ? '*' : '';
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-23 13:52:50 +01:00
|
|
|
RedirectEngine.prototype.lookup = function(context) {
|
2016-10-10 15:01:05 +02:00
|
|
|
var type = context.requestType;
|
|
|
|
if ( this.ruleTypes.has(type) === false ) { return; }
|
|
|
|
var src = context.pageHostname,
|
|
|
|
des = context.requestHostname,
|
|
|
|
desAll = this._desAll,
|
|
|
|
reqURL = context.requestURL;
|
|
|
|
var n = 0;
|
|
|
|
for (;;) {
|
|
|
|
if ( this.ruleDestinations.has(des) ) {
|
|
|
|
desAll[n] = des; n += 1;
|
|
|
|
}
|
|
|
|
des = this.toBroaderHostname(des);
|
|
|
|
if ( des === '' ) { break; }
|
2015-11-23 13:52:50 +01:00
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
if ( n === 0 ) { return; }
|
|
|
|
var entries;
|
2015-11-23 13:52:50 +01:00
|
|
|
for (;;) {
|
2016-10-10 15:01:05 +02:00
|
|
|
if ( this.ruleSources.has(src) ) {
|
|
|
|
for ( var i = 0; i < n; i++ ) {
|
|
|
|
entries = this.rules.get(src + ' ' + desAll[i] + ' ' + type);
|
|
|
|
if ( entries && this.lookupToken(entries, reqURL) ) {
|
|
|
|
return this.resourceNameRegister;
|
2015-11-23 15:49:50 +01:00
|
|
|
}
|
2015-11-23 13:52:50 +01:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
src = this.toBroaderHostname(src);
|
|
|
|
if ( src === '' ) { break; }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
RedirectEngine.prototype.lookupToken = function(entries, reqURL) {
|
|
|
|
var j = entries.length, entry;
|
|
|
|
while ( j-- ) {
|
|
|
|
entry = entries[j];
|
|
|
|
if ( entry.pat instanceof RegExp === false ) {
|
|
|
|
entry.pat = new RegExp(entry.pat, 'i');
|
|
|
|
}
|
|
|
|
if ( entry.pat.test(reqURL) ) {
|
|
|
|
this.resourceNameRegister = entry.tok;
|
|
|
|
return true;
|
2015-11-23 13:52:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-26 23:56:30 +01:00
|
|
|
RedirectEngine.prototype.toURL = function(context) {
|
|
|
|
var token = this.lookup(context);
|
|
|
|
if ( token === undefined ) {
|
|
|
|
return;
|
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
var entry = this.resources.get(token);
|
2015-11-26 23:56:30 +01:00
|
|
|
if ( entry !== undefined ) {
|
2015-12-23 17:02:36 +01:00
|
|
|
return entry.toURL();
|
2015-11-26 23:56:30 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEngine.prototype.matches = function(context) {
|
|
|
|
var token = this.lookup(context);
|
2016-10-10 15:01:05 +02:00
|
|
|
return token !== undefined && this.resources.has(token);
|
2015-11-26 23:56:30 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-24 01:18:25 +01:00
|
|
|
RedirectEngine.prototype.addRule = function(src, des, type, pattern, redirect) {
|
2016-10-10 15:01:05 +02:00
|
|
|
this.ruleSources.add(src);
|
|
|
|
this.ruleDestinations.add(des);
|
|
|
|
this.ruleTypes.add(type);
|
|
|
|
var key = src + ' ' + des + ' ' + type,
|
|
|
|
entries = this.rules.get(key);
|
|
|
|
if ( entries === undefined ) {
|
|
|
|
this.rules.set(key, [ { tok: redirect, pat: pattern } ]);
|
2017-12-21 23:05:25 +01:00
|
|
|
this.modifyTime = Date.now();
|
2016-10-10 15:01:05 +02:00
|
|
|
return;
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
var entry;
|
|
|
|
for ( var i = 0, n = entries.length; i < n; i++ ) {
|
|
|
|
entry = entries[i];
|
|
|
|
if ( redirect === entry.tok ) { break; }
|
2016-01-22 00:30:06 +01:00
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
if ( i === n ) {
|
|
|
|
entries.push({ tok: redirect, pat: pattern });
|
2016-01-22 00:30:06 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
var p = entry.pat;
|
2016-01-22 00:30:06 +01:00
|
|
|
if ( p instanceof RegExp ) {
|
|
|
|
p = p.source;
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
2016-01-22 00:30:06 +01:00
|
|
|
// Duplicate?
|
|
|
|
var pos = p.indexOf(pattern);
|
|
|
|
if ( pos !== -1 ) {
|
|
|
|
if ( pos === 0 || p.charAt(pos - 1) === '|' ) {
|
|
|
|
pos += pattern.length;
|
2016-10-10 15:01:05 +02:00
|
|
|
if ( pos === p.length || p.charAt(pos) === '|' ) { return; }
|
2016-01-22 00:30:06 +01:00
|
|
|
}
|
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
entry.pat = p + '|' + pattern;
|
2015-11-24 01:18:25 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEngine.prototype.fromCompiledRule = function(line) {
|
|
|
|
var fields = line.split('\t');
|
|
|
|
if ( fields.length !== 5 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
this.addRule(fields[0], fields[1], fields[2], fields[3], fields[4]);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEngine.prototype.compileRuleFromStaticFilter = function(line) {
|
|
|
|
var matches = this.reFilterParser.exec(line);
|
|
|
|
if ( matches === null || matches.length !== 4 ) {
|
2015-11-25 16:05:23 +01:00
|
|
|
return;
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
2016-10-26 14:59:15 +02:00
|
|
|
var µburi = µBlock.URI,
|
|
|
|
des = matches[1] || '',
|
|
|
|
pattern = (des + matches[2]).replace(/[.+?{}()|[\]\/\\]/g, '\\$&')
|
2015-12-24 00:36:57 +01:00
|
|
|
.replace(/\^/g, '[^\\w\\d%-]')
|
2016-10-26 14:59:15 +02:00
|
|
|
.replace(/\*/g, '.*?'),
|
|
|
|
type,
|
|
|
|
redirect = '',
|
|
|
|
srcs = [],
|
|
|
|
options = matches[3].split(','), option;
|
2015-11-24 01:18:25 +01:00
|
|
|
while ( (option = options.pop()) ) {
|
2015-12-15 16:40:40 +01:00
|
|
|
if ( option.startsWith('redirect=') ) {
|
2015-11-24 01:18:25 +01:00
|
|
|
redirect = option.slice(9);
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-15 16:40:40 +01:00
|
|
|
if ( option.startsWith('domain=') ) {
|
2015-11-24 01:18:25 +01:00
|
|
|
srcs = option.slice(7).split('|');
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-16 03:34:36 +01:00
|
|
|
if ( option === 'first-party' ) {
|
2016-10-26 14:59:15 +02:00
|
|
|
srcs.push(µburi.domainFromHostname(des) || des);
|
2015-12-16 03:34:36 +01:00
|
|
|
continue;
|
|
|
|
}
|
2015-11-25 16:05:23 +01:00
|
|
|
// One and only one type must be specified.
|
2015-11-24 01:18:25 +01:00
|
|
|
if ( option in this.supportedTypes ) {
|
2015-11-25 16:05:23 +01:00
|
|
|
if ( type !== undefined ) {
|
|
|
|
return;
|
|
|
|
}
|
2015-11-26 17:13:33 +01:00
|
|
|
type = this.supportedTypes[option];
|
2015-11-24 01:18:25 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-24 05:34:03 +01:00
|
|
|
// Need a resource token.
|
|
|
|
if ( redirect === '' ) {
|
2015-11-25 16:05:23 +01:00
|
|
|
return;
|
2015-11-24 05:34:03 +01:00
|
|
|
}
|
|
|
|
|
2015-11-25 16:05:23 +01:00
|
|
|
// Need one single type -- not negated.
|
2015-12-15 16:40:40 +01:00
|
|
|
if ( type === undefined || type.startsWith('~') ) {
|
2015-11-25 16:05:23 +01:00
|
|
|
return;
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( des === '' ) {
|
|
|
|
des = '*';
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( srcs.length === 0 ) {
|
|
|
|
srcs.push('*');
|
|
|
|
}
|
|
|
|
|
|
|
|
var out = [];
|
2015-11-24 05:34:03 +01:00
|
|
|
var i = srcs.length, src;
|
2015-11-24 01:18:25 +01:00
|
|
|
while ( i-- ) {
|
2015-11-24 05:34:03 +01:00
|
|
|
src = srcs[i];
|
2015-11-25 16:05:23 +01:00
|
|
|
if ( src === '' ) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-12-15 16:40:40 +01:00
|
|
|
if ( src.startsWith('~') ) {
|
2015-11-24 05:34:03 +01:00
|
|
|
continue;
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
2015-11-25 16:05:23 +01:00
|
|
|
out.push(src + '\t' + des + '\t' + type + '\t' + pattern + '\t' + redirect);
|
2015-11-24 01:18:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return out;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2016-04-27 16:08:32 +02:00
|
|
|
RedirectEngine.prototype.reFilterParser = /^(?:\|\|([^\/:?#^*]+)|\*)([^$]+)\$([^$]+)$/;
|
2015-11-24 01:18:25 +01:00
|
|
|
|
|
|
|
RedirectEngine.prototype.supportedTypes = (function() {
|
|
|
|
var types = Object.create(null);
|
2016-07-14 19:31:00 +02:00
|
|
|
types.font = 'font';
|
2015-11-24 01:18:25 +01:00
|
|
|
types.image = 'image';
|
2016-07-14 19:31:00 +02:00
|
|
|
types.media = 'media';
|
2015-11-24 01:18:25 +01:00
|
|
|
types.object = 'object';
|
|
|
|
types.script = 'script';
|
2016-07-14 19:31:00 +02:00
|
|
|
types.stylesheet = 'stylesheet';
|
2015-11-24 01:18:25 +01:00
|
|
|
types.subdocument = 'sub_frame';
|
2016-07-14 19:31:00 +02:00
|
|
|
types.xmlhttprequest = 'xmlhttprequest';
|
2015-11-24 01:18:25 +01:00
|
|
|
return types;
|
|
|
|
})();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-29 17:04:42 +01:00
|
|
|
RedirectEngine.prototype.toSelfie = function() {
|
2016-10-10 15:01:05 +02:00
|
|
|
// Because rules may contains RegExp instances, we need to manually
|
|
|
|
// convert it to a serializable format. The serialized format must be
|
|
|
|
// suitable to be used as an argument to the Map() constructor.
|
|
|
|
var rules = [],
|
2017-05-19 16:12:55 +02:00
|
|
|
rule, entries, i, entry;
|
|
|
|
for ( var item of this.rules ) {
|
|
|
|
rule = [ item[0], [] ];
|
|
|
|
entries = item[1];
|
2016-10-10 15:01:05 +02:00
|
|
|
i = entries.length;
|
|
|
|
while ( i-- ) {
|
|
|
|
entry = entries[i];
|
|
|
|
rule[1].push({
|
|
|
|
tok: entry.tok,
|
|
|
|
pat: entry.pat instanceof RegExp ? entry.pat.source : entry.pat
|
|
|
|
});
|
2015-11-29 23:06:58 +01:00
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
rules.push(rule);
|
2015-11-29 23:06:58 +01:00
|
|
|
}
|
2016-10-10 15:01:05 +02:00
|
|
|
var µb = µBlock;
|
|
|
|
return {
|
|
|
|
rules: rules,
|
2017-10-21 19:43:46 +02:00
|
|
|
ruleTypes: µb.arrayFrom(this.ruleTypes),
|
|
|
|
ruleSources: µb.arrayFrom(this.ruleSources),
|
|
|
|
ruleDestinations: µb.arrayFrom(this.ruleDestinations)
|
2016-10-10 15:01:05 +02:00
|
|
|
};
|
2015-11-29 17:04:42 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
RedirectEngine.prototype.fromSelfie = function(selfie) {
|
2017-10-21 19:43:46 +02:00
|
|
|
this.rules = new Map(selfie.rules);
|
|
|
|
this.ruleTypes = new Set(selfie.ruleTypes);
|
|
|
|
this.ruleSources = new Set(selfie.ruleSources);
|
|
|
|
this.ruleDestinations = new Set(selfie.ruleDestinations);
|
2017-12-21 23:05:25 +01:00
|
|
|
this.modifyTime = Date.now();
|
2015-11-29 17:04:42 +01:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-12-23 17:02:36 +01:00
|
|
|
RedirectEngine.prototype.resourceURIFromName = function(name, mime) {
|
2016-10-10 15:01:05 +02:00
|
|
|
var entry = this.resources.get(name);
|
2015-12-22 22:32:09 +01:00
|
|
|
if ( entry && (mime === undefined || entry.mime.startsWith(mime)) ) {
|
|
|
|
return entry.toURL();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-12-23 17:02:36 +01:00
|
|
|
RedirectEngine.prototype.resourceContentFromName = function(name, mime) {
|
2016-10-10 15:01:05 +02:00
|
|
|
var entry;
|
|
|
|
for (;;) {
|
|
|
|
entry = this.resources.get(name);
|
|
|
|
if ( entry === undefined ) { return; }
|
|
|
|
if ( entry.mime.startsWith('alias/') === false ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
name = entry.mime.slice(6);
|
|
|
|
}
|
|
|
|
if ( mime === undefined || entry.mime.startsWith(mime) ) {
|
2015-12-23 17:02:36 +01:00
|
|
|
return entry.toContent();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2015-11-24 01:18:25 +01:00
|
|
|
// TODO: combine same key-redirect pairs into a single regex.
|
|
|
|
|
2015-11-24 05:34:03 +01:00
|
|
|
RedirectEngine.prototype.resourcesFromString = function(text) {
|
2017-01-18 19:17:47 +01:00
|
|
|
var line, fields, encoded,
|
|
|
|
reNonEmptyLine = /\S/,
|
|
|
|
lineIter = new µBlock.LineIterator(text);
|
2015-11-24 19:21:14 +01:00
|
|
|
|
2016-10-10 15:01:05 +02:00
|
|
|
this.resources = new Map();
|
2015-11-23 13:52:50 +01:00
|
|
|
|
2017-01-18 19:17:47 +01:00
|
|
|
while ( lineIter.eot() === false ) {
|
|
|
|
line = lineIter.next();
|
|
|
|
if ( line.startsWith('#') ) { continue; }
|
2015-11-23 13:52:50 +01:00
|
|
|
|
2015-11-24 19:21:14 +01:00
|
|
|
if ( fields === undefined ) {
|
2015-11-26 01:38:05 +01:00
|
|
|
fields = line.trim().split(/\s+/);
|
|
|
|
if ( fields.length === 2 ) {
|
|
|
|
encoded = fields[1].indexOf(';') !== -1;
|
|
|
|
} else {
|
2015-11-24 19:21:14 +01:00
|
|
|
fields = undefined;
|
2015-11-23 13:52:50 +01:00
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-11-26 01:38:05 +01:00
|
|
|
if ( reNonEmptyLine.test(line) ) {
|
|
|
|
fields.push(encoded ? line.trim() : line);
|
2015-11-23 13:52:50 +01:00
|
|
|
continue;
|
|
|
|
}
|
2015-11-24 05:34:03 +01:00
|
|
|
|
2015-11-24 19:21:14 +01:00
|
|
|
// No more data, add the resource.
|
2016-10-10 15:01:05 +02:00
|
|
|
this.resources.set(fields[0], RedirectEntry.fromFields(fields[1], fields.slice(2)));
|
2018-02-15 23:25:38 +01:00
|
|
|
warResolve(fields[0]);
|
2015-11-24 19:21:14 +01:00
|
|
|
|
|
|
|
fields = undefined;
|
|
|
|
}
|
2015-11-24 05:34:03 +01:00
|
|
|
|
2015-11-24 19:21:14 +01:00
|
|
|
// Process pending resource data.
|
|
|
|
if ( fields !== undefined ) {
|
2016-10-10 15:01:05 +02:00
|
|
|
this.resources.set(fields[0], RedirectEntry.fromFields(fields[1], fields.slice(2)));
|
2018-02-15 23:25:38 +01:00
|
|
|
warResolve(fields[0]);
|
2015-11-23 13:52:50 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-02-15 23:25:38 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
var resourcesSelfieVersion = 1;
|
|
|
|
|
|
|
|
RedirectEngine.prototype.selfieFromResources = function() {
|
|
|
|
vAPI.cacheStorage.set({
|
|
|
|
resourcesSelfie: {
|
|
|
|
version: resourcesSelfieVersion,
|
|
|
|
resources: µBlock.arrayFrom(this.resources)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
RedirectEngine.prototype.resourcesFromSelfie = function(callback) {
|
|
|
|
var me = this;
|
|
|
|
|
|
|
|
var onSelfieReady = function(bin) {
|
|
|
|
if ( bin instanceof Object === false ) {
|
|
|
|
return callback(false);
|
|
|
|
}
|
|
|
|
var selfie = bin.resourcesSelfie;
|
|
|
|
if (
|
|
|
|
selfie instanceof Object === false ||
|
|
|
|
selfie.version !== resourcesSelfieVersion ||
|
|
|
|
Array.isArray(selfie.resources) === false
|
|
|
|
) {
|
|
|
|
return callback(false);
|
|
|
|
}
|
|
|
|
me.resources = new Map();
|
|
|
|
for ( var entry of bin.resourcesSelfie.resources ) {
|
|
|
|
me.resources.set(entry[0], RedirectEntry.fromSelfie(entry[1]));
|
|
|
|
}
|
|
|
|
callback(true);
|
|
|
|
};
|
|
|
|
|
|
|
|
vAPI.cacheStorage.get('resourcesSelfie', onSelfieReady);
|
|
|
|
};
|
|
|
|
|
|
|
|
RedirectEngine.prototype.invalidateResourcesSelfie = function() {
|
|
|
|
vAPI.cacheStorage.remove('resourcesSelfie');
|
|
|
|
};
|
|
|
|
|
2015-11-26 23:56:30 +01:00
|
|
|
/******************************************************************************/
|
2015-11-23 13:52:50 +01:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
return new RedirectEngine();
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
})();
|