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