diff --git a/src/background.html b/src/background.html
index 1a6b5c9ae..cb37ad841 100644
--- a/src/background.html
+++ b/src/background.html
@@ -11,7 +11,6 @@
-
diff --git a/src/js/async.js b/src/js/async.js
deleted file mode 100644
index 9debc58a2..000000000
--- a/src/js/async.js
+++ /dev/null
@@ -1,203 +0,0 @@
-/*******************************************************************************
-
- µBlock - a 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
-*/
-
-/* global µBlock */
-'use strict';
-
-/******************************************************************************/
-
-// Async job queue module
-
-µBlock.asyncJobs = (function() {
-
-/******************************************************************************/
-
-var processJobs = function() {
- asyncJobManager.process();
-};
-
-var AsyncJobEntry = function(name) {
- this.name = name;
- this.data = null;
- this.callback = null;
- this.when = 0;
- this.period = 0;
-};
-
-AsyncJobEntry.prototype.destroy = function() {
- this.name = '';
- this.data = null;
- this.callback = null;
-};
-
-/******************************************************************************/
-
-var AsyncJobManager = function() {
- this.timeResolution = 200;
- this.jobs = {};
- this.jobCount = 0;
- this.jobJunkyard = [];
- this.timerId = null;
- this.timerWhen = Number.MAX_VALUE;
-};
-
-/******************************************************************************/
-
-AsyncJobManager.prototype.restartTimer = function() {
- // TODO: Another way to do this is to extract the keys, sort the keys
- // in chronological order, than pick the first entry to get the next
- // time at which we want a time event to fire. Completely unsure the
- // overhead of extracting keys/sorting is less than what is below.
- // I could also keep the keys ordered, and use binary search when adding
- // a new job.
- var when = Number.MAX_VALUE;
- var jobs = this.jobs, job;
- for ( var jobName in jobs ) {
- job = jobs[jobName];
- if ( job instanceof AsyncJobEntry ) {
- if ( job.when < when ) {
- when = job.when;
- }
- }
- }
- // Quantize time value
- when = Math.floor((when + this.timeResolution - 1) / this.timeResolution) * this.timeResolution;
-
- // TODO: Maybe use chrome.alarms() API when the next job is at more than
- // one minute in the future... From reading about it, chrome.alarms() is
- // smarter in that it will fire the event only when the browser is not
- // too busy.
- if ( when < this.timerWhen ) {
- clearTimeout(this.timerId);
- this.timerWhen = when;
- this.timerId = vAPI.setTimeout(processJobs, Math.max(when - Date.now(), 10));
- }
-};
-
-/******************************************************************************/
-
-AsyncJobManager.prototype.add = function(name, data, callback, delay, recurrent) {
- var job = this.jobs[name];
- if ( !job ) {
- job = this.jobJunkyard.pop();
- if ( !job ) {
- job = new AsyncJobEntry(name);
- } else {
- job.name = name;
- }
- this.jobs[name] = job;
- this.jobCount++;
- }
- job.data = data;
- job.callback = callback;
- job.when = Date.now() + delay;
- job.period = recurrent ? delay : 0;
- this.restartTimer();
-};
-
-/******************************************************************************/
-
-AsyncJobManager.prototype.remove = function(jobName) {
- if ( this.jobs.hasOwnProperty(jobName) === false ) {
- return;
- }
- var job = this.jobs[jobName];
- delete this.jobs[jobName];
- job.destroy();
- this.jobCount--;
- this.jobJunkyard.push(job);
- this.restartTimer();
-};
-
-/******************************************************************************/
-
-AsyncJobManager.prototype.process = function() {
- this.timerId = null;
- this.timerWhen = Number.MAX_VALUE;
- var now = Date.now();
- var job;
- for ( var jobName in this.jobs ) {
- if ( this.jobs.hasOwnProperty(jobName) === false ) {
- continue;
- }
- job = this.jobs[jobName];
- if ( job.when > now ) {
- continue;
- }
- job.callback(job.data);
- if ( job.period ) {
- job.when = now + job.period;
- } else {
- delete this.jobs[jobName];
- job.destroy();
- this.jobCount--;
- this.jobJunkyard.push(job);
- }
- }
- this.restartTimer();
-};
-
-/******************************************************************************/
-
-// Only one instance
-var asyncJobManager = new AsyncJobManager();
-
-/******************************************************************************/
-
-// Publish
-return asyncJobManager;
-
-})();
-
-/******************************************************************************/
-
-// Update visual of extension icon.
-
-µBlock.updateBadgeAsync = (function() {
- var tabIdToTimer = Object.create(null);
-
- var updateBadge = function(tabId) {
- delete tabIdToTimer[tabId];
-
- var state = false;
- var badge = '';
-
- var pageStore = this.pageStoreFromTabId(tabId);
- if ( pageStore !== null ) {
- state = pageStore.getNetFilteringSwitch();
- if ( state && this.userSettings.showIconBadge && pageStore.perLoadBlockedRequestCount ) {
- badge = this.utils.formatCount(pageStore.perLoadBlockedRequestCount);
- }
- }
-
- vAPI.setIcon(tabId, state ? 'on' : 'off', badge);
- };
-
- return function(tabId) {
- if ( tabIdToTimer[tabId] ) {
- return;
- }
- if ( vAPI.isBehindTheSceneTabId(tabId) ) {
- return;
- }
- tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 500);
- };
-})();
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 5eddb43fc..96b99a190 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -1137,7 +1137,7 @@ var restoreUserData = function(request) {
var onAllRemoved = function() {
// Be sure to adjust `countdown` if adding/removing anything below
µb.keyvalSetOne('version', userData.version);
- µBlock.saveLocalSettings(true);
+ µBlock.saveLocalSettings();
vAPI.storage.set(userData.userSettings, onCountdown);
µb.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown);
µb.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown);
@@ -1173,7 +1173,7 @@ var resetUserData = function() {
vAPI.storage.clear();
// Keep global counts, people can become quite attached to numbers
- µb.saveLocalSettings(true);
+ µb.saveLocalSettings();
vAPI.app.restart();
};
diff --git a/src/js/redirect-engine.js b/src/js/redirect-engine.js
index 99329d3f0..a5357f079 100644
--- a/src/js/redirect-engine.js
+++ b/src/js/redirect-engine.js
@@ -302,10 +302,33 @@ RedirectEngine.prototype.supportedTypes = (function() {
/******************************************************************************/
RedirectEngine.prototype.toSelfie = function() {
- return {
+ var r = {
resources: this.resources,
- rules: this.rules
+ rules: []
};
+
+ var typeEntry, desEntry, entries, entry;
+ for ( var type in this.rules ) {
+ typeEntry = this.rules[type];
+ for ( var des in typeEntry ) {
+ desEntry = typeEntry[des];
+ for ( var src in desEntry ) {
+ entries = desEntry[src];
+ for ( var i = 0; i < entries.length; i++ ) {
+ entry = entries[i];
+ r.rules.push(
+ src + '\t' +
+ des + '\t' +
+ type + '\t' +
+ entry.c.source + '\t' +
+ entry.r
+ );
+ }
+ }
+ }
+ }
+
+ return r;
};
/******************************************************************************/
@@ -321,26 +344,10 @@ RedirectEngine.prototype.fromSelfie = function(selfie) {
}
// Rules.
- var typeEntry, desEntry, srcEntry;
var rules = selfie.rules;
- for ( var type in rules ) {
- if ( rules.hasOwnProperty(type) === false ) {
- continue;
- }
- typeEntry = rules[type];
- for ( var des in typeEntry ) {
- if ( typeEntry.hasOwnProperty(des) === false ) {
- continue;
- }
- desEntry = typeEntry[des];
- for ( var src in desEntry ) {
- if ( desEntry.hasOwnProperty(des) === false ) {
- continue;
- }
- srcEntry = desEntry[src];
- this.addRule(src, des, type, srcEntry.c, srcEntry.r);
- }
- }
+ var i = rules.length;
+ while ( i-- ) {
+ this.fromCompiledRule(rules[i]);
}
return true;
diff --git a/src/js/start.js b/src/js/start.js
index 5b92c2761..d44975430 100644
--- a/src/js/start.js
+++ b/src/js/start.js
@@ -125,7 +125,10 @@ var onSelfieReady = function(selfie) {
if ( publicSuffixList.fromSelfie(selfie.publicSuffixList) !== true ) {
return false;
}
- //console.log('start.js/onSelfieReady: selfie looks good');
+ if ( selfie.redirectEngine === undefined ) {
+ return false;
+ }
+
µb.remoteBlacklists = selfie.filterLists;
µb.staticNetFilteringEngine.fromSelfie(selfie.staticNetFilteringEngine);
µb.redirectEngine.fromSelfie(selfie.redirectEngine);
@@ -197,7 +200,7 @@ var onSystemSettingsReady = function(fetched) {
}
if ( mustSaveSystemSettings ) {
fetched.selfie = null;
- µb.destroySelfie();
+ µb.selfieManager.destroy();
vAPI.storage.set(µb.systemSettings, µb.noopFunc);
}
};
diff --git a/src/js/storage.js b/src/js/storage.js
index 0dcabd8a5..76a6e7e11 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -46,28 +46,26 @@
/******************************************************************************/
-µBlock.saveLocalSettings = function(force) {
- if ( force ) {
- this.localSettingsModifyTime = Date.now();
- }
- if ( this.localSettingsModifyTime <= this.localSettingsSaveTime ) {
- return;
- }
- this.localSettingsSaveTime = Date.now();
- vAPI.storage.set(this.localSettings);
-};
+µBlock.saveLocalSettings = (function() {
+ var saveAfter = 4 * 60 * 1000;
-/******************************************************************************/
+ var save = function() {
+ this.localSettingsSaveTime = Date.now();
+ vAPI.storage.set(this.localSettings);
+ }.bind(µBlock);
-// Save local settings regularly. Not critical.
+ var onTimeout = function() {
+ var µb = µBlock;
+ if ( µb.localSettingsModifyTime > µb.localSettingsSaveTime ) {
+ save();
+ }
+ vAPI.setTimeout(onTimeout, saveAfter);
+ };
-µBlock.asyncJobs.add(
- 'autoSaveLocalSettings',
- null,
- µBlock.saveLocalSettings.bind(µBlock),
- 4 * 60 * 1000,
- true
-);
+ vAPI.setTimeout(onTimeout, saveAfter);
+
+ return save;
+})();
/******************************************************************************/
@@ -187,6 +185,7 @@
µb.staticNetFilteringEngine.freeze();
µb.redirectEngine.freeze();
µb.cosmeticFilteringEngine.freeze();
+ µb.selfieManager.create();
};
var onLoaded = function(details) {
@@ -371,7 +370,7 @@
vAPI.messaging.broadcast({ what: 'allFilterListsReloaded' });
callback();
- µb.toSelfieAsync();
+ µb.selfieManager.create();
};
var applyCompiledFilters = function(path, compiled) {
@@ -401,7 +400,7 @@
µb.redirectEngine.reset();
µb.cosmeticFilteringEngine.reset();
µb.staticNetFilteringEngine.reset();
- µb.destroySelfie();
+ µb.selfieManager.destroy();
µb.staticFilteringReverseLookup.resetLists();
// We need to build a complete list of assets to pull first: this is
@@ -649,8 +648,6 @@
/******************************************************************************/
-// TODO: toSelfie/fromSelfie.
-
µBlock.loadRedirectResources = function(callback) {
var µb = this;
@@ -703,43 +700,55 @@
/******************************************************************************/
-µBlock.toSelfie = function() {
- var selfie = {
- magic: this.systemSettings.selfieMagic,
- publicSuffixList: publicSuffixList.toSelfie(),
- filterLists: this.remoteBlacklists,
- staticNetFilteringEngine: this.staticNetFilteringEngine.toSelfie(),
- redirectEngine: this.redirectEngine.toSelfie(),
- cosmeticFilteringEngine: this.cosmeticFilteringEngine.toSelfie()
- };
- vAPI.storage.set({ selfie: selfie });
- //console.debug('storage.js > µBlock.toSelfie()');
-};
-
// This is to be sure the selfie is generated in a sane manner: the selfie will
// be generated if the user doesn't change his filter lists selection for
// some set time.
-µBlock.toSelfieAsync = function(after) {
- if ( typeof after !== 'number' ) {
- after = this.selfieAfter;
- }
- this.asyncJobs.add(
- 'toSelfie',
- null,
- this.toSelfie.bind(this),
- after,
- false
- );
-};
+µBlock.selfieManager = (function() {
+ var µb = µBlock;
+ var timer = null;
-/******************************************************************************/
+ var create = function() {
+ timer = null;
-µBlock.destroySelfie = function() {
- vAPI.storage.remove('selfie');
- this.asyncJobs.remove('toSelfie');
- //console.debug('µBlock.destroySelfie()');
-};
+ var selfie = {
+ magic: µb.systemSettings.selfieMagic,
+ publicSuffixList: publicSuffixList.toSelfie(),
+ filterLists: µb.remoteBlacklists,
+ staticNetFilteringEngine: µb.staticNetFilteringEngine.toSelfie(),
+ redirectEngine: µb.redirectEngine.toSelfie(),
+ cosmeticFilteringEngine: µb.cosmeticFilteringEngine.toSelfie()
+ };
+
+ vAPI.storage.set({ selfie: selfie });
+ };
+
+ var createAsync = function(after) {
+ if ( typeof after !== 'number' ) {
+ after = µb.selfieAfter;
+ }
+
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ }
+
+ timer = vAPI.setTimeout(create, after);
+ };
+
+ var destroy = function() {
+ if ( timer !== null ) {
+ clearTimeout(timer);
+ timer = null;
+ }
+
+ vAPI.storage.remove('selfie');
+ };
+
+ return {
+ create: createAsync,
+ destroy: destroy
+ };
+})();
/******************************************************************************/
@@ -874,6 +883,10 @@
// remote servers.
µb.assets.remoteFetchBarrier += 1;
+ if ( details.hasOwnProperty('assets/ublock/redirect-resources.txt') ) {
+ µb.loadRedirectResources();
+ }
+
var onFiltersReady = function() {
µb.assets.remoteFetchBarrier -= 1;
};
@@ -894,10 +907,6 @@
} else {
onPSLReady();
}
-
- if ( details.hasOwnProperty('assets/ublock/redirect-resources.txt') ) {
- µb.loadRedirectResources();
- }
};
/******************************************************************************/
@@ -925,7 +934,7 @@
continue;
}
}
- this.destroySelfie();
+ this.selfieManager.destroy();
barrier = false;
};
diff --git a/src/js/tab.js b/src/js/tab.js
index b4b1cc93f..59212022a 100644
--- a/src/js/tab.js
+++ b/src/js/tab.js
@@ -670,6 +670,41 @@ vAPI.tabs.registerListeners();
/******************************************************************************/
+// Update visual of extension icon.
+
+µb.updateBadgeAsync = (function() {
+ var tabIdToTimer = Object.create(null);
+
+ var updateBadge = function(tabId) {
+ delete tabIdToTimer[tabId];
+
+ var state = false;
+ var badge = '';
+
+ var pageStore = this.pageStoreFromTabId(tabId);
+ if ( pageStore !== null ) {
+ state = pageStore.getNetFilteringSwitch();
+ if ( state && this.userSettings.showIconBadge && pageStore.perLoadBlockedRequestCount ) {
+ badge = this.utils.formatCount(pageStore.perLoadBlockedRequestCount);
+ }
+ }
+
+ vAPI.setIcon(tabId, state ? 'on' : 'off', badge);
+ };
+
+ return function(tabId) {
+ if ( tabIdToTimer[tabId] ) {
+ return;
+ }
+ if ( vAPI.isBehindTheSceneTabId(tabId) ) {
+ return;
+ }
+ tabIdToTimer[tabId] = vAPI.setTimeout(updateBadge.bind(this, tabId), 500);
+ };
+})();
+
+/******************************************************************************/
+
µb.updateTitle = (function() {
var tabIdToTimer = Object.create(null);
var tabIdToTryCount = Object.create(null);