uBlock/src/js/async.js

204 lines
6 KiB
JavaScript
Raw Normal View History

2014-06-24 00:42:43 +02:00
/*******************************************************************************
2015-03-07 19:20:18 +01:00
µBlock - a browser extension to block requests.
2014-06-24 00:42:43 +02:00
Copyright (C) 2014 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
2014-10-19 16:44:09 +02:00
/* global µBlock */
'use strict';
2014-06-24 00:42:43 +02:00
/******************************************************************************/
// Async job queue module
µBlock.asyncJobs = (function() {
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
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;
};
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
var AsyncJobManager = function() {
this.timeResolution = 200;
this.jobs = {};
this.jobCount = 0;
this.jobJunkyard = [];
this.timerId = null;
this.timerWhen = Number.MAX_VALUE;
};
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
AsyncJobManager.prototype.restartTimer = function() {
2014-09-14 22:20:40 +02:00
// 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.
2014-06-24 00:42:43 +02:00
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;
2014-09-14 22:20:40 +02:00
// 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
2015-04-10 08:17:12 +02:00
// too busy.
2014-06-24 00:42:43 +02:00
if ( when < this.timerWhen ) {
clearTimeout(this.timerId);
this.timerWhen = when;
this.timerId = setTimeout(processJobs, Math.max(when - Date.now(), 10));
}
};
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
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();
};
2014-09-14 22:20:40 +02:00
/******************************************************************************/
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();
};
/******************************************************************************/
2014-06-24 00:42:43 +02:00
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();
};
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
// Only one instance
var asyncJobManager = new AsyncJobManager();
2014-09-14 22:20:40 +02:00
/******************************************************************************/
2014-06-24 00:42:43 +02:00
// Publish
return asyncJobManager;
})();
/******************************************************************************/
// Update visual of extension icon.
2015-05-01 14:27:41 +02:00
µBlock.updateBadgeAsync = (function() {
2015-05-02 06:13:19 +02:00
var tabIdToTimer = Object.create(null);
2014-09-14 22:20:40 +02:00
var updateBadge = function(tabId) {
2015-05-01 14:27:41 +02:00
delete tabIdToTimer[tabId];
2015-05-02 06:13:19 +02:00
var state = false;
2015-05-01 14:27:41 +02:00
var badge = '';
2015-05-02 06:13:19 +02:00
var pageStore = this.pageStoreFromTabId(tabId);
if ( pageStore !== null ) {
state = pageStore.getNetFilteringSwitch();
if ( state && this.userSettings.showIconBadge && pageStore.perLoadBlockedRequestCount ) {
badge = this.utils.formatCount(pageStore.perLoadBlockedRequestCount);
}
2015-05-01 14:27:41 +02:00
}
2015-05-02 06:13:19 +02:00
vAPI.setIcon(tabId, state ? 'on' : 'off', badge);
2014-06-24 00:42:43 +02:00
};
2014-09-14 22:20:40 +02:00
2015-05-01 14:27:41 +02:00
return function(tabId) {
2015-05-02 06:13:19 +02:00
if ( tabIdToTimer[tabId] ) {
2014-09-14 22:20:40 +02:00
return;
}
2015-05-02 06:13:19 +02:00
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
2015-05-01 14:27:41 +02:00
return;
}
2015-05-02 06:13:19 +02:00
tabIdToTimer[tabId] = setTimeout(updateBadge.bind(this, tabId), 500);
2014-09-14 22:20:40 +02:00
};
2015-03-07 19:20:18 +01:00
})();