2021-07-25 16:55:35 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
uBlock Origin - a browser extension to block requests.
|
|
|
|
Copyright (C) 2014-present 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
|
|
|
|
*/
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2021-07-28 16:38:24 +02:00
|
|
|
import { createRequire } from 'module';
|
|
|
|
|
2021-08-02 18:32:47 +02:00
|
|
|
import { readFileSync } from 'fs';
|
|
|
|
import { dirname, resolve } from 'path';
|
|
|
|
import { fileURLToPath } from 'url';
|
|
|
|
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
|
2021-08-01 21:42:46 +02:00
|
|
|
import './lib/punycode.js';
|
2021-07-25 16:55:35 +02:00
|
|
|
import './lib/publicsuffixlist/publicsuffixlist.js';
|
|
|
|
|
|
|
|
import globals from './js/globals.js';
|
2021-07-29 01:48:38 +02:00
|
|
|
import snfe from './js/static-net-filtering.js';
|
2021-07-25 16:55:35 +02:00
|
|
|
import { FilteringContext } from './js/filtering-context.js';
|
2021-07-31 14:38:33 +02:00
|
|
|
import { LineIterator } from './js/text-utils.js';
|
2021-07-25 16:55:35 +02:00
|
|
|
import { StaticFilteringParser } from './js/static-filtering-parser.js';
|
|
|
|
|
|
|
|
import {
|
|
|
|
CompiledListReader,
|
|
|
|
CompiledListWriter,
|
|
|
|
} from './js/static-filtering-io.js';
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2021-08-02 18:32:47 +02:00
|
|
|
function loadJSON(path) {
|
|
|
|
return JSON.parse(readFileSync(resolve(__dirname, path), 'utf8'));
|
|
|
|
}
|
|
|
|
|
2021-08-05 19:30:20 +02:00
|
|
|
function compileList(list, compiler, writer, options = {}) {
|
|
|
|
const lineIter = new LineIterator(list.raw);
|
2021-07-29 01:48:38 +02:00
|
|
|
const events = Array.isArray(options.events) ? options.events : undefined;
|
2021-07-25 16:55:35 +02:00
|
|
|
|
2021-08-05 19:30:20 +02:00
|
|
|
if ( list.name ) {
|
|
|
|
writer.properties.set('name', list.name);
|
|
|
|
}
|
|
|
|
|
|
|
|
const { parser } = compiler;
|
2021-07-25 16:55:35 +02:00
|
|
|
|
|
|
|
while ( lineIter.eot() === false ) {
|
|
|
|
let line = lineIter.next();
|
|
|
|
while ( line.endsWith(' \\') ) {
|
|
|
|
if ( lineIter.peek(4) !== ' ' ) { break; }
|
|
|
|
line = line.slice(0, -2).trim() + lineIter.next().trim();
|
|
|
|
}
|
|
|
|
parser.analyze(line);
|
|
|
|
if ( parser.shouldIgnore() ) { continue; }
|
|
|
|
if ( parser.category !== parser.CATStaticNetFilter ) { continue; }
|
|
|
|
if ( parser.patternHasUnicode() && parser.toASCII() === false ) {
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-05 19:30:20 +02:00
|
|
|
if ( compiler.compile(writer) ) { continue; }
|
2021-08-04 21:14:48 +02:00
|
|
|
if ( compiler.error !== undefined && events !== undefined ) {
|
2021-07-29 01:48:38 +02:00
|
|
|
options.events.push({
|
2021-07-25 16:55:35 +02:00
|
|
|
type: 'error',
|
2021-08-04 21:14:48 +02:00
|
|
|
text: compiler.error
|
2021-07-29 01:48:38 +02:00
|
|
|
});
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-29 22:54:51 +02:00
|
|
|
async function enableWASM() {
|
2021-07-29 23:04:32 +02:00
|
|
|
const wasmModuleFetcher = function(path) {
|
2021-07-31 13:06:11 +02:00
|
|
|
const require = createRequire(import.meta.url); // jshint ignore:line
|
|
|
|
const wasm = new Uint8Array(require(`${path}.wasm.json`));
|
|
|
|
return globals.WebAssembly.compile(wasm);
|
2021-07-29 22:54:51 +02:00
|
|
|
};
|
|
|
|
try {
|
|
|
|
const results = await Promise.all([
|
2021-07-31 13:06:11 +02:00
|
|
|
globals.publicSuffixList.enableWASM(wasmModuleFetcher, './lib/publicsuffixlist/wasm/'),
|
|
|
|
snfe.enableWASM(wasmModuleFetcher, './js/wasm/'),
|
2021-07-29 22:54:51 +02:00
|
|
|
]);
|
|
|
|
return results.every(a => a === true);
|
|
|
|
} catch(reason) {
|
2021-07-31 13:06:11 +02:00
|
|
|
console.log(reason);
|
2021-07-29 22:54:51 +02:00
|
|
|
}
|
|
|
|
return false;
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
|
2021-08-01 21:42:46 +02:00
|
|
|
function pslInit(raw) {
|
|
|
|
if ( typeof raw !== 'string' || raw.trim() === '' ) {
|
|
|
|
const require = createRequire(import.meta.url); // jshint ignore:line
|
2021-08-01 22:42:05 +02:00
|
|
|
|
|
|
|
let serialized = null;
|
|
|
|
|
|
|
|
// Use serialized version if available
|
|
|
|
try {
|
2021-08-02 18:32:47 +02:00
|
|
|
// Use loadJSON() because require() would keep the string in memory.
|
|
|
|
serialized = loadJSON('build/publicsuffixlist.json');
|
2021-08-01 22:42:05 +02:00
|
|
|
} catch (error) {
|
2021-08-03 18:03:11 +02:00
|
|
|
if ( process.env.npm_lifecycle_event !== 'install' ) {
|
2021-08-02 18:32:47 +02:00
|
|
|
// This should never happen except during package installation.
|
|
|
|
console.error(error);
|
|
|
|
}
|
2021-08-01 22:42:05 +02:00
|
|
|
}
|
|
|
|
|
2021-08-03 18:03:11 +02:00
|
|
|
if ( serialized !== null ) {
|
2021-08-01 22:42:05 +02:00
|
|
|
globals.publicSuffixList.fromSelfie(serialized);
|
|
|
|
return globals.publicSuffixList;
|
|
|
|
}
|
|
|
|
|
2021-08-01 21:42:46 +02:00
|
|
|
raw = require('./data/effective_tld_names.json');
|
|
|
|
if ( typeof raw !== 'string' || raw.trim() === '' ) {
|
|
|
|
console.error('Unable to populate public suffix list');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
globals.publicSuffixList.parse(raw, globals.punycode.toASCII);
|
2021-07-31 13:09:01 +02:00
|
|
|
return globals.publicSuffixList;
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
|
2021-08-05 19:37:41 +02:00
|
|
|
function createCompiler(parser) {
|
|
|
|
return snfe.createCompiler(parser);
|
|
|
|
}
|
|
|
|
|
2021-08-05 19:30:20 +02:00
|
|
|
async function useCompiledLists(lists) {
|
2021-07-25 16:55:35 +02:00
|
|
|
// Remove all filters
|
|
|
|
reset();
|
|
|
|
|
2021-08-05 19:30:20 +02:00
|
|
|
if ( Array.isArray(lists) === false || lists.length === 0 ) {
|
|
|
|
return snfe;
|
|
|
|
}
|
|
|
|
|
|
|
|
const consumeList = list => {
|
|
|
|
snfe.fromCompiled(new CompiledListReader(list.compiled));
|
|
|
|
};
|
|
|
|
|
|
|
|
// Populate filtering engine with filter lists
|
|
|
|
const promises = [];
|
|
|
|
for ( const list of lists ) {
|
|
|
|
const promise = list instanceof Promise ? list : Promise.resolve(list);
|
|
|
|
promises.push(promise.then(list => consumeList(list)));
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
|
2021-08-05 19:30:20 +02:00
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
// Commit changes
|
|
|
|
snfe.freeze();
|
|
|
|
snfe.optimize();
|
|
|
|
|
|
|
|
return snfe;
|
|
|
|
}
|
|
|
|
|
|
|
|
async function useRawLists(lists, options = {}) {
|
|
|
|
// Remove all filters
|
|
|
|
reset();
|
|
|
|
|
|
|
|
if ( Array.isArray(lists) === false || lists.length === 0 ) {
|
|
|
|
return snfe;
|
|
|
|
}
|
|
|
|
|
2021-08-05 19:37:41 +02:00
|
|
|
const compiler = createCompiler(new StaticFilteringParser());
|
2021-08-05 19:30:20 +02:00
|
|
|
|
|
|
|
const consumeList = list => {
|
|
|
|
const writer = new CompiledListWriter();
|
|
|
|
compileList(list, compiler, writer, options);
|
|
|
|
snfe.fromCompiled(new CompiledListReader(writer.toString()));
|
|
|
|
};
|
|
|
|
|
|
|
|
// Populate filtering engine with filter lists
|
|
|
|
const promises = [];
|
|
|
|
for ( const list of lists ) {
|
|
|
|
const promise = list instanceof Promise ? list : Promise.resolve(list);
|
|
|
|
promises.push(promise.then(list => consumeList(list)));
|
|
|
|
}
|
|
|
|
|
|
|
|
await Promise.all(promises);
|
|
|
|
|
|
|
|
// Commit changes
|
|
|
|
snfe.freeze();
|
|
|
|
snfe.optimize();
|
|
|
|
|
2021-07-29 01:48:38 +02:00
|
|
|
return snfe;
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function reset() {
|
2021-07-29 01:48:38 +02:00
|
|
|
snfe.reset();
|
2021-07-25 16:55:35 +02:00
|
|
|
}
|
|
|
|
|
2021-08-08 13:22:02 +02:00
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
let pslInitialized = false;
|
|
|
|
let staticNetFilteringEngineInstance = null;
|
|
|
|
|
|
|
|
class StaticNetFilteringEngine {
|
|
|
|
constructor() {
|
|
|
|
if ( staticNetFilteringEngineInstance !== null ) {
|
|
|
|
throw new Error('Only a single instance is supported.');
|
|
|
|
}
|
|
|
|
|
|
|
|
staticNetFilteringEngineInstance = this;
|
|
|
|
|
|
|
|
this._context = new FilteringContext();
|
|
|
|
}
|
|
|
|
|
|
|
|
async useLists(lists) {
|
|
|
|
await useRawLists(lists);
|
|
|
|
}
|
|
|
|
|
|
|
|
matchRequest({ url, originURL, type }) {
|
|
|
|
this._context.setDocOriginFromURL(originURL);
|
|
|
|
this._context.setURL(url);
|
|
|
|
this._context.setType(type);
|
|
|
|
|
|
|
|
return snfe.matchRequest(this._context);
|
|
|
|
}
|
|
|
|
|
|
|
|
toLogData() {
|
|
|
|
return snfe.toLogData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
StaticNetFilteringEngine.initialize = async function initialize() {
|
|
|
|
if ( !pslInitialized ) {
|
|
|
|
if ( !pslInit() ) {
|
|
|
|
throw new Error('Failed to initialize public suffix list.');
|
|
|
|
}
|
|
|
|
|
|
|
|
pslInitialized = true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2021-08-02 00:33:13 +02:00
|
|
|
// rollup.js needs module.exports to be set back to the local exports object.
|
|
|
|
// This is because some of the code (e.g. publicsuffixlist.js) sets
|
|
|
|
// module.exports. Once all included files are written like ES modules, using
|
|
|
|
// export statements, this should no longer be necessary.
|
2021-08-03 18:03:11 +02:00
|
|
|
if ( typeof module !== 'undefined' && typeof exports !== 'undefined' ) {
|
2021-08-03 16:14:40 +02:00
|
|
|
module.exports = exports;
|
2021-08-02 00:33:13 +02:00
|
|
|
}
|
|
|
|
|
2021-07-25 16:55:35 +02:00
|
|
|
export {
|
|
|
|
FilteringContext,
|
2021-08-08 13:22:02 +02:00
|
|
|
StaticNetFilteringEngine,
|
2021-07-25 16:55:35 +02:00
|
|
|
enableWASM,
|
|
|
|
pslInit,
|
2021-08-05 19:37:41 +02:00
|
|
|
createCompiler,
|
2021-08-05 19:30:20 +02:00
|
|
|
useCompiledLists,
|
|
|
|
useRawLists,
|
2021-07-25 16:55:35 +02:00
|
|
|
};
|