diff --git a/src/epicker.html b/src/epicker.html
index 29a098f64..0d51f2690 100644
--- a/src/epicker.html
+++ b/src/epicker.html
@@ -121,7 +121,7 @@ svg > path:first-child {
svg > path + path {
stroke: #F00;
stroke-width: 0.5px;
- fill: rgba(255,0,0,0.25);
+ fill: rgba(255,31,31,0.25);
}
aside {
background-color: rgba(255,255,255,0.9);
diff --git a/src/js/background.js b/src/js/background.js
index c640a3cb2..1419a43b7 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -63,6 +63,7 @@ return {
externalLists: defaultExternalLists,
firewallPaneMinimized: true,
parseAllABPHideFilters: true,
+ requestLogMaxEntries: 0,
showIconBadge: true
},
@@ -80,15 +81,24 @@ return {
localSettings: {
blockedRequestCount: 0,
- allowedRequestCount: 0
+ allowedRequestCount: 0,
},
+ localSettingsModifyTime: 0,
+ localSettingsSaveTime: 0,
// read-only
systemSettings: {
- compiledMagic: 'shztbfhkfjit',
+ compiledMagic: 'wcuwrlodqyee',
selfieMagic: 'spqmeuaftfra'
},
+ restoreBackupSettings: {
+ lastRestoreFile: '',
+ lastRestoreTime: 0,
+ lastBackupFile: '',
+ lastBackupTime: 0
+ },
+
// EasyList, EasyPrivacy and many others have an 4-day update period,
// as per list headers.
updateAssetsEvery: 97 * oneHour,
diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js
index 5efbeb797..3812fc478 100644
--- a/src/js/contentscript-end.js
+++ b/src/js/contentscript-end.js
@@ -364,7 +364,7 @@ var messager = vAPI.messaging.channel('contentscript-end.js');
// so we will just skip them from now on.
if ( processHighHighGenericsMisses === 0 ) {
injectedSelectors['{{highHighGenerics}}'] = true;
- console.debug('high-high generic: apparently not needed...');
+ //console.debug('high-high generic: apparently not needed...');
}
return;
}
diff --git a/src/js/contentscript-start.js b/src/js/contentscript-start.js
index e2eb6f0a2..6fba5730c 100644
--- a/src/js/contentscript-start.js
+++ b/src/js/contentscript-start.js
@@ -156,11 +156,12 @@ var hideElements = function(selectors) {
}
};
+var url = window.location.href;
localMessager.send(
{
what: 'retrieveDomainCosmeticSelectors',
- pageURL: window.location.href,
- locationURL: window.location.href
+ pageURL: url,
+ locationURL: url
},
filteringHandler
);
diff --git a/src/js/devtool-log.js b/src/js/devtool-log.js
index 621e504e1..6f2bd1918 100644
--- a/src/js/devtool-log.js
+++ b/src/js/devtool-log.js
@@ -37,6 +37,15 @@ var body = doc.body;
var tbody = doc.querySelector('#content tbody');
var rowJunkyard = [];
var reFilter = null;
+var filterTargetTestResult = true;
+var maxEntries = 0;
+
+var prettyRequestTypes = {
+ 'main_frame': 'doc',
+ 'stylesheet': 'css',
+ 'sub_frame': 'frame',
+ 'xmlhttprequest': 'xhr'
+};
/******************************************************************************/
@@ -102,7 +111,7 @@ var renderLogEntry = function(entry) {
var tr = createRow();
if ( entry.result.charAt(1) === 'b' ) {
tr.classList.add('blocked');
- tr.cells[0].textContent = ' \u2212\u00A0';
+ tr.cells[0].textContent = ' -\u00A0';
} else if ( entry.result.charAt(1) === 'a' ) {
tr.classList.add('allowed');
if ( entry.result.charAt(0) === 'm' ) {
@@ -110,7 +119,7 @@ var renderLogEntry = function(entry) {
}
tr.cells[0].textContent = ' +\u00A0';
} else {
- tr.cells[0].textContent = ' ';
+ tr.cells[0].textContent = '';
}
if ( entry.type === 'main_frame' ) {
tr.classList.add('maindoc');
@@ -119,8 +128,8 @@ var renderLogEntry = function(entry) {
if ( entry.result.lastIndexOf('sa', 0) === 0 ) {
filterText = '@@' + filterText;
}
- tr.cells[1].textContent = filterText + ' ';
- tr.cells[2].textContent = entry.type + ' ';
+ tr.cells[1].textContent = filterText + '\t';
+ tr.cells[2].textContent = (prettyRequestTypes[entry.type] || entry.type) + '\t';
vAPI.insertHTML(tr.cells[3], renderURL(entry.url, entry.result));
applyFilterToRow(tr);
tbody.insertBefore(tr, tbody.firstChild);
@@ -141,6 +150,11 @@ var renderLogBuffer = function(buffer) {
renderLogEntry(buffer[i]);
}
+ // Prevent logger from growing infinitely and eating all memory. For
+ // instance someone could forget that it is left opened for some
+ // dynamically refreshed pages.
+ truncateLog(maxEntries);
+
var yDelta = tbody.offsetHeight - height;
if ( yDelta === 0 ) {
return;
@@ -165,6 +179,18 @@ var renderLogBuffer = function(buffer) {
/******************************************************************************/
+var truncateLog = function(size) {
+ if ( size === 0 ) {
+ size = 25000;
+ }
+ size = Math.min(size, 25000);
+ while ( tbody.childElementCount > size ) {
+ rowJunkyard.push(tbody.removeChild(tbody.lastElementChild));
+ }
+};
+
+/******************************************************************************/
+
var onBufferRead = function(buffer) {
if ( Array.isArray(buffer) ) {
renderLogBuffer(buffer);
@@ -200,7 +226,7 @@ var reloadTab = function() {
var applyFilterToRow = function(row) {
var re = reFilter;
- if ( re === null || re.test(row.textContent) ) {
+ if ( re === null || re.test(row.textContent) === filterTargetTestResult ) {
row.classList.remove('hidden');
} else {
row.classList.add('hidden');
@@ -219,8 +245,9 @@ var applyFilter = function() {
return;
}
var re = reFilter;
+ var target = filterTargetTestResult;
while ( row !== null ) {
- if ( re.test(row.textContent) ) {
+ if ( re.test(row.textContent) === target ) {
row.classList.remove('hidden');
} else {
row.classList.add('hidden');
@@ -251,25 +278,45 @@ var onFilterButton = function() {
/******************************************************************************/
var onFilterChanged = function() {
- var filterRaw = uDom('#filterExpression').val().trim();
+ var filterExpression = uDom('#filterExpression');
+ var filterRaw = filterExpression.val().trim();
+ // Assume good filter expression
+ filterExpression.removeClass('bad');
+
+ // Invert resultset?
+ filterTargetTestResult = filterRaw.charAt(0) !== '!';
+ if ( filterTargetTestResult === false ) {
+ filterRaw = filterRaw.slice(1);
+ }
+
+ // No filter
if ( filterRaw === '') {
reFilter = null;
- unapplyFilter();
return;
}
+ // Regex?
+ if ( filterRaw.length > 1 && filterRaw.charAt(0) === '/' && filterRaw.slice(-1) === '/' ) {
+ try {
+ reFilter = new RegExp(filterRaw.slice(1, -1));
+ } catch (e) {
+ reFilter = null;
+ filterExpression.addClass('bad');
+ }
+ return;
+ }
+
+ // Plain filtering
var filterParts = filterRaw
- .replace(/^\s*-(\s+|$)/, '−\xA0')
- .replace(/^\s*\\+(\s+|$)/, '\\+\xA0')
- .split(/\s+/);
+ .replace(/^\s*-(\s+|$)/, '-\xA0 ')
+ .replace(/^\s*\\+(\s+|$)/, '+\xA0 ')
+ .split(/[ \f\n\r\t\v]+/);
var n = filterParts.length;
for ( var i = 0; i < n; i++ ) {
filterParts[i] = filterParts[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
reFilter = new RegExp(filterParts.join('.*\\s+.*'));
-
- applyFilter();
};
/******************************************************************************/
@@ -280,6 +327,7 @@ var onFilterChangedAsync = (function() {
var commit = function() {
timer = null;
onFilterChanged();
+ applyFilter();
};
var changed = function() {
@@ -294,6 +342,28 @@ var onFilterChangedAsync = (function() {
/******************************************************************************/
+var onMaxEntriesChanged = function() {
+ var raw = uDom(this).val();
+ try {
+ maxEntries = parseInt(raw, 10);
+ if ( isNaN(maxEntries) ) {
+ maxEntries = 0;
+ }
+ } catch (e) {
+ maxEntries = 0;
+ }
+
+ messager.send({
+ what: 'userSettings',
+ name: 'requestLogMaxEntries',
+ value: maxEntries
+ });
+
+ truncateLog(maxEntries);
+};
+
+/******************************************************************************/
+
uDom.onLoad(function() {
// Extract the tab id of the page we need to pull the log
var matches = window.location.search.match(/[\?&]tabId=([^&]+)/);
@@ -301,12 +371,19 @@ uDom.onLoad(function() {
inspectedTabId = matches[1];
}
+ var onSettingsReady = function(settings) {
+ maxEntries = settings.requestLogMaxEntries || 0;
+ uDom('#maxEntries').val(maxEntries || '');
+ };
+ messager.send({ what: 'getUserSettings' }, onSettingsReady);
+
readLogBuffer();
uDom('#reload').on('click', reloadTab);
uDom('#clear').on('click', clearBuffer);
uDom('#filterButton').on('click', onFilterButton);
uDom('#filterExpression').on('input', onFilterChangedAsync);
+ uDom('#maxEntries').on('change', onMaxEntriesChanged);
});
/******************************************************************************/
diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js
index 312cb322c..a64a44a8a 100644
--- a/src/js/dynamic-net-filtering.js
+++ b/src/js/dynamic-net-filtering.js
@@ -26,6 +26,8 @@
µBlock.Firewall = (function() {
+'use strict';
+
/******************************************************************************/
var magicId = 'chmdgxwtetgu';
@@ -148,11 +150,22 @@ Matrix.prototype.assign = function(other) {
Matrix.prototype.copyRules = function(other, srcHostname, desHostnames) {
var thisRules = this.rules;
var otherRules = other.rules;
+ var ruleKey, ruleValue;
// Specific types
- thisRules['* *'] = otherRules['* *'];
- var ruleKey = srcHostname + ' *';
- thisRules[ruleKey] = otherRules[ruleKey];
+ ruleValue = otherRules['* *'] || 0;
+ if ( ruleValue !== 0 ) {
+ thisRules['* *'] = ruleValue;
+ } else {
+ delete thisRules['* *'];
+ }
+ ruleKey = srcHostname + ' *';
+ ruleValue = otherRules[ruleKey] || 0;
+ if ( ruleValue !== 0 ) {
+ thisRules[ruleKey] = ruleValue;
+ } else {
+ delete thisRules[ruleKey];
+ }
// Specific destinations
for ( var desHostname in desHostnames ) {
@@ -160,9 +173,19 @@ Matrix.prototype.copyRules = function(other, srcHostname, desHostnames) {
continue;
}
ruleKey = '* ' + desHostname;
- thisRules[ruleKey] = otherRules[ruleKey];
+ ruleValue = otherRules[ruleKey] || 0;
+ if ( ruleValue !== 0 ) {
+ thisRules[ruleKey] = ruleValue;
+ } else {
+ delete thisRules[ruleKey];
+ }
ruleKey = srcHostname + ' ' + desHostname ;
- thisRules[ruleKey] = otherRules[ruleKey];
+ ruleValue = otherRules[ruleKey] || 0;
+ if ( ruleValue !== 0 ) {
+ thisRules[ruleKey] = ruleValue;
+ } else {
+ delete thisRules[ruleKey];
+ }
}
return true;
diff --git a/src/js/element-picker.js b/src/js/element-picker.js
index b3be0ffb4..a5f35a480 100644
--- a/src/js/element-picker.js
+++ b/src/js/element-picker.js
@@ -412,9 +412,9 @@ var elementsFromFilter = function(filter) {
// One idea is to normalize all a[href] on the page, but for now I will
// wait and see, as I prefer to refrain from tampering with the page
// content if I can avoid it.
- if ( filter.slice(0, 2) === '##' ) {
+ if ( filter.lastIndexOf('##', 0) === 0 ) {
try {
- out = document.querySelectorAll(filter.replace('##', ''));
+ out = document.querySelectorAll(filter.slice(2));
}
catch (e) {
}
@@ -423,16 +423,47 @@ var elementsFromFilter = function(filter) {
// Net filters: we need to lookup manually -- translating into a
// foolproof CSS selector is just not possible
- if ( filter.slice(0, 2) === '||' ) {
- filter = filter.replace('||', '');
+
+ // https://github.com/gorhill/uBlock/issues/945
+ // Transform into a regular expression, this allows the user to edit and
+ // insert wildcard(s) into the proposed filter
+ var reStr = '';
+ if ( filter.length > 1 && filter.charAt(0) === '/' && filter.slice(-1) === '/' ) {
+ reStr = filter.slice(1, -1);
}
- var elems = document.querySelectorAll('iframe, img, object, embed');
+ else {
+ var rePrefix = '', reSuffix = '';
+ if ( filter.slice(0, 2) === '||' ) {
+ filter = filter.replace('||', '');
+ } else {
+ if ( filter.charAt(0) === '|' ) {
+ rePrefix = '^';
+ filter = filter.slice(1);
+ }
+ }
+ if ( filter.slice(-1) === '|' ) {
+ reSuffix = '$';
+ filter = filter.slice(0, -1);
+ }
+ reStr = rePrefix +
+ filter.replace(/[.+?${}()|[\]\\]/g, '\\$&').replace(/[\*^]+/g, '.*') +
+ reSuffix;
+ }
+ var reFilter = null;
+ try {
+ reFilter = new RegExp(reStr);
+ } catch (e) {
+ return out;
+ }
+
+ var props = netFilterSources;
+ var elems = document.querySelectorAll(Object.keys(props).join());
var i = elems.length;
var elem, src;
while ( i-- ) {
elem = elems[i];
- src = elem[netFilterSources[elem.tagName.toLowerCase()]];
- if ( src && src.indexOf(filter) !== -1 ) {
+ src = elem[props[elem.tagName.toLowerCase()]];
+ if ( src && reFilter.test(src) ) {
out.push(elem);
}
}
@@ -452,14 +483,15 @@ var userFilterFromCandidate = function() {
}
// Cosmetic filter?
- if ( v.slice(0, 2) === '##' ) {
+ if ( v.lastIndexOf('##', 0) === 0 ) {
return window.location.hostname + v;
}
// If domain included in filter, no need for domain option
- if ( v.slice(0, 2) === '||' ) {
+ if ( v.lastIndexOf('||', 0) === 0 ) {
return v;
}
+
// Assume net filter
return v + '$domain=' + window.location.hostname;
};
@@ -529,7 +561,11 @@ var onDialogClicked = function(ev) {
else if ( ev.target.id === 'create' ) {
var filter = userFilterFromCandidate();
if ( filter ) {
- localMessager.send({ what: 'createUserFilter', filters: filter });
+ var d = new Date();
+ localMessager.send({
+ what: 'createUserFilter',
+ filters: '! ' + d.toLocaleString() + ' ' + window.location.href + '\n' + filter,
+ });
removeElements(elementsFromFilter(taCandidate.value));
stopPicker();
}
diff --git a/src/js/i18n.js b/src/js/i18n.js
index 95304f51d..231a1dbf5 100644
--- a/src/js/i18n.js
+++ b/src/js/i18n.js
@@ -37,10 +37,7 @@ uDom.onLoad(function() {
}
});
uDom('[placeholder]').forEach(function(elem) {
- var placeholder = vAPI.i18n(elem.attr('placeholder'));
- if ( placeholder ) {
- elem.attr('placeholder', placeholder);
- }
+ elem.attr('placeholder', vAPI.i18n(elem.attr('placeholder')));
});
uDom('[data-i18n-tip]').forEach(function(elem) {
elem.attr(
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 3f5f73598..ff811b42b 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -961,24 +961,58 @@ var µb = µBlock;
/******************************************************************************/
-var getUserData = function(callback) {
- var onUserFiltersReady = function(details) {
+var getLocalData = function(callback) {
+ var onStorageInfoReady = function(bytesInUse) {
+ var o = µb.restoreBackupSettings;
callback({
- 'timeStamp': Date.now(),
- 'version': vAPI.app.version,
- 'userSettings': µb.userSettings,
- 'filterLists': µb.remoteBlacklists,
- 'netWhitelist': µb.stringFromWhitelist(µb.netWhitelist),
- 'userFilters': details.content
+ storageUsed: bytesInUse,
+ lastRestoreFile: o.lastRestoreFile,
+ lastRestoreTime: o.lastRestoreTime,
+ lastBackupFile: o.lastBackupFile,
+ lastBackupTime: o.lastBackupTime
});
};
+
+ µb.getBytesInUse(onStorageInfoReady);
+};
+
+/******************************************************************************/
+
+var backupUserData = function(callback) {
+ var onUserFiltersReady = function(details) {
+ var userData = {
+ timeStamp: Date.now(),
+ version: vAPI.app.version,
+ userSettings: µb.userSettings,
+ filterLists: µb.remoteBlacklists,
+ netWhitelist: µb.stringFromWhitelist(µb.netWhitelist),
+ userFilters: details.content
+ };
+ var now = new Date();
+ var filename = vAPI.i18n('aboutBackupFilename')
+ .replace('{{datetime}}', now.toLocaleString())
+ .replace(/ +/g, '_');
+
+ vAPI.download({
+ 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')),
+ 'filename': filename
+ });
+
+ µb.restoreBackupSettings.lastBackupFile = filename;
+ µb.restoreBackupSettings.lastBackupTime = Date.now();
+ µb.XAL.keyvalSetMany(µb.restoreBackupSettings);
+
+ getLocalData(callback);
+ };
+
µb.assets.get('assets/user/filters.txt', onUserFiltersReady);
};
/******************************************************************************/
-var restoreUserData = function(userData) {
- var countdown = 5;
+var restoreUserData = function(request) {
+ var userData = request.userData;
+ var countdown = 6;
var onCountdown = function() {
countdown -= 1;
if ( countdown === 0 ) {
@@ -994,6 +1028,13 @@ var restoreUserData = function(userData) {
µb.XAL.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown);
µb.XAL.keyvalSetOne('netWhitelist', userData.netWhitelist, onCountdown);
µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown);
+
+ µb.XAL.keyvalSetMany({
+ lastRestoreFile: request.file || '',
+ lastRestoreTime: Date.now(),
+ lastBackupFile: '',
+ lastBackupTime: 0
+ }, onCountdown);
};
// If we are going to restore all, might as well wipe out clean local
@@ -1006,7 +1047,7 @@ var restoreUserData = function(userData) {
var resetUserData = function() {
µb.XAL.keyvalRemoveAll();
// Keep global counts, people can become quite attached to numbers
- µBlock.saveLocalSettings();
+ µb.saveLocalSettings();
vAPI.app.restart();
};
@@ -1015,8 +1056,11 @@ var resetUserData = function() {
var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
- case 'getUserData':
- return getUserData(callback);
+ case 'backupUserData':
+ return backupUserData(callback);
+
+ case 'getLocalData':
+ return getLocalData(callback);
default:
break;
@@ -1027,7 +1071,7 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) {
case 'restoreUserData':
- restoreUserData(request.userData);
+ restoreUserData(request);
break;
case 'resetUserData':
diff --git a/src/js/pagestore.js b/src/js/pagestore.js
index a8cb32dbd..d1c642e45 100644
--- a/src/js/pagestore.js
+++ b/src/js/pagestore.js
@@ -713,9 +713,10 @@ PageStore.prototype.logRequest = function(context, result) {
if ( requestHostname === '' ) {
requestHostname = context.pageHostname;
}
+ var now = Date.now();
if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) {
this.hostnameToCountMap[requestHostname] = 0;
- this.contentLastModified = Date.now();
+ this.contentLastModified = now;
}
var c = result.charAt(1);
if ( c === '' || c === 'a' ) {
@@ -727,6 +728,7 @@ PageStore.prototype.logRequest = function(context, result) {
this.perLoadBlockedRequestCount++;
µb.localSettings.blockedRequestCount++;
}
+ µb.localSettingsModifyTime = now;
this.logBuffer.writeOne(context, result);
};
diff --git a/src/js/settings.js b/src/js/settings.js
index cf9981b2f..ae10d2067 100644
--- a/src/js/settings.js
+++ b/src/js/settings.js
@@ -33,27 +33,16 @@ var messager = vAPI.messaging.channel('settings.js');
/******************************************************************************/
-var exportToFile = function() {
- var onUserDataReady = function(userData) {
- if (!userData) {
- return;
- }
- var now = new Date();
- var filename = vAPI.i18n('aboutBackupFilename')
- .replace('{{datetime}}', now.toLocaleString())
- .replace(/ +/g, '_');
- vAPI.download({
- 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(userData, null, ' ')),
- 'filename': filename
- });
- };
-
- messager.send({ what: 'getUserData' }, onUserDataReady);
-};
-
-/******************************************************************************/
-
var handleImportFilePicker = function() {
+ var file = this.files[0];
+ if ( file === undefined || file.name === '' ) {
+ return;
+ }
+ if ( file.type.indexOf('text') !== 0 ) {
+ return;
+ }
+ var filename = file.name;
+
var fileReaderOnLoadHandler = function() {
var userData;
try {
@@ -80,20 +69,17 @@ var handleImportFilePicker = function() {
}
var time = new Date(userData.timeStamp);
var msg = vAPI.i18n('aboutRestoreDataConfirm')
- .replace('{{time}}', time.toLocaleString());
+ .replace('{{time}}', time.toLocaleString());
var proceed = window.confirm(msg);
if ( proceed ) {
- messager.send({ what: 'restoreUserData', userData: userData });
+ messager.send({
+ what: 'restoreUserData',
+ userData: userData,
+ file: filename
+ });
}
};
- var file = this.files[0];
- if ( file === undefined || file.name === '' ) {
- return;
- }
- if ( file.type.indexOf('text') !== 0 ) {
- return;
- }
var fr = new FileReader();
fr.onload = fileReaderOnLoadHandler;
fr.readAsText(file);
@@ -112,6 +98,47 @@ var startImportFilePicker = function() {
/******************************************************************************/
+var exportToFile = function() {
+ messager.send({ what: 'backupUserData' }, onLocalDataReceived);
+};
+
+/******************************************************************************/
+
+var onLocalDataReceived = function(details) {
+ uDom('#localData > ul > li:nth-of-type(1)').text(
+ vAPI.i18n('settingsStorageUsed').replace('{{value}}', details.storageUsed.toLocaleString())
+ );
+
+ var elem, dt;
+ var timeOptions = {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ timeZoneName: 'short'
+ };
+ var lastBackupFile = details.lastBackupFile || '';
+ if ( lastBackupFile !== '' ) {
+ dt = new Date(details.lastBackupTime);
+ uDom('#localData > ul > li:nth-of-type(2) > ul > li:nth-of-type(1)').text(dt.toLocaleString('fullwide', timeOptions));
+ //uDom('#localData > ul > li:nth-of-type(2) > ul > li:nth-of-type(2)').text(lastBackupFile);
+ uDom('#localData > ul > li:nth-of-type(2)').css('display', '');
+ }
+
+ var lastRestoreFile = details.lastRestoreFile || '';
+ elem = uDom('#localData > p:nth-of-type(3)');
+ if ( lastRestoreFile !== '' ) {
+ dt = new Date(details.lastRestoreTime);
+ uDom('#localData > ul > li:nth-of-type(3) > ul > li:nth-of-type(1)').text(dt.toLocaleString('fullwide', timeOptions));
+ uDom('#localData > ul > li:nth-of-type(3) > ul > li:nth-of-type(2)').text(lastRestoreFile);
+ uDom('#localData > ul > li:nth-of-type(3)').css('display', '');
+ }
+};
+
+/******************************************************************************/
+
var resetUserData = function() {
var msg = vAPI.i18n('aboutResetDataConfirm');
var proceed = window.confirm(msg);
@@ -175,6 +202,7 @@ var onUserSettingsReceived = function(details) {
uDom.onLoad(function() {
messager.send({ what: 'userSettings' }, onUserSettingsReceived);
+ messager.send({ what: 'getLocalData' }, onLocalDataReceived);
});
/******************************************************************************/
diff --git a/src/js/start.js b/src/js/start.js
index bca966bb0..b1c82f84a 100644
--- a/src/js/start.js
+++ b/src/js/start.js
@@ -143,12 +143,6 @@ var onUserSettingsReady = function(fetched) {
/******************************************************************************/
-var onLocalSettingsReady = function(fetched) {
- fromFetch(µb.localSettings, fetched);
-};
-
-/******************************************************************************/
-
// Housekeeping, as per system setting changes
var onSystemSettingsReady = function(fetched) {
@@ -170,11 +164,11 @@ var onSystemSettingsReady = function(fetched) {
/******************************************************************************/
var onFirstFetchReady = function(fetched) {
-
// Order is important -- do not change:
onSystemSettingsReady(fetched);
- onLocalSettingsReady(fetched);
+ fromFetch(µb.localSettings, fetched);
onUserSettingsReady(fetched);
+ fromFetch(µb.restoreBackupSettings, fetched);
onNetWhitelistReady(fetched.netWhitelist);
onVersionReady(fetched.version);
@@ -191,6 +185,10 @@ var onFirstFetchReady = function(fetched) {
var fetchableProps = {
'compiledMagic': '',
+ 'lastRestoreFile': '',
+ 'lastRestoreTime': 0,
+ 'lastBackupFile': '',
+ 'lastBackupTime': 0,
'netWhitelist': '',
'selfie': null,
'selfieMagic': '',
@@ -222,6 +220,7 @@ var fromFetch = function(to, fetched) {
toFetch(µb.localSettings, fetchableProps);
toFetch(µb.userSettings, fetchableProps);
+toFetch(µb.restoreBackupSettings, fetchableProps);
vAPI.storage.get(fetchableProps, onFirstFetchReady);
diff --git a/src/js/static-net-filtering.js b/src/js/static-net-filtering.js
index 85c3a31cf..cf92cce3c 100644
--- a/src/js/static-net-filtering.js
+++ b/src/js/static-net-filtering.js
@@ -22,13 +22,12 @@
/* jshint bitwise: false, esnext: true, boss: true */
/* global punycode, µBlock */
-// Older Safari throws an exception for const when it's used with 'use strict'.
-// 'use strict';
-
/******************************************************************************/
µBlock.staticNetFilteringEngine = (function(){
+'use strict';
+
/******************************************************************************/
var µb = µBlock;
@@ -45,17 +44,17 @@ var µb = µBlock;
// | +---- bit 8-15: unused
// +---- bit 15: never use! (to ensure valid unicode character)
-const BlockAction = 0 << 0;
-const AllowAction = 1 << 0;
-const ToggleAction = BlockAction ^ AllowAction;
+var BlockAction = 0 << 0;
+var AllowAction = 1 << 0;
+var ToggleAction = BlockAction ^ AllowAction;
-const Important = 1 << 1;
+var Important = 1 << 1;
-const AnyParty = 0 << 2;
-const FirstParty = 1 << 2;
-const ThirdParty = 2 << 2;
+var AnyParty = 0 << 2;
+var FirstParty = 1 << 2;
+var ThirdParty = 2 << 2;
-const AnyType = 1 << 4;
+var AnyType = 1 << 4;
var typeNameToTypeValue = {
'stylesheet': 2 << 4,
'image': 3 << 4,
@@ -80,13 +79,13 @@ var typeOtherValue = typeNameToTypeValue.other;
// The 2 lsb *must* be zeroed
var allNetRequestTypesBitmap = (1 << (typeOtherValue >>> 4) + 2) - 4;
-const BlockAnyTypeAnyParty = BlockAction | AnyType | AnyParty;
-const BlockAnyType = BlockAction | AnyType;
-const BlockAnyParty = BlockAction | AnyParty;
+var BlockAnyTypeAnyParty = BlockAction | AnyType | AnyParty;
+var BlockAnyType = BlockAction | AnyType;
+var BlockAnyParty = BlockAction | AnyParty;
-const AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty;
-const AllowAnyType = AllowAction | AnyType;
-const AllowAnyParty = AllowAction | AnyParty;
+var AllowAnyTypeAnyParty = AllowAction | AnyType | AnyParty;
+var AllowAnyType = AllowAction | AnyType;
+var AllowAnyParty = AllowAction | AnyParty;
var reHostnameRule = /^[0-9a-z][0-9a-z.-]*[0-9a-z]$/;
var reURLPostHostnameAnchors = /[\/?#]/;
@@ -166,10 +165,18 @@ var isFirstParty = function(firstPartyDomain, hostname) {
return c === '.' || c === '';
};
-var strToRegex = function(prefix, s) {
- var reStr = s.replace(/([.+?^=!:${}()|\[\]\/\\])/g, '\\$1')
+// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
+
+var strToRegex = function(s, anchor) {
+ var reStr = s.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
.replace(/\*/g, '.*');
- return new RegExp(prefix + reStr);
+ if ( anchor < 0 ) {
+ reStr = '^' + reStr;
+ } else if ( anchor > 0 ) {
+ reStr += reStr + '$';
+ }
+ //console.debug('µBlock.staticNetFilteringEngine: created RegExp("%s")', reStr);
+ return new RegExp(reStr);
};
/*******************************************************************************
@@ -190,33 +197,13 @@ Filters family tree:
- no hostname
- specific hostname (not implemented)
-- one wildcard
- - anywhere
- - no hostname
- - specific hostname
- - anchored at start
- - no hostname
- - specific hostname
- - anchored at end
- - no hostname
- - specific hostname
+- with wildcard(s)
- anchored within hostname
- - no hostname (not implemented)
- - specific hostname (not implemented)
-
-- more than one wildcard
- - anywhere
- no hostname
- specific hostname
- - anchored at start
+ - all else
- no hostname
- specific hostname
- - anchored at end
- - no hostname
- - specific hostname
- - anchored within hostname
- - no hostname (not implemented)
- - specific hostname (not implemented)
*/
@@ -546,7 +533,7 @@ FilterPlainHnAnchored.prototype.match = function(url, tokenBeg) {
reURLPostHostnameAnchors.test(url.slice(pos + 3, tokenBeg)) === false;
};
-FilterPlainHnAnchored.fid = FilterPlainHnAnchored.prototype.fid = 'h|a';
+FilterPlainHnAnchored.fid = FilterPlainHnAnchored.prototype.fid = '||a';
FilterPlainHnAnchored.prototype.toString = function() {
return '||' + this.s;
@@ -568,310 +555,81 @@ FilterPlainHnAnchored.fromSelfie = function(s) {
/******************************************************************************/
-// With a single wildcard, regex is not optimal.
-// See:
-// http://jsperf.com/regexp-vs-indexof-abp-miss/5
-// http://jsperf.com/regexp-vs-indexof-abp-hit/4
+// Generic filter
-var FilterSingleWildcard = function(lSegment, rSegment, tokenBeg) {
- this.tokenBeg = tokenBeg;
- this.lSegment = lSegment;
- this.rSegment = rSegment;
+var FilterGeneric = function(s, anchor) {
+ this.s = s;
+ this.anchor = anchor;
+ this.re = null;
};
-FilterSingleWildcard.prototype.match = function(url, tokenBeg) {
- tokenBeg -= this.tokenBeg;
- return url.substr(tokenBeg, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
+FilterGeneric.prototype.match = function(url) {
+ if ( this.re === null ) {
+ this.re = strToRegex(this.s, this.anchor);
+ }
+ return this.re.test(url);
};
-FilterSingleWildcard.fid = FilterSingleWildcard.prototype.fid = '*';
+FilterGeneric.fid = FilterGeneric.prototype.fid = '_';
-FilterSingleWildcard.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment;
+FilterGeneric.prototype.toString = function() {
+ if ( this.anchor === 0 ) {
+ return this.s;
+ }
+ if ( this.anchor < 0 ) {
+ return '|' + this.s;
+ }
+ return this.s + '|';
};
-FilterSingleWildcard.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment + '\t' +
- this.tokenBeg;
+FilterGeneric.prototype.toSelfie = function() {
+ return this.s + '\t' + this.anchor;
};
-FilterSingleWildcard.compile = function(details) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1) + '\t' +
- details.tokenBeg;
+FilterGeneric.compile = function(details) {
+ return details.f + '\t' + details.anchor;
};
-FilterSingleWildcard.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterSingleWildcard(args[0], args[1], atoi(args[2]));
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardHostname = function(lSegment, rSegment, tokenBeg, hostname) {
- this.tokenBeg = tokenBeg;
- this.lSegment = lSegment;
- this.rSegment = rSegment;
- this.hostname = hostname;
-};
-
-FilterSingleWildcardHostname.prototype.match = function(url, tokenBeg) {
- tokenBeg -= this.tokenBeg;
- return pageHostnameRegister.slice(-this.hostname.length) === this.hostname &&
- url.substr(tokenBeg, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
-};
-
-FilterSingleWildcardHostname.fid = FilterSingleWildcardHostname.prototype.fid = '*h';
-
-FilterSingleWildcardHostname.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
-};
-
-FilterSingleWildcardHostname.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment + '\t' +
- this.tokenBeg + '\t' +
- this.hostname;
-};
-
-FilterSingleWildcardHostname.compile = function(details, hostname) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1) + '\t' +
- details.tokenBeg + '\t' +
- hostname;
-};
-
-FilterSingleWildcardHostname.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterSingleWildcardHostname(args[0], args[1], atoi(args[2]), args[3]);
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardPrefix0 = function(lSegment, rSegment) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
-};
-
-FilterSingleWildcardPrefix0.prototype.match = function(url, tokenBeg) {
- return url.substr(tokenBeg, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
-};
-
-FilterSingleWildcardPrefix0.fid = FilterSingleWildcardPrefix0.prototype.fid = '0*';
-
-FilterSingleWildcardPrefix0.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment;
-};
-
-FilterSingleWildcardPrefix0.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment;
-};
-
-FilterSingleWildcardPrefix0.compile = function(details) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' + s.slice(pos + 1);
-};
-
-FilterSingleWildcardPrefix0.fromSelfie = function(s) {
+FilterGeneric.fromSelfie = function(s) {
var pos = s.indexOf('\t');
- return new FilterSingleWildcardPrefix0(s.slice(0, pos), s.slice(pos + 1));
+ return new FilterGeneric(s.slice(0, pos), parseInt(s.slice(pos + 1), 10));
};
/******************************************************************************/
-var FilterSingleWildcardPrefix0Hostname = function(lSegment, rSegment, hostname) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
+// Generic filter
+
+var FilterGenericHostname = function(s, anchor, hostname) {
+ FilterGeneric.call(this, s, anchor);
this.hostname = hostname;
};
+FilterGenericHostname.prototype = Object.create(FilterGeneric.prototype);
+FilterGenericHostname.prototype.constructor = FilterGenericHostname;
-FilterSingleWildcardPrefix0Hostname.prototype.match = function(url, tokenBeg) {
- return pageHostnameRegister.slice(-this.hostname.length) === this.hostname &&
- url.substr(tokenBeg, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, tokenBeg + this.lSegment.length) > 0;
+FilterGenericHostname.prototype.match = function(url) {
+ if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) {
+ return false;
+ }
+ return FilterGeneric.prototype.match.call(this, url);
};
-FilterSingleWildcardPrefix0Hostname.fid = FilterSingleWildcardPrefix0Hostname.prototype.fid = '0*h';
+FilterGenericHostname.fid = FilterGenericHostname.prototype.fid = '_h';
-FilterSingleWildcardPrefix0Hostname.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
+FilterGenericHostname.prototype.toString = function() {
+ return FilterGeneric.prototype.toString.call(this) + '$domain=' + this.hostname;
};
-FilterSingleWildcardPrefix0Hostname.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment + '\t' +
- this.hostname;
+FilterGenericHostname.prototype.toSelfie = function() {
+ return FilterGeneric.prototype.toSelfie.call(this) + '\t' + this.hostname;
};
-FilterSingleWildcardPrefix0Hostname.compile = function(details, hostname) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1) + '\t' +
- hostname;
+FilterGenericHostname.compile = function(details, hostname) {
+ return FilterGeneric.compile(details) + '\t' + hostname;
};
-FilterSingleWildcardPrefix0Hostname.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterSingleWildcardPrefix0Hostname(args[0], args[1], args[2]);
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardLeftAnchored = function(lSegment, rSegment) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
-};
-
-FilterSingleWildcardLeftAnchored.prototype.match = function(url) {
- return url.slice(0, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, this.lSegment.length) > 0;
-};
-
-FilterSingleWildcardLeftAnchored.fid = FilterSingleWildcardLeftAnchored.prototype.fid = '|*';
-
-FilterSingleWildcardLeftAnchored.prototype.toString = function() {
- return '|' + this.lSegment + '*' + this.rSegment;
-};
-
-FilterSingleWildcardLeftAnchored.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment;
-};
-
-FilterSingleWildcardLeftAnchored.compile = function(details) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1);
-};
-
-FilterSingleWildcardLeftAnchored.fromSelfie = function(s) {
- var pos = s.indexOf('\t');
- return new FilterSingleWildcardLeftAnchored(s.slice(0, pos), s.slice(pos + 1));
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardLeftAnchoredHostname = function(lSegment, rSegment, hostname) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
- this.hostname = hostname;
-};
-
-FilterSingleWildcardLeftAnchoredHostname.prototype.match = function(url) {
- return pageHostnameRegister.slice(-this.hostname.length) === this.hostname &&
- url.slice(0, this.lSegment.length) === this.lSegment &&
- url.indexOf(this.rSegment, this.lSegment.length) > 0;
-};
-
-FilterSingleWildcardLeftAnchoredHostname.fid = FilterSingleWildcardLeftAnchoredHostname.prototype.fid = '|*h';
-
-FilterSingleWildcardLeftAnchoredHostname.prototype.toString = function() {
- return '|' + this.lSegment + '*' + this.rSegment + '$domain=' + this.hostname;
-};
-
-FilterSingleWildcardLeftAnchoredHostname.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment + '\t' +
- this.hostname;
-};
-
-FilterSingleWildcardLeftAnchoredHostname.compile = function(details, hostname) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1) + '\t' +
- hostname;
-};
-
-FilterSingleWildcardLeftAnchoredHostname.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterSingleWildcardLeftAnchoredHostname(args[0], args[1], args[2]);
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardRightAnchored = function(lSegment, rSegment) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
-};
-
-FilterSingleWildcardRightAnchored.prototype.match = function(url) {
- return url.slice(-this.rSegment.length) === this.rSegment &&
- url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
-};
-
-FilterSingleWildcardRightAnchored.fid = FilterSingleWildcardRightAnchored.prototype.fid = '*|';
-
-FilterSingleWildcardRightAnchored.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment + '|';
-};
-
-FilterSingleWildcardRightAnchored.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment;
-};
-
-FilterSingleWildcardRightAnchored.compile = function(details) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1);
-};
-
-FilterSingleWildcardRightAnchored.fromSelfie = function(s) {
- var pos = s.indexOf('\t');
- return new FilterSingleWildcardRightAnchored(s.slice(0, pos), s.slice(pos + 1));
-};
-
-/******************************************************************************/
-
-var FilterSingleWildcardRightAnchoredHostname = function(lSegment, rSegment, hostname) {
- this.lSegment = lSegment;
- this.rSegment = rSegment;
- this.hostname = hostname;
-};
-
-FilterSingleWildcardRightAnchoredHostname.prototype.match = function(url) {
- return pageHostnameRegister.slice(-this.hostname.length) === this.hostname &&
- url.slice(-this.rSegment.length) === this.rSegment &&
- url.lastIndexOf(this.lSegment, url.length - this.rSegment.length - this.lSegment.length) >= 0;
-};
-
-FilterSingleWildcardRightAnchoredHostname.fid = FilterSingleWildcardRightAnchoredHostname.prototype.fid = '*|h';
-
-FilterSingleWildcardRightAnchoredHostname.prototype.toString = function() {
- return this.lSegment + '*' + this.rSegment + '|$domain=' + this.hostname;
-};
-
-FilterSingleWildcardRightAnchoredHostname.prototype.toSelfie = function() {
- return this.lSegment + '\t' +
- this.rSegment + '\t' +
- this.hostname;
-};
-
-FilterSingleWildcardRightAnchoredHostname.compile = function(details, hostname) {
- var s = details.f;
- var pos = s.indexOf('*');
- return s.slice(0, pos) + '\t' +
- s.slice(pos + 1) + '\t' +
- hostname;
-};
-
-FilterSingleWildcardRightAnchoredHostname.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterSingleWildcardRightAnchoredHostname(args[0], args[1], args[2]);
+FilterGenericHostname.fromSelfie = function(s) {
+ var fields = s.split('\t');
+ return new FilterGenericHostname(fields[0], parseInt(fields[1], 10), fields[2]);
};
/******************************************************************************/
@@ -887,7 +645,7 @@ var FilterGenericHnAnchored = function(s) {
FilterGenericHnAnchored.prototype.match = function(url) {
if ( this.re === null ) {
- this.re = strToRegex('', this.s);
+ this.re = strToRegex(this.s, 0);
}
// Quick test first
if ( this.re.test(url) === false ) {
@@ -926,12 +684,13 @@ var FilterGenericHnAnchoredHostname = function(s, hostname) {
this.hostname = hostname;
};
FilterGenericHnAnchoredHostname.prototype = Object.create(FilterGenericHnAnchored.prototype);
+FilterGenericHnAnchoredHostname.prototype.constructor = FilterGenericHnAnchoredHostname;
FilterGenericHnAnchoredHostname.prototype.match = function(url) {
if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) {
return false;
}
- return FilterGenericHnAnchored.prototype.match.call(this. url);
+ return FilterGenericHnAnchored.prototype.match.call(this, url);
};
FilterGenericHnAnchoredHostname.fid = FilterGenericHnAnchoredHostname.prototype.fid = '||_h';
@@ -955,88 +714,6 @@ FilterGenericHnAnchoredHostname.fromSelfie = function(s) {
/******************************************************************************/
-// With many wildcards, a regex is best.
-
-// Ref: regex escaper taken from:
-// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
-// modified for the purpose here.
-
-var FilterManyWildcards = function(s, tokenBeg) {
- this.s = s;
- this.tokenBeg = tokenBeg;
- this.re = null;
-};
-
-FilterManyWildcards.prototype.match = function(url, tokenBeg) {
- if ( this.re === null ) {
- this.re = strToRegex('^', this.s);
- }
- return this.re.test(url.slice(tokenBeg - this.tokenBeg));
-};
-
-FilterManyWildcards.fid = FilterManyWildcards.prototype.fid = '*+';
-
-FilterManyWildcards.prototype.toString = function() {
- return this.s;
-};
-
-FilterManyWildcards.prototype.toSelfie = function() {
- return this.s + '\t' + this.tokenBeg;
-};
-
-FilterManyWildcards.compile = function(details) {
- return details.f + '\t' + details.tokenBeg;
-};
-
-FilterManyWildcards.fromSelfie = function(s) {
- var pos = s.indexOf('\t');
- return new FilterManyWildcards(s.slice(0, pos), atoi(s.slice(pos + 1)));
-};
-
-/******************************************************************************/
-
-var FilterManyWildcardsHostname = function(s, tokenBeg, hostname) {
- this.s = s;
- this.tokenBeg = tokenBeg;
- this.re = null;
- this.hostname = hostname;
-};
-
-FilterManyWildcardsHostname.prototype.match = function(url, tokenBeg) {
- if ( pageHostnameRegister.slice(-this.hostname.length) !== this.hostname ) {
- return false;
- }
- if ( this.re === null ) {
- this.re = strToRegex('^', this.s);
- }
- return this.re.test(url.slice(tokenBeg - this.tokenBeg));
-};
-
-FilterManyWildcardsHostname.fid = FilterManyWildcardsHostname.prototype.fid = '*+h';
-
-FilterManyWildcardsHostname.prototype.toString = function() {
- return this.s + '$domain=' + this.hostname;
-};
-
-FilterManyWildcardsHostname.prototype.toSelfie = function() {
- return this.s + '\t' +
- this.tokenBeg + '\t' +
- this.hostname;
-};
-
-FilterManyWildcardsHostname.compile = function(details, hostname) {
- return details.f + '\t' +
- details.tokenBeg + '\t' +
- hostname;
-};
-
-FilterManyWildcardsHostname.fromSelfie = function(s) {
- var args = s.split('\t');
- return new FilterManyWildcardsHostname(args[0], atoi(args[1]), args[2]);
-};
-
-/******************************************************************************/
-
// Regex-based filters
var FilterRegex = function(s) {
@@ -1405,24 +1082,11 @@ var getFilterClass = function(details) {
return FilterRegex;
}
var s = details.f;
- var wcOffset = s.indexOf('*');
- if ( wcOffset !== -1 ) {
+ if ( s.indexOf('*') !== -1 ) {
if ( details.hostnameAnchored ) {
return FilterGenericHnAnchored;
}
- if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
- return details.anchor === 0 ? FilterManyWildcards : null;
- }
- if ( details.anchor < 0 ) {
- return FilterSingleWildcardLeftAnchored;
- }
- if ( details.anchor > 0 ) {
- return FilterSingleWildcardRightAnchored;
- }
- if ( details.tokenBeg === 0 ) {
- return FilterSingleWildcardPrefix0;
- }
- return FilterSingleWildcard;
+ return FilterGeneric;
}
if ( details.anchor < 0 ) {
return FilterPlainLeftAnchored;
@@ -1449,24 +1113,11 @@ var getHostnameBasedFilterClass = function(details) {
return FilterRegexHostname;
}
var s = details.f;
- var wcOffset = s.indexOf('*');
- if ( wcOffset !== -1 ) {
+ if ( s.indexOf('*') !== -1 ) {
if ( details.hostnameAnchored ) {
return FilterGenericHnAnchoredHostname;
}
- if ( s.indexOf('*', wcOffset + 1) !== -1 ) {
- return details.anchor === 0 ? FilterManyWildcardsHostname : null;
- }
- if ( details.anchor < 0 ) {
- return FilterSingleWildcardLeftAnchoredHostname;
- }
- if ( details.anchor > 0 ) {
- return FilterSingleWildcardRightAnchoredHostname;
- }
- if ( details.tokenBeg === 0 ) {
- return FilterSingleWildcardPrefix0Hostname;
- }
- return FilterSingleWildcardHostname;
+ return FilterGenericHostname;
}
if ( details.anchor < 0 ) {
return FilterPlainLeftAnchoredHostname;
@@ -1860,6 +1511,7 @@ FilterContainer.prototype.reset = function() {
this.duplicateBuster = {};
this.categories = Object.create(null);
this.filterParser.reset();
+ this.filterCounts = {};
};
/******************************************************************************/
@@ -1895,20 +1547,12 @@ FilterContainer.prototype.factories = {
'|ah': FilterPlainLeftAnchoredHostname,
'a|': FilterPlainRightAnchored,
'a|h': FilterPlainRightAnchoredHostname,
- 'h|a': FilterPlainHnAnchored,
- '*': FilterSingleWildcard,
- '*h': FilterSingleWildcardHostname,
- '0*': FilterSingleWildcardPrefix0,
- '0*h': FilterSingleWildcardPrefix0Hostname,
- '|*': FilterSingleWildcardLeftAnchored,
- '|*h': FilterSingleWildcardLeftAnchoredHostname,
- '*|': FilterSingleWildcardRightAnchored,
- '*|h': FilterSingleWildcardRightAnchoredHostname,
- '*+': FilterManyWildcards,
- '*+h': FilterManyWildcardsHostname,
+ '||a': FilterPlainHnAnchored,
'//': FilterRegex,
'//h': FilterRegexHostname,
'{h}': FilterHostnameDict,
+ '_': FilterGeneric,
+ '_h': FilterGenericHostname,
'||_': FilterGenericHnAnchored,
'||_h': FilterGenericHnAnchoredHostname
};
@@ -2240,6 +1884,14 @@ FilterContainer.prototype.fromCompiledContent = function(text, lineBeg) {
this.duplicateBuster[line] = true;
factory = this.factories[fields[2]];
+
+ // For development purpose
+ //if ( this.filterCounts.hasOwnProperty(fields[2]) === false ) {
+ // this.filterCounts[fields[2]] = 1;
+ //} else {
+ // this.filterCounts[fields[2]]++;
+ //}
+
filter = factory.fromSelfie(fields[3]);
if ( entry === undefined ) {
bucket[fields[1]] = filter;
diff --git a/src/js/storage.js b/src/js/storage.js
index beb3edeed..478f3a200 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -25,9 +25,13 @@
/******************************************************************************/
-µBlock.getBytesInUse = function() {
+µBlock.getBytesInUse = function(callback) {
+ if ( typeof callback !== 'function' ) {
+ callback = this.noopFunc;
+ }
var getBytesInUseHandler = function(bytesInUse) {
µBlock.storageUsed = bytesInUse;
+ callback(bytesInUse);
};
vAPI.storage.getBytesInUse(null, getBytesInUseHandler);
};
@@ -38,6 +42,11 @@
if ( typeof callback !== 'function' ) {
callback = this.noopFunc;
}
+ if ( this.localSettingsModifyTime <= this.localSettingsSaveTime ) {
+ callback();
+ return;
+ }
+ this.localSettingsSaveTime = Date.now();
vAPI.storage.set(this.localSettings, callback);
};
@@ -124,7 +133,7 @@
if ( details.content.indexOf(content.trim()) !== -1 ) {
return;
}
- µb.saveUserFilters(details.content.trim() + '\n' + content.trim(), onSaved);
+ µb.saveUserFilters(details.content.trim() + '\n\n' + content.trim(), onSaved);
};
this.loadUserFilters(onLoaded);
diff --git a/src/settings.html b/src/settings.html
index fe376538c..17fea4963 100644
--- a/src/settings.html
+++ b/src/settings.html
@@ -7,9 +7,21 @@