2014-06-24 00:42:43 +02:00
|
|
|
/*******************************************************************************
|
|
|
|
|
|
|
|
µBlock - a Chromium browser extension to block requests.
|
|
|
|
Copyright (C) 2014 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
|
|
|
|
*/
|
|
|
|
|
2014-11-17 00:27:48 +01:00
|
|
|
/* global µBlock, vAPI, punycode, publicSuffixList */
|
|
|
|
|
2014-10-19 13:11:27 +02:00
|
|
|
'use strict';
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.getBytesInUse = function() {
|
|
|
|
var getBytesInUseHandler = function(bytesInUse) {
|
|
|
|
µBlock.storageUsed = bytesInUse;
|
|
|
|
};
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.getBytesInUse(null, getBytesInUseHandler);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-10-07 14:59:35 +02:00
|
|
|
µBlock.saveLocalSettings = function(callback) {
|
|
|
|
if ( typeof callback !== 'function' ) {
|
|
|
|
callback = this.noopFunc;
|
|
|
|
}
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.set(this.localSettings, callback);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.loadLocalSettings = function() {
|
|
|
|
var settingsLoaded = function(store) {
|
|
|
|
µBlock.localSettings = store;
|
|
|
|
};
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.get(this.localSettings, settingsLoaded);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// Save local settings regularly. Not critical.
|
|
|
|
|
|
|
|
µBlock.asyncJobs.add(
|
|
|
|
'autoSaveLocalSettings',
|
|
|
|
null,
|
|
|
|
µBlock.saveLocalSettings.bind(µBlock),
|
|
|
|
2 * 60 * 1000,
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.saveUserSettings = function() {
|
2015-01-22 03:46:11 +01:00
|
|
|
vAPI.storage.set(this.userSettings);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-08-20 02:41:52 +02:00
|
|
|
µBlock.loadUserSettings = function(callback) {
|
2014-06-24 00:42:43 +02:00
|
|
|
var settingsLoaded = function(store) {
|
2014-08-25 02:52:34 +02:00
|
|
|
µBlock.userSettings = store;
|
|
|
|
if ( typeof callback === 'function' ) {
|
2015-01-07 14:49:52 +01:00
|
|
|
callback(µBlock.userSettings);
|
2014-08-25 02:52:34 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.get(this.userSettings, settingsLoaded);
|
2014-08-25 02:52:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-12-31 23:26:17 +01:00
|
|
|
µBlock.saveDynamicRules = function() {
|
|
|
|
this.userSettings.dynamicFilteringString = this.dynamicNetFilteringEngine.toString();
|
|
|
|
this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-08-25 02:52:34 +02:00
|
|
|
µBlock.saveWhitelist = function() {
|
|
|
|
var bin = {
|
|
|
|
'netWhitelist': this.stringFromWhitelist(this.netWhitelist)
|
|
|
|
};
|
2015-01-22 03:46:11 +01:00
|
|
|
vAPI.storage.set(bin);
|
2014-08-25 02:52:34 +02:00
|
|
|
this.netWhitelistModifyTime = Date.now();
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.loadWhitelist = function(callback) {
|
|
|
|
var onWhitelistLoaded = function(store) {
|
2014-08-02 17:40:27 +02:00
|
|
|
var µb = µBlock;
|
|
|
|
// Backward compatibility after fix to #5
|
|
|
|
// TODO: remove once all users are up to date with latest version.
|
|
|
|
if ( store.netExceptionList ) {
|
|
|
|
if ( store.netWhitelist === '' ) {
|
|
|
|
store.netWhitelist = Object.keys(store.netExceptionList).join('\n');
|
|
|
|
if ( store.netWhitelist !== '' ) {
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.set({ 'netWhitelist': store.netWhitelist });
|
2014-08-02 17:40:27 +02:00
|
|
|
}
|
|
|
|
}
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.remove('netExceptionList');
|
2014-08-02 17:40:27 +02:00
|
|
|
}
|
|
|
|
µb.netWhitelist = µb.whitelistFromString(store.netWhitelist);
|
|
|
|
µb.netWhitelistModifyTime = Date.now();
|
2014-08-20 02:41:52 +02:00
|
|
|
|
2014-08-25 02:52:34 +02:00
|
|
|
if ( typeof callback === 'function' ) {
|
|
|
|
callback();
|
|
|
|
}
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
2014-08-25 02:52:34 +02:00
|
|
|
var bin = {
|
2015-01-24 18:06:22 +01:00
|
|
|
'netWhitelist': this.netWhitelistDefault,
|
2014-08-25 02:52:34 +02:00
|
|
|
'netExceptionList': ''
|
|
|
|
};
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.get(bin, onWhitelistLoaded);
|
2014-07-17 16:52:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-07-13 02:32:44 +02:00
|
|
|
µBlock.saveUserFilters = function(content, callback) {
|
|
|
|
return this.assets.put(this.userFiltersPath, content, callback);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.loadUserFilters = function(callback) {
|
|
|
|
return this.assets.get(this.userFiltersPath, callback);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.appendUserFilters = function(content) {
|
2014-09-08 23:46:58 +02:00
|
|
|
var µb = this;
|
|
|
|
|
2014-07-13 02:32:44 +02:00
|
|
|
var onSaved = function(details) {
|
|
|
|
if ( details.error ) {
|
|
|
|
return;
|
|
|
|
}
|
2014-09-25 21:44:18 +02:00
|
|
|
µb.mergeFilterText(content);
|
2014-12-28 16:07:43 +01:00
|
|
|
µb.staticNetFilteringEngine.freeze();
|
2014-09-25 21:44:18 +02:00
|
|
|
µb.cosmeticFilteringEngine.freeze();
|
|
|
|
µb.destroySelfie();
|
|
|
|
µb.toSelfieAsync();
|
2014-07-13 02:32:44 +02:00
|
|
|
};
|
2014-09-08 23:46:58 +02:00
|
|
|
|
2014-07-13 02:32:44 +02:00
|
|
|
var onLoaded = function(details) {
|
|
|
|
if ( details.error ) {
|
|
|
|
return;
|
|
|
|
}
|
2014-07-13 18:57:20 +02:00
|
|
|
if ( details.content.indexOf(content.trim()) !== -1 ) {
|
2014-07-13 02:32:44 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.saveUserFilters(details.content + '\n' + content, onSaved);
|
2014-07-13 02:32:44 +02:00
|
|
|
};
|
2014-09-08 23:46:58 +02:00
|
|
|
|
2014-07-13 02:32:44 +02:00
|
|
|
if ( content.length > 0 ) {
|
|
|
|
this.loadUserFilters(onLoaded);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
µBlock.getAvailableLists = function(callback) {
|
|
|
|
var availableLists = {};
|
2014-06-24 00:42:43 +02:00
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
// selected lists
|
|
|
|
var onSelectedListsLoaded = function(store) {
|
2014-08-25 02:52:34 +02:00
|
|
|
var µb = µBlock;
|
2014-07-25 22:12:20 +02:00
|
|
|
var lists = store.remoteBlacklists;
|
|
|
|
var locations = Object.keys(lists);
|
2014-11-17 00:27:48 +01:00
|
|
|
var location, availableEntry, storedEntry;
|
2014-07-25 22:12:20 +02:00
|
|
|
|
2014-11-17 00:27:48 +01:00
|
|
|
while ( location = locations.pop() ) {
|
|
|
|
availableEntry = availableLists[location];
|
2014-08-25 02:52:34 +02:00
|
|
|
if ( availableEntry === undefined ) {
|
2014-07-25 22:12:20 +02:00
|
|
|
continue;
|
|
|
|
}
|
2014-11-17 00:27:48 +01:00
|
|
|
storedEntry = lists[location];
|
2014-08-25 02:52:34 +02:00
|
|
|
availableEntry.off = storedEntry.off || false;
|
2014-11-17 00:27:48 +01:00
|
|
|
µb.assets.setHomeURL(location, availableEntry.homeURL);
|
2014-08-25 02:52:34 +02:00
|
|
|
if ( storedEntry.entryCount !== undefined ) {
|
|
|
|
availableEntry.entryCount = storedEntry.entryCount;
|
|
|
|
}
|
|
|
|
if ( storedEntry.entryUsedCount !== undefined ) {
|
|
|
|
availableEntry.entryUsedCount = storedEntry.entryUsedCount;
|
2014-07-25 22:12:20 +02:00
|
|
|
}
|
2014-09-26 02:21:21 +02:00
|
|
|
// This may happen if the list name was pulled from the list content
|
|
|
|
if ( availableEntry.title === '' && storedEntry.title !== '' ) {
|
|
|
|
availableEntry.title = storedEntry.title;
|
|
|
|
}
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
2014-07-25 22:12:20 +02:00
|
|
|
callback(availableLists);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
// built-in lists
|
|
|
|
var onBuiltinListsLoaded = function(details) {
|
|
|
|
var location, locations;
|
|
|
|
try {
|
|
|
|
locations = JSON.parse(details.content);
|
|
|
|
} catch (e) {
|
|
|
|
locations = {};
|
|
|
|
}
|
2014-12-17 14:02:37 +01:00
|
|
|
var entry;
|
2014-07-25 22:12:20 +02:00
|
|
|
for ( location in locations ) {
|
|
|
|
if ( locations.hasOwnProperty(location) === false ) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-12-17 14:02:37 +01:00
|
|
|
entry = locations[location];
|
|
|
|
// https://github.com/gorhill/uBlock/issues/418
|
|
|
|
// We now support built-in external filter lists
|
|
|
|
if ( /^https?:/.test(location) === false ) {
|
|
|
|
location = 'assets/thirdparties/' + location;
|
|
|
|
}
|
|
|
|
availableLists[location] = entry;
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
2014-07-25 22:12:20 +02:00
|
|
|
|
|
|
|
// Now get user's selection of lists
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.get(
|
2014-07-25 22:12:20 +02:00
|
|
|
{ 'remoteBlacklists': availableLists },
|
2014-10-17 21:44:19 +02:00
|
|
|
onSelectedListsLoaded
|
2014-06-24 00:42:43 +02:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
// permanent lists
|
|
|
|
var location;
|
|
|
|
var lists = this.permanentLists;
|
|
|
|
for ( location in lists ) {
|
|
|
|
if ( lists.hasOwnProperty(location) === false ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
availableLists[location] = lists[location];
|
|
|
|
}
|
|
|
|
|
|
|
|
// custom lists
|
|
|
|
var c;
|
|
|
|
var locations = this.userSettings.externalLists.split('\n');
|
|
|
|
for ( var i = 0; i < locations.length; i++ ) {
|
|
|
|
location = locations[i].trim();
|
|
|
|
c = location.charAt(0);
|
|
|
|
if ( location === '' || c === '!' || c === '#' ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Coarse validation
|
|
|
|
if ( /[^0-9A-Za-z!*'();:@&=+$,\/?%#\[\]_.~-]/.test(location) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
availableLists[location] = {
|
|
|
|
title: '',
|
|
|
|
group: 'custom',
|
|
|
|
external: true
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// get built-in block lists.
|
|
|
|
this.assets.get('assets/ublock/filter-lists.json', onBuiltinListsLoaded);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
µBlock.loadFilterLists = function(callback) {
|
2014-07-25 22:12:20 +02:00
|
|
|
var µb = this;
|
2015-02-06 07:20:04 +01:00
|
|
|
var filterlistCount;
|
2014-07-25 22:12:20 +02:00
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
if ( typeof callback !== 'function' ) {
|
|
|
|
callback = this.noopFunc;
|
|
|
|
}
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
var loadBlacklistsEnd = function() {
|
2014-12-28 16:07:43 +01:00
|
|
|
µb.staticNetFilteringEngine.freeze();
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.cosmeticFilteringEngine.freeze();
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.set({ 'remoteBlacklists': µb.remoteBlacklists });
|
|
|
|
vAPI.messaging.broadcast({ what: 'loadUbiquitousBlacklistCompleted' });
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.toSelfieAsync();
|
|
|
|
callback();
|
2014-07-25 22:12:20 +02:00
|
|
|
};
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
var mergeBlacklist = function(details) {
|
2014-09-25 21:44:18 +02:00
|
|
|
µb.mergeFilterList(details);
|
2015-02-06 07:20:04 +01:00
|
|
|
filterlistCount -= 1;
|
|
|
|
if ( filterlistCount === 0 ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
loadBlacklistsEnd();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
var loadBlacklistsStart = function(lists) {
|
|
|
|
µb.remoteBlacklists = lists;
|
2014-12-28 16:07:43 +01:00
|
|
|
µb.staticNetFilteringEngine.reset();
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.cosmeticFilteringEngine.reset();
|
|
|
|
µb.destroySelfie();
|
2014-07-25 22:12:20 +02:00
|
|
|
var locations = Object.keys(lists);
|
2015-02-06 07:20:04 +01:00
|
|
|
filterlistCount = locations.length;
|
2014-06-24 00:42:43 +02:00
|
|
|
|
2015-02-06 07:20:04 +01:00
|
|
|
// Load all filter lists which are not disabled
|
2014-06-24 00:42:43 +02:00
|
|
|
var location;
|
2015-02-06 12:49:48 +01:00
|
|
|
while ( location = locations.pop() ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
// rhill 2013-12-09:
|
|
|
|
// Ignore list if disabled
|
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/78
|
2015-02-06 12:49:48 +01:00
|
|
|
if ( lists[location].off ) {
|
|
|
|
filterlistCount -= 1;
|
2014-06-24 00:42:43 +02:00
|
|
|
continue;
|
|
|
|
}
|
2015-02-06 12:49:48 +01:00
|
|
|
µb.assets.get(location, mergeBlacklist);
|
2015-02-06 07:20:04 +01:00
|
|
|
}
|
|
|
|
// https://github.com/gorhill/uBlock/issues/695
|
2015-02-06 07:20:43 +01:00
|
|
|
// It may happen not a single filter list is selected
|
2015-02-06 12:49:48 +01:00
|
|
|
if ( filterlistCount === 0 ) {
|
2015-02-06 07:20:04 +01:00
|
|
|
loadBlacklistsEnd();
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-07-25 22:12:20 +02:00
|
|
|
this.getAvailableLists(loadBlacklistsStart);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-09-25 21:44:18 +02:00
|
|
|
µBlock.mergeFilterList = function(details) {
|
|
|
|
// console.log('µBlock > mergeFilterList from "%s": "%s..."', details.path, details.content.slice(0, 40));
|
2014-06-24 00:42:43 +02:00
|
|
|
|
2014-12-28 16:07:43 +01:00
|
|
|
var staticNetFilteringEngine = this.staticNetFilteringEngine;
|
2014-09-25 21:44:18 +02:00
|
|
|
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
|
2014-12-28 16:07:43 +01:00
|
|
|
var duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount;
|
|
|
|
var acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount;
|
2014-06-24 00:42:43 +02:00
|
|
|
|
2014-09-25 21:44:18 +02:00
|
|
|
this.mergeFilterText(details.content);
|
|
|
|
|
|
|
|
// For convenience, store the number of entries for this
|
|
|
|
// blacklist, user might be happy to know this information.
|
2014-12-28 16:07:43 +01:00
|
|
|
duplicateCount = staticNetFilteringEngine.duplicateCount + cosmeticFilteringEngine.duplicateCount - duplicateCount;
|
|
|
|
acceptedCount = staticNetFilteringEngine.acceptedCount + cosmeticFilteringEngine.acceptedCount - acceptedCount;
|
2014-09-25 21:44:18 +02:00
|
|
|
|
2014-09-26 02:21:21 +02:00
|
|
|
var filterListMeta = this.remoteBlacklists[details.path];
|
|
|
|
|
|
|
|
filterListMeta.entryCount = acceptedCount;
|
|
|
|
filterListMeta.entryUsedCount = acceptedCount - duplicateCount;
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
// Try to extract a human-friendly name (works only for
|
2014-09-26 02:21:21 +02:00
|
|
|
// ABP-compatible filter lists)
|
|
|
|
if ( filterListMeta.title === '' ) {
|
|
|
|
var matches = details.content.slice(0, 1024).match(/(?:^|\n)!\s*Title:([^\n]+)/i);
|
|
|
|
if ( matches !== null ) {
|
|
|
|
filterListMeta.title = matches[1].trim();
|
|
|
|
}
|
|
|
|
}
|
2014-09-25 21:44:18 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.mergeFilterText = function(rawText) {
|
|
|
|
var rawEnd = rawText.length;
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
// Useful references:
|
|
|
|
// https://adblockplus.org/en/filter-cheatsheet
|
|
|
|
// https://adblockplus.org/en/filters
|
2014-12-28 16:07:43 +01:00
|
|
|
var staticNetFilteringEngine = this.staticNetFilteringEngine;
|
2014-09-08 23:46:58 +02:00
|
|
|
var cosmeticFilteringEngine = this.cosmeticFilteringEngine;
|
2014-08-21 16:56:36 +02:00
|
|
|
var parseCosmeticFilters = this.userSettings.parseAllABPHideFilters;
|
2014-09-25 21:44:18 +02:00
|
|
|
|
2015-01-23 17:32:49 +01:00
|
|
|
var reIsCosmeticFilter = /#[@#]/;
|
|
|
|
var reIsWhitespaceChar = /\s/;
|
|
|
|
var reMaybeLocalIp = /^[\d:f]/;
|
|
|
|
var reIsLocalhostRedirect = /\s+(?:broadcasthost|local|localhost|localhost\.localdomain)(?=\s|$)/;
|
|
|
|
var reLocalIp = /^(?:0\.0\.0\.0|127\.0\.0\.1|::1|fe80::1%lo0)/;
|
|
|
|
//var reAsciiSegment = /^[\x21-\x7e]+$/;
|
2014-06-24 00:42:43 +02:00
|
|
|
var matches;
|
|
|
|
var lineBeg = 0, lineEnd, currentLineBeg;
|
2015-01-23 17:32:49 +01:00
|
|
|
var line, lineRaw, c, pos;
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
while ( lineBeg < rawEnd ) {
|
|
|
|
lineEnd = rawText.indexOf('\n', lineBeg);
|
2014-09-25 21:44:18 +02:00
|
|
|
if ( lineEnd === -1 ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
lineEnd = rawText.indexOf('\r', lineBeg);
|
2014-09-25 21:44:18 +02:00
|
|
|
if ( lineEnd === -1 ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
lineEnd = rawEnd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
2015-01-23 17:32:49 +01:00
|
|
|
line = lineRaw = rawText.slice(lineBeg, lineEnd).trim();
|
2014-06-24 00:42:43 +02:00
|
|
|
currentLineBeg = lineBeg;
|
|
|
|
lineBeg = lineEnd + 1;
|
|
|
|
|
2015-01-23 17:32:49 +01:00
|
|
|
if ( line.length === 0 ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
// Strip comments
|
|
|
|
c = line.charAt(0);
|
|
|
|
if ( c === '!' || c === '[' ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-09-25 21:44:18 +02:00
|
|
|
// Parse or skip cosmetic filters
|
2015-01-23 17:32:49 +01:00
|
|
|
// All cosmetic filters are caught here
|
2014-08-21 16:56:36 +02:00
|
|
|
if ( parseCosmeticFilters ) {
|
2014-09-08 23:46:58 +02:00
|
|
|
if ( cosmeticFilteringEngine.add(line) ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
continue;
|
|
|
|
}
|
2014-09-25 21:44:18 +02:00
|
|
|
} else if ( reIsCosmeticFilter.test(line) ) {
|
|
|
|
continue;
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
|
|
|
|
2015-01-23 17:32:49 +01:00
|
|
|
// Whatever else is next can be assumed to not be a cosmetic filter
|
|
|
|
|
|
|
|
// Most comments start in first column
|
2014-06-24 00:42:43 +02:00
|
|
|
if ( c === '#' ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-01-23 17:32:49 +01:00
|
|
|
// Catch comments somewhere on the line
|
|
|
|
// Remove:
|
|
|
|
// ... #blah blah blah
|
|
|
|
// ... # blah blah blah
|
|
|
|
// Don't remove:
|
|
|
|
// ...#blah blah blah
|
|
|
|
// because some ABP filters uses the `#` character (URL fragment)
|
|
|
|
pos = line.indexOf('#');
|
|
|
|
if ( pos !== -1 && reIsWhitespaceChar.test(line.charAt(pos - 1)) ) {
|
|
|
|
line = line.slice(0, pos).trim();
|
|
|
|
}
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
// https://github.com/gorhill/httpswitchboard/issues/15
|
|
|
|
// Ensure localhost et al. don't end up in the ubiquitous blacklist.
|
2015-01-23 17:32:49 +01:00
|
|
|
// With hosts files, we need to remove local IP redirection
|
|
|
|
if ( reMaybeLocalIp.test(c) ) {
|
|
|
|
// Ignore hosts file redirect configuration
|
|
|
|
// 127.0.0.1 localhost
|
|
|
|
// 255.255.255.255 broadcasthost
|
|
|
|
if ( reIsLocalhostRedirect.test(line) ) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
line = line.replace(reLocalIp, '').trim();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( line.length === 0 ) {
|
|
|
|
continue;
|
|
|
|
}
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
// The filter is whatever sequence of printable ascii character without
|
|
|
|
// whitespaces
|
2015-01-23 17:32:49 +01:00
|
|
|
//matches = reAsciiSegment.exec(line);
|
|
|
|
//if ( matches === null ) {
|
|
|
|
// console.debug('storage.js > µBlock.mergeFilterList(): skipping "%s"', lineRaw);
|
|
|
|
// continue;
|
|
|
|
//}
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
// Bypass anomalies
|
|
|
|
// For example, when a filter contains whitespace characters, or
|
|
|
|
// whatever else outside the range of printable ascii characters.
|
2015-01-23 17:32:49 +01:00
|
|
|
//if ( matches[0] !== line ) {
|
|
|
|
// console.error('"%s" !== "%s"', matches[0], line);
|
|
|
|
// continue;
|
|
|
|
//}
|
2014-06-24 00:42:43 +02:00
|
|
|
|
2015-01-23 17:32:49 +01:00
|
|
|
staticNetFilteringEngine.add(line);
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// `switches` contains the preset blacklists for which the switch must be
|
|
|
|
// revisited.
|
|
|
|
|
2014-08-20 02:41:52 +02:00
|
|
|
µBlock.reloadPresetBlacklists = function(switches, update) {
|
2014-09-15 20:28:07 +02:00
|
|
|
var µb = µBlock;
|
|
|
|
|
|
|
|
var onFilterListsReady = function() {
|
|
|
|
µb.loadUpdatableAssets({ update: update, psl: update });
|
|
|
|
};
|
2014-06-24 00:42:43 +02:00
|
|
|
|
|
|
|
// Toggle switches, if any
|
|
|
|
if ( switches !== undefined ) {
|
2014-09-15 20:28:07 +02:00
|
|
|
var filterLists = this.remoteBlacklists;
|
2014-06-24 00:42:43 +02:00
|
|
|
var i = switches.length;
|
|
|
|
while ( i-- ) {
|
2014-09-15 20:28:07 +02:00
|
|
|
if ( filterLists.hasOwnProperty(switches[i].location) === false ) {
|
2014-06-24 00:42:43 +02:00
|
|
|
continue;
|
|
|
|
}
|
2014-09-15 20:28:07 +02:00
|
|
|
filterLists[switches[i].location].off = !!switches[i].off;
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
|
|
|
// Save switch states
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.set({ 'remoteBlacklists': filterLists }, onFilterListsReady);
|
2014-09-15 20:28:07 +02:00
|
|
|
} else {
|
|
|
|
onFilterListsReady();
|
2014-06-24 00:42:43 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-08-25 02:52:34 +02:00
|
|
|
µBlock.loadPublicSuffixList = function(callback) {
|
2014-09-08 23:46:58 +02:00
|
|
|
if ( typeof callback !== 'function' ) {
|
|
|
|
callback = this.noopFunc;
|
|
|
|
}
|
2014-06-24 00:42:43 +02:00
|
|
|
var applyPublicSuffixList = function(details) {
|
|
|
|
// TODO: Not getting proper suffix list is a bit serious, I think
|
|
|
|
// the extension should be force-restarted if it occurs..
|
|
|
|
if ( !details.error ) {
|
|
|
|
publicSuffixList.parse(details.content, punycode.toASCII);
|
|
|
|
}
|
2014-09-08 23:46:58 +02:00
|
|
|
callback();
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
2014-09-08 23:46:58 +02:00
|
|
|
this.assets.get(this.pslPath, applyPublicSuffixList);
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
// Load updatable assets
|
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
µBlock.loadUpdatableAssets = function(details) {
|
|
|
|
var µb = this;
|
|
|
|
|
|
|
|
details = details || {};
|
|
|
|
var update = details.update !== false;
|
|
|
|
|
2014-08-21 04:34:22 +02:00
|
|
|
this.assets.autoUpdate = update || this.userSettings.autoUpdate;
|
2014-08-20 02:41:52 +02:00
|
|
|
this.assets.autoUpdateDelay = this.updateAssetsEvery;
|
2014-08-21 01:39:49 +02:00
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
var onFiltersReady = function() {
|
|
|
|
if ( update ) {
|
|
|
|
µb.updater.restart();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
var onPSLReady = function() {
|
|
|
|
µb.loadFilterLists(onFiltersReady);
|
|
|
|
};
|
|
|
|
|
|
|
|
if ( details.psl !== false ) {
|
|
|
|
this.loadPublicSuffixList(onPSLReady);
|
|
|
|
} else {
|
|
|
|
this.loadFilterLists(onFiltersReady);
|
2014-08-21 01:39:49 +02:00
|
|
|
}
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
µBlock.toSelfie = function() {
|
|
|
|
var selfie = {
|
|
|
|
magic: this.selfieMagic,
|
|
|
|
publicSuffixList: publicSuffixList.toSelfie(),
|
|
|
|
filterLists: this.remoteBlacklists,
|
2014-12-28 16:07:43 +01:00
|
|
|
staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(),
|
2014-09-11 22:00:50 +02:00
|
|
|
cosmeticFilteringEngine: this.cosmeticFilteringEngine.toSelfie()
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.set({ selfie: selfie });
|
2014-09-08 23:46:58 +02:00
|
|
|
// console.log('µBlock.toSelfie> made a selfie!');
|
|
|
|
};
|
|
|
|
|
|
|
|
// This is to be sure the selfie is generated in a sane manner: the selfie will
|
2014-10-17 21:44:19 +02:00
|
|
|
// be generated if the user doesn't change his filter lists selection for
|
2014-09-08 23:46:58 +02:00
|
|
|
// some set time.
|
2014-09-25 21:44:18 +02:00
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
µBlock.toSelfieAsync = function(after) {
|
|
|
|
if ( typeof after !== 'number' ) {
|
|
|
|
after = this.selfieAfter;
|
|
|
|
}
|
|
|
|
this.asyncJobs.add(
|
|
|
|
'toSelfie',
|
|
|
|
null,
|
|
|
|
this.toSelfie.bind(this),
|
|
|
|
after,
|
|
|
|
false
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.fromSelfie = function(callback) {
|
|
|
|
var µb = this;
|
|
|
|
|
|
|
|
if ( typeof callback !== 'function' ) {
|
|
|
|
callback = this.noopFunc;
|
|
|
|
}
|
|
|
|
|
|
|
|
var onSelfieReady = function(store) {
|
|
|
|
var selfie = store.selfie;
|
|
|
|
if ( typeof selfie !== 'object' || selfie.magic !== µb.selfieMagic ) {
|
|
|
|
callback(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) {
|
|
|
|
callback(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// console.log('µBlock.fromSelfie> selfie looks good');
|
|
|
|
µb.remoteBlacklists = selfie.filterLists;
|
2014-12-28 16:07:43 +01:00
|
|
|
µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.cosmeticFilteringEngine.fromSelfie(selfie.cosmeticFilteringEngine);
|
|
|
|
callback(true);
|
|
|
|
};
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.get('selfie', onSelfieReady);
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
µBlock.destroySelfie = function() {
|
2014-10-17 21:44:19 +02:00
|
|
|
vAPI.storage.remove('selfie');
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
2014-06-24 00:42:43 +02:00
|
|
|
// Load all
|
|
|
|
|
|
|
|
µBlock.load = function() {
|
2014-08-25 02:52:34 +02:00
|
|
|
var µb = this;
|
2014-09-11 22:00:50 +02:00
|
|
|
var fromSelfie = false;
|
2014-08-25 02:52:34 +02:00
|
|
|
|
2014-09-15 17:09:06 +02:00
|
|
|
// Final initialization steps after all needed assets are in memory.
|
|
|
|
// - Initialize internal state with maybe already existing tabs.
|
|
|
|
// - Schedule next update operation.
|
2015-01-22 03:46:11 +01:00
|
|
|
var onAllReady = function() {
|
2014-12-20 21:28:16 +01:00
|
|
|
// https://github.com/gorhill/uBlock/issues/426
|
|
|
|
// Important: remove barrier to remote fetching, this was useful only
|
|
|
|
// for launch time.
|
|
|
|
µb.assets.allowRemoteFetch = true;
|
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
// https://github.com/gorhill/uBlock/issues/184
|
2014-12-20 21:28:16 +01:00
|
|
|
// Check for updates not too far in the future.
|
|
|
|
µb.updater.restart(µb.firstUpdateAfter);
|
2015-01-23 17:32:49 +01:00
|
|
|
|
|
|
|
vAPI.onLoadAllCompleted();
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
|
|
|
|
2015-01-22 03:46:11 +01:00
|
|
|
// To bring older versions up to date
|
|
|
|
var onVersionReady = function(bin) {
|
|
|
|
var lastVersion = bin.version || '0.0.0.0';
|
2015-01-24 18:06:22 +01:00
|
|
|
|
|
|
|
// Whitelist some key scopes by default
|
2015-01-22 03:46:11 +01:00
|
|
|
if ( lastVersion.localeCompare('0.8.6.0') < 0 ) {
|
2015-01-24 18:06:22 +01:00
|
|
|
µb.netWhitelist = µb.whitelistFromString(
|
|
|
|
µb.stringFromWhitelist(µb.netWhitelist) +
|
|
|
|
'\n' +
|
|
|
|
µb.netWhitelistDefault
|
|
|
|
);
|
|
|
|
µb.saveWhitelist();
|
2015-01-22 03:46:11 +01:00
|
|
|
}
|
2015-01-24 18:06:22 +01:00
|
|
|
|
2015-01-22 03:46:11 +01:00
|
|
|
vAPI.storage.set({ version: vAPI.app.version });
|
|
|
|
onAllReady();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Filter lists
|
|
|
|
// Whitelist
|
|
|
|
var countdown = 2;
|
|
|
|
var doCountdown = function() {
|
|
|
|
countdown -= 1;
|
|
|
|
if ( countdown !== 0 ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Last step: do whatever is necessary when version changes
|
|
|
|
vAPI.storage.get('version', onVersionReady);
|
|
|
|
};
|
|
|
|
|
2014-09-11 22:00:50 +02:00
|
|
|
// Filters are in memory.
|
|
|
|
// Filter engines need PSL to be ready.
|
2014-09-08 23:46:58 +02:00
|
|
|
var onFiltersReady = function() {
|
2015-01-16 16:57:56 +01:00
|
|
|
doCountdown();
|
2014-12-08 17:16:13 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// https://github.com/gorhill/uBlock/issues/226
|
|
|
|
// Whitelist in memory.
|
|
|
|
// Whitelist parser needs PSL to be ready.
|
2014-12-15 20:37:09 +01:00
|
|
|
// gorhill 2014-12-15: not anymore
|
2014-12-08 17:16:13 +01:00
|
|
|
var onWhitelistReady = function() {
|
2015-01-16 16:57:56 +01:00
|
|
|
doCountdown();
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
// Load order because dependencies:
|
2014-12-15 20:37:09 +01:00
|
|
|
// User settings -> PSL -> [filter lists]
|
2014-08-25 02:52:34 +02:00
|
|
|
var onPSLReady = function() {
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.loadFilterLists(onFiltersReady);
|
2014-08-25 02:52:34 +02:00
|
|
|
};
|
|
|
|
|
2014-10-17 21:44:19 +02:00
|
|
|
// If no selfie available, take the long way, i.e. load and parse
|
2014-09-08 23:46:58 +02:00
|
|
|
// raw data.
|
|
|
|
var onSelfieReady = function(success) {
|
|
|
|
if ( success === true ) {
|
2014-09-11 22:00:50 +02:00
|
|
|
fromSelfie = true;
|
2014-12-15 20:37:09 +01:00
|
|
|
onFiltersReady();
|
2014-09-08 23:46:58 +02:00
|
|
|
return;
|
|
|
|
}
|
2014-08-25 02:52:34 +02:00
|
|
|
µb.loadPublicSuffixList(onPSLReady);
|
|
|
|
};
|
|
|
|
|
2014-09-08 23:46:58 +02:00
|
|
|
// User settings are in memory
|
2015-01-07 14:49:52 +01:00
|
|
|
var onUserSettingsReady = function(userSettings) {
|
2014-12-20 21:28:16 +01:00
|
|
|
// https://github.com/gorhill/uBlock/issues/426
|
|
|
|
// Important: block remote fetching for when loading assets at launch
|
|
|
|
// time.
|
|
|
|
µb.assets.allowRemoteFetch = false;
|
2015-01-07 14:49:52 +01:00
|
|
|
µb.assets.autoUpdate = userSettings.autoUpdate;
|
2014-09-08 23:46:58 +02:00
|
|
|
µb.fromSelfie(onSelfieReady);
|
2015-01-19 01:17:36 +01:00
|
|
|
|
|
|
|
// https://github.com/gorhill/uBlock/issues/540
|
|
|
|
// Disabling local mirroring for the time being
|
|
|
|
userSettings.experimentalEnabled = false;
|
2015-01-19 04:26:07 +01:00
|
|
|
µb.mirrors.toggle(false /* userSettings.experimentalEnabled */);
|
2015-01-19 01:17:36 +01:00
|
|
|
|
2015-01-07 14:49:52 +01:00
|
|
|
µb.contextMenu.toggle(userSettings.contextMenuEnabled);
|
|
|
|
µb.dynamicNetFilteringEngine.fromString(userSettings.dynamicFilteringString);
|
2014-12-28 16:07:43 +01:00
|
|
|
|
2015-01-06 14:01:15 +01:00
|
|
|
// Remove obsolete setting
|
2015-01-07 14:49:52 +01:00
|
|
|
delete userSettings.logRequests;
|
2015-01-06 14:01:15 +01:00
|
|
|
µb.XAL.keyvalRemoveOne('logRequests');
|
|
|
|
|
2015-01-07 14:49:52 +01:00
|
|
|
if ( typeof userSettings.dynamicFilteringSelfie === 'string' ) {
|
|
|
|
if ( userSettings.dynamicFilteringString === '' && userSettings.dynamicFilteringSelfie !== '' ) {
|
|
|
|
µb.dynamicNetFilteringEngine.fromObsoleteSelfie(userSettings.dynamicFilteringSelfie);
|
|
|
|
userSettings.dynamicFilteringString = µb.dynamicNetFilteringEngine.toString();
|
|
|
|
µb.XAL.keyvalSetOne('dynamicFilteringString', userSettings.dynamicFilteringString);
|
2015-01-06 17:44:06 +01:00
|
|
|
|
|
|
|
// Auto-enable advanced user if there were dynamic rules
|
2015-01-07 14:49:52 +01:00
|
|
|
userSettings.advancedUserEnabled = userSettings.dynamicFilteringString !== '';
|
|
|
|
µb.XAL.keyvalSetOne('advancedUserEnabled', userSettings.advancedUserEnabled);
|
2014-12-28 16:07:43 +01:00
|
|
|
}
|
2015-01-07 14:49:52 +01:00
|
|
|
delete userSettings.dynamicFilteringSelfie;
|
2014-12-28 16:07:43 +01:00
|
|
|
µb.XAL.keyvalRemoveOne('dynamicFilteringSelfie');
|
|
|
|
}
|
2014-09-08 23:46:58 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
this.loadUserSettings(onUserSettingsReady);
|
2014-12-15 20:37:09 +01:00
|
|
|
this.loadWhitelist(onWhitelistReady);
|
2014-08-20 07:18:53 +02:00
|
|
|
this.loadLocalSettings();
|
2014-06-24 00:42:43 +02:00
|
|
|
};
|