code review

This commit is contained in:
gorhill 2015-11-29 17:06:58 -05:00
parent ba96a4ff4a
commit 72f5e44ec0
7 changed files with 138 additions and 288 deletions

View File

@ -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>

View File

@ -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);
};
})();

View File

@ -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();
};

View File

@ -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;

View File

@ -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);
}
};

View File

@ -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;
};

View File

@ -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);