mirror of https://github.com/gorhill/uBlock.git
code review
This commit is contained in:
parent
ba96a4ff4a
commit
72f5e44ec0
|
@ -11,7 +11,6 @@
|
|||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-background.js"></script>
|
||||
<script src="js/background.js"></script>
|
||||
<script src="js/async.js"></script>
|
||||
<script src="js/utils.js"></script>
|
||||
<script src="js/uritools.js"></script>
|
||||
<script src="js/assets.js"></script>
|
||||
|
|
203
src/js/async.js
203
src/js/async.js
|
@ -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);
|
||||
};
|
||||
})();
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue