diff --git a/platform/common/vapi-common.js b/platform/common/vapi-common.js index 290837584..e5c09ad2b 100644 --- a/platform/common/vapi-common.js +++ b/platform/common/vapi-common.js @@ -35,6 +35,116 @@ vAPI.T0 = Date.now(); vAPI.setTimeout = vAPI.setTimeout || self.setTimeout.bind(self); +vAPI.defer = { + create(callback) { + return new this.Client(callback); + }, + once(delay, ...args) { + const delayInMs = vAPI.defer.normalizeDelay(delay); + return new Promise(resolve => { + vAPI.setTimeout( + (...args) => { resolve(...args); }, + delayInMs, + ...args + ); + }); + }, + Client: class { + constructor(callback) { + this.timer = null; + this.type = 0; + this.callback = callback; + } + on(delay, ...args) { + if ( this.timer !== null ) { return; } + const delayInMs = vAPI.defer.normalizeDelay(delay); + this.type = 0; + this.timer = vAPI.setTimeout(( ) => { + this.timer = null; + this.callback(...args); + }, delayInMs || 1); + } + offon(delay, ...args) { + this.off(); + this.on(delay, ...args); + } + onvsync(delay, ...args) { + if ( this.timer !== null ) { return; } + const delayInMs = vAPI.defer.normalizeDelay(delay); + if ( delayInMs !== 0 ) { + this.type = 0; + this.timer = vAPI.setTimeout(( ) => { + this.timer = null; + this.onraf(...args); + }, delayInMs); + } else { + this.onraf(...args); + } + } + onidle(delay, ...args) { + if ( this.timer !== null ) { return; } + const delayInMs = vAPI.defer.normalizeDelay(delay); + if ( delayInMs !== 0 ) { + this.type = 0; + this.timer = vAPI.setTimeout(( ) => { + this.timer = null; + this.onric(...args); + }, delayInMs); + } else { + this.onric(...args); + } + } + off() { + if ( this.timer === null ) { return; } + switch ( this.type ) { + case 0: + self.clearTimeout(this.timer); + break; + case 1: + self.cancelAnimationFrame(this.timer); + break; + case 2: + self.cancelIdleCallback(this.timer); + break; + default: + break; + } + this.timer = null; + } + onraf(...args) { + if ( this.timer !== null ) { return; } + this.type = 1; + this.timer = requestAnimationFrame(( ) => { + this.timer = null; + this.callback(...args); + }); + } + onric(...args) { + if ( this.timer !== null ) { return; } + this.type = 2; + this.timer = self.requestIdleCallback(deadline => { + this.timer = null; + this.callback(deadline, ...args); + }); + } + ongoing() { + return this.timer !== null; + } + }, + normalizeDelay(delay = 0) { + if ( typeof delay === 'object' ) { + if ( delay.sec !== undefined ) { + return delay.sec * 1000; + } else if ( delay.min !== undefined ) { + return delay.min * 60000; + } else if ( delay.hr !== undefined ) { + return delay.hr * 3600000; + } + } + return delay; + } +}; + /******************************************************************************/ vAPI.webextFlavor = { diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 4a31ace32..84b912c8d 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -56,12 +56,17 @@ let cachedUserFilters = ''; /******************************************************************************/ -// Add auto-complete ability to the editor. +// Add auto-complete ability to the editor. Polling is used as the suggested +// hints also depend on the tabs currently opened. { let hintUpdateToken = 0; - const responseHandler = function(response) { + const getHints = async function() { + const response = await vAPI.messaging.send('dashboard', { + what: 'getAutoCompleteDetails', + hintUpdateToken + }); if ( response instanceof Object === false ) { return; } if ( response.hintUpdateToken !== undefined ) { const mode = cmEditor.getMode(); @@ -73,15 +78,12 @@ let cachedUserFilters = ''; } hintUpdateToken = response.hintUpdateToken; } - vAPI.setTimeout(getHints, 2503); + timer.on(2503); }; - const getHints = function() { - vAPI.messaging.send('dashboard', { - what: 'getAutoCompleteDetails', - hintUpdateToken - }).then(responseHandler); - }; + const timer = vAPI.defer.create(( ) => { + getHints(); + }); getHints(); } @@ -306,8 +308,7 @@ dom.on('#userFiltersRevert', 'click', revertChanges); // https://github.com/gorhill/uBlock/issues/3706 // Save/restore cursor position { - const line = - await vAPI.localStorage.getItemAsync('myFiltersCursorPosition'); + const line = await vAPI.localStorage.getItemAsync('myFiltersCursorPosition'); if ( typeof line === 'number' ) { cmEditor.setCursor(line, 0); } @@ -317,15 +318,14 @@ dom.on('#userFiltersRevert', 'click', revertChanges); // Save/restore cursor position { let curline = 0; - let timer; cmEditor.on('cursorActivity', ( ) => { - if ( timer !== undefined ) { return; } + if ( timer.ongoing() ) { return; } if ( cmEditor.getCursor().line === curline ) { return; } - timer = vAPI.setTimeout(( ) => { - timer = undefined; - curline = cmEditor.getCursor().line; - vAPI.localStorage.setItem('myFiltersCursorPosition', curline); - }, 701); + timer.on(701); + }); + const timer = vAPI.defer.create(( ) => { + curline = cmEditor.getCursor().line; + vAPI.localStorage.setItem('myFiltersCursorPosition', curline); }); } diff --git a/src/js/advanced-settings.js b/src/js/advanced-settings.js index 58b95d1c0..55f1b02f4 100644 --- a/src/js/advanced-settings.js +++ b/src/js/advanced-settings.js @@ -118,19 +118,16 @@ const arrayFromString = function(s) { /******************************************************************************/ const advancedSettingsChanged = (( ) => { - let timer; - const handler = ( ) => { - timer = undefined; - const changed = - hashFromAdvancedSettings(cmEditor.getValue()) !== beforeHash; + const changed = hashFromAdvancedSettings(cmEditor.getValue()) !== beforeHash; qs$('#advancedSettingsApply').disabled = !changed; CodeMirror.commands.save = changed ? applyChanges : function(){}; }; + const timer = vAPI.defer.create(handler); + return function() { - if ( timer !== undefined ) { clearTimeout(timer); } - timer = vAPI.setTimeout(handler, 100); + timer.offon(200); }; })(); diff --git a/src/js/assets.js b/src/js/assets.js index fa6392125..4423f0776 100644 --- a/src/js/assets.js +++ b/src/js/assets.js @@ -73,20 +73,16 @@ assets.fetch = function(url, options = {}) { return new Promise((resolve, reject) => { // Start of executor - const timeoutAfter = µb.hiddenSettings.assetFetchTimeout * 1000 || 30000; + const timeoutAfter = µb.hiddenSettings.assetFetchTimeout || 30; const xhr = new XMLHttpRequest(); let contentLoaded = 0; - let timeoutTimer; const cleanup = function() { xhr.removeEventListener('load', onLoadEvent); xhr.removeEventListener('error', onErrorEvent); xhr.removeEventListener('abort', onErrorEvent); xhr.removeEventListener('progress', onProgressEvent); - if ( timeoutTimer !== undefined ) { - clearTimeout(timeoutTimer); - timeoutTimer = undefined; - } + timeoutTimer.off(); }; const fail = function(details, msg) { @@ -130,12 +126,11 @@ assets.fetch = function(url, options = {}) { const onProgressEvent = function(ev) { if ( ev.loaded === contentLoaded ) { return; } contentLoaded = ev.loaded; - if ( timeoutTimer !== undefined ) { - clearTimeout(timeoutTimer); - } - timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter); + timeoutTimer.offon({ sec: timeoutAfter }); }; + const timeoutTimer = vAPI.defer.create(onTimeout); + // Be ready for thrown exceptions: // I am pretty sure it used to work, but now using a URL such as // `file:///` on Chromium 40 results in an exception being thrown. @@ -147,7 +142,7 @@ assets.fetch = function(url, options = {}) { xhr.addEventListener('progress', onProgressEvent); xhr.responseType = options.responseType || 'text'; xhr.send(); - timeoutTimer = vAPI.setTimeout(onTimeout, timeoutAfter); + timeoutTimer.on({ sec: timeoutAfter }); } catch (e) { onErrorEvent.call(xhr); } @@ -422,17 +417,14 @@ const unregisterAssetSource = function(assetKey) { }; const saveAssetSourceRegistry = (( ) => { - let timer; - const save = function() { - timer = undefined; + const save = ( ) => { + timer.off(); cacheStorage.set({ assetSourceRegistry }); }; + const timer = vAPI.defer.create(save); return function(lazily) { - if ( timer !== undefined ) { - clearTimeout(timer); - } if ( lazily ) { - timer = vAPI.setTimeout(save, 500); + timer.offon(500); } else { save(); } @@ -533,15 +525,14 @@ const getAssetCacheRegistry = function() { }; const saveAssetCacheRegistry = (( ) => { - let timer; const save = function() { - timer = undefined; + timer.off(); cacheStorage.set({ assetCacheRegistry }); }; + const timer = vAPI.defer.create(save); return function(lazily) { - if ( timer !== undefined ) { clearTimeout(timer); } if ( lazily ) { - timer = vAPI.setTimeout(save, 30000); + timer.offon({ sec: 30 }); } else { save(); } @@ -961,7 +952,6 @@ const updaterUpdated = []; const updaterFetched = new Set(); let updaterStatus; -let updaterTimer; let updaterAssetDelay = updaterAssetDelayDefault; let updaterAuto = false; @@ -1043,9 +1033,11 @@ const updateNext = async function() { fireNotification('asset-update-failed', { assetKey: result.assetKey }); } - vAPI.setTimeout(updateNext, updaterAssetDelay); + updaterTimer.on(updaterAssetDelay); }; +const updaterTimer = vAPI.defer.create(updateNext); + const updateDone = function() { const assetKeys = updaterUpdated.slice(0); updaterFetched.clear(); @@ -1064,8 +1056,7 @@ assets.updateStart = function(details) { updaterAuto = details.auto === true; if ( updaterStatus !== undefined ) { if ( newUpdateDelay < oldUpdateDelay ) { - clearTimeout(updaterTimer); - updaterTimer = vAPI.setTimeout(updateNext, updaterAssetDelay); + updaterTimer.offon(updaterAssetDelay); } return; } @@ -1073,10 +1064,7 @@ assets.updateStart = function(details) { }; assets.updateStop = function() { - if ( updaterTimer ) { - clearTimeout(updaterTimer); - updaterTimer = undefined; - } + updaterTimer.off(); if ( updaterStatus !== undefined ) { updateDone(); } diff --git a/src/js/background.js b/src/js/background.js index 402500694..4d43a1706 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -172,7 +172,6 @@ const µBlock = { // jshint ignore:line allowedRequestCount: 0, }, localSettingsLastModified: 0, - localSettingsLastSaved: 0, // Read-only systemSettings: { diff --git a/src/js/benchmarks.js b/src/js/benchmarks.js index 54a98fcb2..802aacd73 100644 --- a/src/js/benchmarks.js +++ b/src/js/benchmarks.js @@ -69,18 +69,13 @@ import { const loadBenchmarkDataset = (( ) => { let datasetPromise; - let ttlTimer; + + const ttlTimer = vAPI.defer.create(( ) => { + datasetPromise = undefined; + }); return function() { - if ( ttlTimer !== undefined ) { - clearTimeout(ttlTimer); - ttlTimer = undefined; - } - - setTimeout(( ) => { - ttlTimer = undefined; - datasetPromise = undefined; - }, 5 * 60 * 1000); + ttlTimer.offon({ min: 5 }); if ( datasetPromise !== undefined ) { return datasetPromise; diff --git a/src/js/cachestorage.js b/src/js/cachestorage.js index 78a87e207..88b2c67ee 100644 --- a/src/js/cachestorage.js +++ b/src/js/cachestorage.js @@ -101,36 +101,27 @@ const cacheStorage = { const selectIDB = async function() { let db; let dbPromise; - let dbTimer; const noopfn = function () { }; const disconnect = function() { - if ( dbTimer !== undefined ) { - clearTimeout(dbTimer); - dbTimer = undefined; - } + dbTimer.off(); if ( db instanceof IDBDatabase ) { db.close(); db = undefined; } }; + const dbTimer = vAPI.defer.create(( ) => { + disconnect(); + }); + const keepAlive = function() { - if ( dbTimer !== undefined ) { - clearTimeout(dbTimer); - } - dbTimer = vAPI.setTimeout( - ( ) => { - dbTimer = undefined; - disconnect(); - }, - Math.max( - µb.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000, - 180000 - ) - ); + dbTimer.offon(Math.max( + µb.hiddenSettings.autoUpdateAssetFetchPeriod * 2 * 1000, + 180000 + )); }; // https://github.com/gorhill/uBlock/issues/3156 @@ -192,13 +183,13 @@ const selectIDB = async function() { resolve(null); resolve = undefined; }; - setTimeout(( ) => { + vAPI.defer.once(5000).then(( ) => { if ( resolve === undefined ) { return; } db = null; dbPromise = undefined; resolve(null); resolve = undefined; - }, 5000); + }); }); return dbPromise; }; diff --git a/src/js/code-viewer.js b/src/js/code-viewer.js index 5b78a311b..6f6cdb55b 100644 --- a/src/js/code-viewer.js +++ b/src/js/code-viewer.js @@ -211,7 +211,7 @@ async function setURL(resourceURL) { dom.attr(a, 'title', afterURL); addPastURLs(afterURL); // For unknown reasons, calling focus() synchronously does not work... - vAPI.setTimeout(( ) => { cmEditor.focus(); }, 1); + vAPI.defer.once(1).then(( ) => { cmEditor.focus(); }); } /******************************************************************************/ diff --git a/src/js/codemirror/search.js b/src/js/codemirror/search.js index 882e7afd4..1335d816d 100644 --- a/src/js/codemirror/search.js +++ b/src/js/codemirror/search.js @@ -86,16 +86,7 @@ import { i18n$ } from '../i18n.js'; return; } if ( queryTextFromSearchWidget(cm) === state.queryText ) { return; } - if ( state.queryTimer !== null ) { - clearTimeout(state.queryTimer); - } - state.queryTimer = setTimeout( - () => { - state.queryTimer = null; - findCommit(cm, 0); - }, - 350 - ); + state.queryTimer.offon(350); }; const searchWidgetClickHandler = function(cm, ev) { @@ -141,7 +132,6 @@ import { i18n$ } from '../i18n.js'; this.panel = cm.addPanel(this.widget); } this.queryText = ''; - this.queryTimer = null; this.dirty = true; this.lines = []; cm.on('changes', (cm, changes) => { @@ -155,6 +145,9 @@ import { i18n$ } from '../i18n.js'; cm.on('cursorActivity', cm => { updateCount(cm); }); + this.queryTimer = vAPI.defer.create(( ) => { + findCommit(cm, 0); + }); }; // We want the search widget to behave as if the focus was on the @@ -426,10 +419,7 @@ import { i18n$ } from '../i18n.js'; const findCommit = function(cm, dir) { const state = getSearchState(cm); - if ( state.queryTimer !== null ) { - clearTimeout(state.queryTimer); - state.queryTimer = null; - } + state.queryTimer.off(); const queryText = queryTextFromSearchWidget(cm); if ( queryText === state.queryText ) { return; } state.queryText = queryText; diff --git a/src/js/commands.js b/src/js/commands.js index de9c6c669..4bfcf9f2b 100644 --- a/src/js/commands.js +++ b/src/js/commands.js @@ -111,19 +111,15 @@ const relaxBlockingMode = (( ) => { if ( noReload ) { return; } // Reload: use a timer to coalesce bursts of reload commands. - let timer = reloadTimers.get(tab.id); - if ( timer !== undefined ) { - clearTimeout(timer); - } - timer = vAPI.setTimeout( - tabId => { + const timer = reloadTimers.get(tab.id) || (( ) => { + const t = vAPI.defer.create(tabId => { reloadTimers.delete(tabId); vAPI.tabs.reload(tabId); - }, - 547, - tab.id - ); - reloadTimers.set(tab.id, timer); + }); + reloadTimers.set(tab.id, t); + return t; + })(); + timer.offon(547, tab.id); }; })(); diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js index 2cd7ad854..9509833cc 100644 --- a/src/js/cosmetic-filtering.js +++ b/src/js/cosmetic-filtering.js @@ -225,10 +225,12 @@ const FilterContainer = function() { this.reSimpleHighGeneric = /^(?:[a-z]*\[[^\]]+\]|\S+)$/; this.selectorCache = new Map(); - this.selectorCachePruneDelay = 10 * 60 * 1000; // 10 minutes + this.selectorCachePruneDelay = 10; // 10 minutes this.selectorCacheCountMin = 40; this.selectorCacheCountMax = 50; - this.selectorCacheTimer = null; + this.selectorCacheTimer = vAPI.defer.create(( ) => { + this.pruneSelectorCacheAsync(); + }); // specific filters this.specificFilters = new StaticExtFilteringHostnameDB(2); @@ -274,10 +276,7 @@ FilterContainer.prototype.reset = function() { this.duplicateBuster = new Set(); this.selectorCache.clear(); - if ( this.selectorCacheTimer !== null ) { - clearTimeout(this.selectorCacheTimer); - this.selectorCacheTimer = null; - } + this.selectorCacheTimer.off(); // hostname, entity-based filters this.specificFilters.clear(); @@ -604,18 +603,6 @@ FilterContainer.prototype.fromSelfie = function(selfie) { /******************************************************************************/ -FilterContainer.prototype.triggerSelectorCachePruner = function() { - // Of interest: http://fitzgeraldnick.com/weblog/40/ - // http://googlecode.blogspot.ca/2009/07/gmail-for-mobile-html5-series-using.html - if ( this.selectorCacheTimer !== null ) { return; } - this.selectorCacheTimer = vAPI.setTimeout( - ( ) => { this.pruneSelectorCacheAsync(); }, - this.selectorCachePruneDelay - ); -}; - -/******************************************************************************/ - FilterContainer.prototype.addToSelectorCache = function(details) { const hostname = details.hostname; if ( typeof hostname !== 'string' || hostname === '' ) { return; } @@ -626,7 +613,7 @@ FilterContainer.prototype.addToSelectorCache = function(details) { entry = SelectorCacheEntry.factory(); this.selectorCache.set(hostname, entry); if ( this.selectorCache.size > this.selectorCacheCountMax ) { - this.triggerSelectorCachePruner(); + this.selectorCacheTimer.on({ min: this.selectorCachePruneDelay }); } } entry.add(details); @@ -658,7 +645,6 @@ FilterContainer.prototype.removeFromSelectorCache = function( /******************************************************************************/ FilterContainer.prototype.pruneSelectorCacheAsync = function() { - this.selectorCacheTimer = null; if ( this.selectorCache.size <= this.selectorCacheCountMax ) { return; } const cache = this.selectorCache; const hostnames = Array.from(cache.keys()) diff --git a/src/js/dashboard-common.js b/src/js/dashboard-common.js index 616eb27a2..fa18f6bb5 100644 --- a/src/js/dashboard-common.js +++ b/src/js/dashboard-common.js @@ -113,18 +113,18 @@ self.uBlockDashboard.dateNowToSensibleString = function() { /******************************************************************************/ self.uBlockDashboard.patchCodeMirrorEditor = (function() { - let grabFocusTimer; let grabFocusTarget; const grabFocus = function() { grabFocusTarget.focus(); - grabFocusTimer = grabFocusTarget = undefined; + grabFocusTarget = undefined; }; + + const grabFocusTimer = vAPI.defer.create(grabFocus); + const grabFocusAsync = function(cm) { grabFocusTarget = cm; - if ( grabFocusTimer === undefined ) { - grabFocusTimer = vAPI.setTimeout(grabFocus, 1); - } + grabFocusTimer.on(1); }; // https://github.com/gorhill/uBlock/issues/3646 diff --git a/src/js/dashboard.js b/src/js/dashboard.js index 8a53efa21..a88675957 100644 --- a/src/js/dashboard.js +++ b/src/js/dashboard.js @@ -104,19 +104,19 @@ if ( self.location.hash.slice(1) === 'no-dashboard.html' ) { (async ( ) => { // Wait for uBO's main process to be ready await new Promise(resolve => { - const check = ( ) => { - vAPI.messaging.send('dashboard', { - what: 'readyToFilter' - }).then(response => { + const check = async ( ) => { + try { + const response = await vAPI.messaging.send('dashboard', { + what: 'readyToFilter' + }); if ( response ) { return resolve(true); } const iframe = qs$('#iframe'); if ( iframe.src !== '' ) { iframe.src = ''; } - vAPI.setTimeout(check, 250); - }).catch(( ) => { - vAPI.setTimeout(check, 250); - }); + } catch(ex) { + } + vAPI.defer.once(250).then(( ) => check()); }; check(); }); diff --git a/src/js/logger-ui-inspector.js b/src/js/logger-ui-inspector.js index 1c493331d..c423c3693 100644 --- a/src/js/logger-ui-inspector.js +++ b/src/js/logger-ui-inspector.js @@ -336,39 +336,35 @@ const startDialog = (function() { let textarea; let hideSelectors = []; let unhideSelectors = []; - let inputTimer; - const onInputChanged = (function() { - const parse = function() { - inputTimer = undefined; - hideSelectors = []; - unhideSelectors = []; + const parse = function() { + hideSelectors = []; + unhideSelectors = []; - const re = /^([^#]*)(#@?#)(.+)$/; - for ( let line of textarea.value.split(/\s*\n\s*/) ) { - line = line.trim(); - if ( line === '' || line.charAt(0) === '!' ) { continue; } - const matches = re.exec(line); - if ( matches === null || matches.length !== 4 ) { continue; } - if ( inspectedHostname.lastIndexOf(matches[1]) === -1 ) { - continue; - } - if ( matches[2] === '##' ) { - hideSelectors.push(matches[3]); - } else { - unhideSelectors.push(matches[3]); - } + const re = /^([^#]*)(#@?#)(.+)$/; + for ( let line of textarea.value.split(/\s*\n\s*/) ) { + line = line.trim(); + if ( line === '' || line.charAt(0) === '!' ) { continue; } + const matches = re.exec(line); + if ( matches === null || matches.length !== 4 ) { continue; } + if ( inspectedHostname.lastIndexOf(matches[1]) === -1 ) { + continue; } - - showCommitted(); - }; - - return function parseAsync() { - if ( inputTimer === undefined ) { - inputTimer = vAPI.setTimeout(parse, 743); + if ( matches[2] === '##' ) { + hideSelectors.push(matches[3]); + } else { + unhideSelectors.push(matches[3]); } - }; - })(); + } + + showCommitted(); + }; + + const inputTimer = vAPI.defer.create(parse); + + const onInputChanged = ( ) => { + inputTimer.on(743); + }; const onClicked = function(ev) { var target = ev.target; @@ -433,10 +429,7 @@ const startDialog = (function() { }; const stop = function() { - if ( inputTimer !== undefined ) { - clearTimeout(inputTimer); - inputTimer = undefined; - } + inputTimer.off(); showInteractive(); textarea.removeEventListener('input', onInputChanged); dialog.removeEventListener('click', onClicked, true); @@ -513,11 +506,9 @@ const onClicked = function(ev) { /******************************************************************************/ const onMouseOver = (function() { - var mouseoverTarget = null; - var mouseoverTimer = null; + let mouseoverTarget = null; - var timerHandler = function() { - mouseoverTimer = null; + const timerHandler = ( ) => { vAPI.MessagingConnection.sendTo(inspectorConnectionId, { what: 'highlightOne', selector: selectorFromNode(mouseoverTarget), @@ -526,21 +517,17 @@ const onMouseOver = (function() { }); }; + const mouseoverTimer = vAPI.defer.create(timerHandler); + return function(ev) { if ( inspectedTabId === 0 ) { return; } // Convenience: skip real-time highlighting if shift key is pressed. if ( ev.shiftKey ) { return; } // Find closest `li` - var target = ev.target; - while ( target !== null ) { - if ( target.localName === 'li' ) { break; } - target = target.parentElement; - } + const target = ev.target.closest('li'); if ( target === mouseoverTarget ) { return; } mouseoverTarget = target; - if ( mouseoverTimer === null ) { - mouseoverTimer = vAPI.setTimeout(timerHandler, 50); - } + mouseoverTimer.on(50); }; })(); diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 7452f454a..00002c3a5 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -567,8 +567,6 @@ const viewPort = (( ) => { let wholeHeight = 0; let lastTopPix = 0; let lastTopRow = 0; - let scrollTimer; - let resizeTimer; const ViewEntry = function() { this.div = document.createElement('div'); @@ -602,19 +600,10 @@ const viewPort = (( ) => { }; // Coalesce scroll events - const onScroll = function() { - if ( scrollTimer !== undefined ) { return; } - scrollTimer = setTimeout( - ( ) => { - scrollTimer = requestAnimationFrame(( ) => { - scrollTimer = undefined; - onScrollChanged(); - }); - }, - 1000/32 - ); + const scrollTimer = vAPI.defer.create(onScrollChanged); + const onScroll = ( ) => { + scrollTimer.onvsync(1000/32); }; - dom.on(vwScroller, 'scroll', onScroll, { passive: true }); const onLayoutChanged = function() { @@ -721,19 +710,10 @@ const viewPort = (( ) => { updateContent(0); }; + const resizeTimer = vAPI.defer.create(onLayoutChanged); const updateLayout = function() { - if ( resizeTimer !== undefined ) { return; } - resizeTimer = setTimeout( - ( ) => { - resizeTimer = requestAnimationFrame(( ) => { - resizeTimer = undefined; - onLayoutChanged(); - }); - }, - 1000/8 - ); + resizeTimer.onvsync(1000/8); }; - dom.on(window, 'resize', updateLayout, { passive: true }); updateLayout(); @@ -1128,10 +1108,13 @@ const onLogBufferRead = function(response) { /******************************************************************************/ const readLogBuffer = (( ) => { - let timer; + let reading = false; const readLogBufferNow = async function() { if ( logger.ownerId === undefined ) { return; } + if ( reading ) { return; } + + reading = true; const msg = { what: 'readAll', @@ -1159,20 +1142,20 @@ const readLogBuffer = (( ) => { const response = await vAPI.messaging.send('loggerUI', msg); - timer = undefined; onLogBufferRead(response); - readLogBufferLater(); + + reading = false; + + timer.on(1200); }; - const readLogBufferLater = function() { - if ( timer !== undefined ) { return; } - if ( logger.ownerId === undefined ) { return; } - timer = vAPI.setTimeout(readLogBufferNow, 1200); - }; + const timer = vAPI.defer.create(readLogBufferNow); readLogBufferNow(); - return readLogBufferLater; + return ( ) => { + timer.on(1200); + }; })(); /******************************************************************************/ @@ -2203,17 +2186,13 @@ const rowFilterer = (( ) => { }; const onFilterChangedAsync = (( ) => { - let timer; const commit = ( ) => { - timer = undefined; parseInput(); filterAll(); }; + const timer = vAPI.defer.create(commit); return ( ) => { - if ( timer !== undefined ) { - clearTimeout(timer); - } - timer = vAPI.setTimeout(commit, 750); + timer.offon(750); }; })(); @@ -2290,7 +2269,7 @@ const rowJanitor = (( ) => { let rowIndex = 0; - const discard = function(timeRemaining) { + const discard = function(deadline) { const opts = loggerSettings.discard; const maxLoadCount = typeof opts.maxLoadCount === 'number' ? opts.maxLoadCount @@ -2301,7 +2280,6 @@ const rowJanitor = (( ) => { const obsolete = typeof opts.maxAge === 'number' ? Date.now() - opts.maxAge * 60000 : 0; - const deadline = Date.now() + Math.ceil(timeRemaining); let i = rowIndex; // TODO: below should not happen -- remove when confirmed. @@ -2322,7 +2300,7 @@ const rowJanitor = (( ) => { while ( i < loggerEntries.length ) { - if ( i % 64 === 0 && Date.now() >= deadline ) { break; } + if ( i % 64 === 0 && deadline.timeRemaining() === 0 ) { break; } const entry = loggerEntries[i]; const tabId = entry.tabId || 0; @@ -2391,18 +2369,15 @@ const rowJanitor = (( ) => { rowFilterer.filterAll(); }; - const discardAsync = function() { - setTimeout( - ( ) => { - self.requestIdleCallback(deadline => { - discard(deadline.timeRemaining()); - discardAsync(); - }); - }, - 1889 - ); + const discardAsync = function(deadline) { + if ( deadline ) { + discard(deadline); + } + janitorTimer.onidle(1889); }; + const janitorTimer = vAPI.defer.create(discardAsync); + // Clear voided entries from the logger's visible content. // // Voided entries should be visible only from the "All" option of the @@ -3030,15 +3005,14 @@ dom.on(window, 'hashchange', pageSelectorFromURLHash); // to the window geometry pontentially not settling fast enough. if ( self.location.search.includes('popup=1') ) { dom.on(window, 'load', ( ) => { - setTimeout( - ( ) => { - popupLoggerBox = { - x: self.screenX, - y: self.screenY, - w: self.outerWidth, - h: self.outerHeight, - }; - }, 2000); + vAPI.defer.once(2000).then(( ) => { + popupLoggerBox = { + x: self.screenX, + y: self.screenY, + w: self.outerWidth, + h: self.outerHeight, + }; + }); }, { once: true }); } diff --git a/src/js/logger.js b/src/js/logger.js index 9318621b2..7ff3a3312 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -27,21 +27,21 @@ let buffer = null; let lastReadTime = 0; let writePtr = 0; -// After 30 seconds without being read, a buffer will be considered -// unused, and thus removed from memory. +// After 30 seconds without being read, the logger buffer will be considered +// unused, and thus disabled. const logBufferObsoleteAfter = 30 * 1000; -const janitor = ( ) => { +const janitorTimer = vAPI.defer.create(( ) => { if ( buffer === null ) { return; } if ( lastReadTime >= (Date.now() - logBufferObsoleteAfter) ) { - return vAPI.setTimeout(janitor, logBufferObsoleteAfter); + return janitorTimer.on(logBufferObsoleteAfter); } logger.enabled = false; buffer = null; writePtr = 0; logger.ownerId = undefined; vAPI.messaging.broadcast({ what: 'loggerDisabled' }); -}; +}); const boxEntry = function(details) { if ( details.tstamp === undefined ) { @@ -68,7 +68,7 @@ const logger = { if ( buffer === null ) { this.enabled = true; buffer = []; - vAPI.setTimeout(janitor, logBufferObsoleteAfter); + janitorTimer.on(logBufferObsoleteAfter); } const out = buffer.slice(0, writePtr); writePtr = 0; diff --git a/src/js/lz4.js b/src/js/lz4.js index 0c91b2f76..51021d16d 100644 --- a/src/js/lz4.js +++ b/src/js/lz4.js @@ -42,7 +42,6 @@ let lz4CodecInstance; let pendingInitialization; let textEncoder, textDecoder; let ttlCount = 0; -let ttlTimer; let ttlDelay = 60000; const init = function() { @@ -84,18 +83,16 @@ const destroy = function() { lz4CodecInstance = undefined; textEncoder = textDecoder = undefined; ttlCount = 0; - ttlTimer = undefined; }; +const ttlTimer = vAPI.defer.create(destroy); + const ttlManage = function(count) { - if ( ttlTimer !== undefined ) { - clearTimeout(ttlTimer); - ttlTimer = undefined; - } + ttlTimer.off(); ttlCount += count; if ( ttlCount > 0 ) { return; } if ( lz4CodecInstance === null ) { return; } - ttlTimer = vAPI.setTimeout(destroy, ttlDelay); + ttlTimer.on(ttlDelay); }; const encodeValue = function(dataIn) { diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 4e7b1f0a6..7dd3eb69c 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -56,6 +56,9 @@ To create a log of net requests const NetFilteringResultCache = class { constructor() { + this.pruneTimer = vAPI.defer.create(( ) => { + this.prune(); + }); this.init(); } @@ -63,7 +66,6 @@ const NetFilteringResultCache = class { this.blocked = new Map(); this.results = new Map(); this.hash = 0; - this.timer = undefined; return this; } @@ -111,10 +113,7 @@ const NetFilteringResultCache = class { this.blocked.clear(); this.results.clear(); this.hash = 0; - if ( this.timer !== undefined ) { - clearTimeout(this.timer); - this.timer = undefined; - } + this.pruneTimer.off(); } prune() { @@ -136,14 +135,7 @@ const NetFilteringResultCache = class { } pruneAsync() { - if ( this.timer !== undefined ) { return; } - this.timer = vAPI.setTimeout( - ( ) => { - this.timer = undefined; - this.prune(); - }, - this.shelfLife - ); + this.pruneTimer.on(this.shelfLife); } lookupResult(fctxt) { @@ -353,12 +345,17 @@ const PageStore = class { constructor(tabId, details) { this.extraData = new Map(); this.journal = []; - this.journalTimer = undefined; this.journalLastCommitted = this.journalLastUncommitted = -1; this.journalLastUncommittedOrigin = undefined; this.netFilteringCache = NetFilteringResultCache.factory(); this.hostnameDetailsMap = new HostnameDetailsMap(); this.counts = new CountDetails(); + this.journalTimer = vAPI.defer.create(( ) => { + this.journalProcess(); + }); + this.largeMediaTimer = vAPI.defer.create(( ) => { + this.injectLargeMediaElementScriptlet(); + }); this.init(tabId, details); } @@ -398,7 +395,6 @@ const PageStore = class { this.remoteFontCount = 0; this.popupBlockedCount = 0; this.largeMediaCount = 0; - this.largeMediaTimer = null; this.allowLargeMediaElementsRegex = undefined; this.extraData.clear(); @@ -441,10 +437,7 @@ const PageStore = class { } // A new page is completely reloaded from scratch, reset all. - if ( this.largeMediaTimer !== null ) { - clearTimeout(this.largeMediaTimer); - this.largeMediaTimer = null; - } + this.largeMediaTimer.off(); this.disposeFrameStores(); this.init(this.tabId, details); return this; @@ -458,15 +451,9 @@ const PageStore = class { this.netFilteringCache.empty(); this.allowLargeMediaElementsUntil = Date.now(); this.allowLargeMediaElementsRegex = undefined; - if ( this.largeMediaTimer !== null ) { - clearTimeout(this.largeMediaTimer); - this.largeMediaTimer = null; - } + this.largeMediaTimer.off(); this.disposeFrameStores(); - if ( this.journalTimer !== undefined ) { - clearTimeout(this.journalTimer); - this.journalTimer = undefined; - } + this.journalTimer.off(); this.journal = []; this.journalLastUncommittedOrigin = undefined; this.journalLastCommitted = this.journalLastUncommitted = -1; @@ -668,11 +655,7 @@ const PageStore = class { const hostname = fctxt.getHostname(); if ( hostname === '' ) { return; } this.journal.push(hostname, result, fctxt.itype); - if ( this.journalTimer !== undefined ) { return; } - this.journalTimer = vAPI.setTimeout( - ( ) => { this.journalProcess(true); }, - µb.hiddenSettings.requestJournalProcessPeriod - ); + this.journalTimer.on(µb.hiddenSettings.requestJournalProcessPeriod); } journalAddRootFrame(type, url) { @@ -695,18 +678,11 @@ const PageStore = class { this.journalLastUncommittedOrigin = newOrigin; } } - if ( this.journalTimer !== undefined ) { - clearTimeout(this.journalTimer); - } - this.journalTimer = vAPI.setTimeout( - ( ) => { this.journalProcess(true); }, - µb.hiddenSettings.requestJournalProcessPeriod - ); + this.journalTimer.offon(µb.hiddenSettings.requestJournalProcessPeriod); } - journalProcess(fromTimer = false) { - if ( fromTimer === false ) { clearTimeout(this.journalTimer); } - this.journalTimer = undefined; + journalProcess() { + this.journalTimer.off(); const journal = this.journal; const pivot = Math.max(0, this.journalLastCommitted); @@ -1058,12 +1034,7 @@ const PageStore = class { } this.largeMediaCount += 1; - if ( this.largeMediaTimer === null ) { - this.largeMediaTimer = vAPI.setTimeout(( ) => { - this.largeMediaTimer = null; - this.injectLargeMediaElementScriptlet(); - }, 500); - } + this.largeMediaTimer.on(500); if ( logger.enabled ) { fctxt.filter = sessionSwitches.toLogData(); diff --git a/src/js/popup-fenix.js b/src/js/popup-fenix.js index bd22a20ae..1849a7aa9 100644 --- a/src/js/popup-fenix.js +++ b/src/js/popup-fenix.js @@ -1376,29 +1376,23 @@ const toggleHostnameSwitch = async function(ev) { // it and thus having to push it all the time unconditionally. const pollForContentChange = (( ) => { - let pollTimer; - const pollCallback = async function() { - pollTimer = undefined; const response = await messaging.send('popupPanel', { what: 'hasPopupContentChanged', tabId: popupData.tabId, contentLastModified: popupData.contentLastModified, }); - queryCallback(response); - }; - - const queryCallback = function(response) { if ( response ) { - getPopupData(popupData.tabId); + await getPopupData(popupData.tabId); return; } poll(); }; + const pollTimer = vAPI.defer.create(pollCallback); + const poll = function() { - if ( pollTimer !== undefined ) { return; } - pollTimer = vAPI.setTimeout(pollCallback, 1500); + pollTimer.on(1500); }; return poll; diff --git a/src/js/reverselookup.js b/src/js/reverselookup.js index d79fdc31f..3ed82aff3 100644 --- a/src/js/reverselookup.js +++ b/src/js/reverselookup.js @@ -36,11 +36,9 @@ import { /******************************************************************************/ -const workerTTL = 5 * 60 * 1000; const pendingResponses = new Map(); let worker = null; -let workerTTLTimer; let needLists = true; let messageId = 1; @@ -52,10 +50,7 @@ const onWorkerMessage = function(e) { }; const stopWorker = function() { - if ( workerTTLTimer !== undefined ) { - clearTimeout(workerTTLTimer); - workerTTLTimer = undefined; - } + stopWorker.off(); if ( worker === null ) { return; } worker.terminate(); worker = null; @@ -66,6 +61,9 @@ const stopWorker = function() { pendingResponses.clear(); }; +const workerTTLTimer = vAPI.defer.create(stopWorker); +const workerTTL = { min: 5 }; + const initWorker = function() { if ( worker === null ) { worker = new Worker('js/reverselookup-worker.js'); @@ -73,10 +71,7 @@ const initWorker = function() { } // The worker will be shutdown after n minutes without being used. - if ( workerTTLTimer !== undefined ) { - clearTimeout(workerTTLTimer); - } - workerTTLTimer = vAPI.setTimeout(stopWorker, workerTTL); + workerTTLTimer.offon(workerTTL); if ( needLists === false ) { return Promise.resolve(); diff --git a/src/js/storage.js b/src/js/storage.js index 23f1b3e87..b188dfd14 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -98,23 +98,26 @@ import { /******************************************************************************/ -µb.saveLocalSettings = (( ) => { - const saveAfter = 4 * 60 * 1000; +{ + let localSettingsLastSaved = Date.now(); - const onTimeout = ( ) => { - if ( µb.localSettingsLastModified > µb.localSettingsLastSaved ) { + const shouldSave = ( ) => { + if ( µb.localSettingsLastModified > localSettingsLastSaved ) { µb.saveLocalSettings(); } - vAPI.setTimeout(onTimeout, saveAfter); + saveTimer.on(saveDelay); }; - vAPI.setTimeout(onTimeout, saveAfter); + const saveTimer = vAPI.defer.create(shouldSave); + const saveDelay = { min: 4 }; - return function() { - this.localSettingsLastSaved = Date.now(); + saveTimer.on(saveDelay); + + µb.saveLocalSettings = function() { + localSettingsLastSaved = Date.now(); return vAPI.storage.set(this.localSettings); }; -})(); +} /******************************************************************************/ @@ -1445,15 +1448,17 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { /******************************************************************************/ { - let timer, next = 0; + let next = 0; let lastEmergencyUpdate = 0; - µb.scheduleAssetUpdater = async function(updateDelay) { + const launchTimer = vAPI.defer.create(fetchDelay => { + next = 0; + io.updateStart({ delay: fetchDelay, auto: true }); + }); + + µb.scheduleAssetUpdater = async function(updateDelay) { + launchTimer.off(); - if ( timer ) { - clearTimeout(timer); - timer = undefined; - } if ( updateDelay === 0 ) { next = 0; return; @@ -1499,11 +1504,7 @@ self.addEventListener('hiddenSettingsChanged', ( ) => { ? 2000 : this.hiddenSettings.autoUpdateAssetFetchPeriod * 1000 || 60000; - timer = vAPI.setTimeout(( ) => { - timer = undefined; - next = 0; - io.updateStart({ delay: fetchDelay, auto: true }); - }, updateDelay); + launchTimer.on(updateDelay, fetchDelay); }; } diff --git a/src/js/tab.js b/src/js/tab.js index 5ae990a6b..978ab6446 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -515,25 +515,19 @@ housekeep itself. ? µb.maybeGoodPopup.url : '' }; - this.selfDestructionTimer = null; + this.selfDestructionTimer = vAPI.defer.create(( ) => { + this.destroy(); + }); this.launchSelfDestruction(); } destroy() { - if ( this.selfDestructionTimer !== null ) { - clearTimeout(this.selfDestructionTimer); - } + this.selfDestructionTimer.off(); popupCandidates.delete(this.targetTabId); } launchSelfDestruction() { - if ( this.selfDestructionTimer !== null ) { - clearTimeout(this.selfDestructionTimer); - } - this.selfDestructionTimer = vAPI.setTimeout( - ( ) => this.destroy(), - 10000 - ); + this.selfDestructionTimer.offon(10000); } }; @@ -618,8 +612,12 @@ housekeep itself. this.origin = this.rootHostname = this.rootDomain = ''; - this.commitTimer = null; - this.gcTimer = null; + this.commitTimer = vAPI.defer.create(( ) => { + this.onCommit(); + }); + this.gcTimer = vAPI.defer.create(( ) => { + this.onGC(); + }); this.onGCBarrier = false; this.netFiltering = true; this.netFilteringReadTime = 0; @@ -629,28 +627,20 @@ housekeep itself. TabContext.prototype.destroy = function() { if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; } - if ( this.gcTimer !== null ) { - clearTimeout(this.gcTimer); - this.gcTimer = null; - } + this.gcTimer.off(); tabContexts.delete(this.tabId); }; TabContext.prototype.onGC = async function() { if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; } - // https://github.com/gorhill/uBlock/issues/1713 - // For unknown reasons, Firefox's setTimeout() will sometimes - // causes the callback function to be called immediately, bypassing - // the main event loop. For now this should prevent uBO from - // crashing as a result of the bad setTimeout() behavior. if ( this.onGCBarrier ) { return; } this.onGCBarrier = true; - this.gcTimer = null; + this.gcTimer.off(); const tab = await vAPI.tabs.get(this.tabId); if ( tab instanceof Object === false || tab.discarded === true ) { this.destroy(); } else { - this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod); + this.gcTimer.on(gcPeriod); } this.onGCBarrier = false; }; @@ -659,10 +649,8 @@ housekeep itself. // Stack entries have to be committed to stick. Non-committed stack // entries are removed after a set delay. TabContext.prototype.onCommit = function() { - if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { - return; - } - this.commitTimer = null; + if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; } + this.commitTimer.off(); // Remove uncommitted entries at the top of the stack. let i = this.stack.length; while ( i-- ) { @@ -687,7 +675,7 @@ housekeep itself. // want to flush it. TabContext.prototype.autodestroy = function() { if ( vAPI.isBehindTheSceneTabId(this.tabId) ) { return; } - this.gcTimer = vAPI.setTimeout(( ) => this.onGC(), gcPeriod); + this.gcTimer.on(gcPeriod); }; // Update just force all properties to be updated to match the most recent @@ -727,10 +715,7 @@ housekeep itself. this.stack.push(new StackEntry(url)); this.update(); popupCandidateTest(this.tabId); - if ( this.commitTimer !== null ) { - clearTimeout(this.commitTimer); - } - this.commitTimer = vAPI.setTimeout(( ) => this.onCommit(), 500); + this.commitTimer.offon(500); }; // This tells that the url is definitely the one to be associated with the @@ -1156,7 +1141,6 @@ vAPI.tabs = new vAPI.Tabs(); // Stale page store entries janitor { - const pageStoreJanitorPeriod = 15 * 60 * 1000; let pageStoreJanitorSampleAt = 0; let pageStoreJanitorSampleSize = 10; @@ -1182,10 +1166,13 @@ vAPI.tabs = new vAPI.Tabs(); } pageStoreJanitorSampleAt = n; - vAPI.setTimeout(pageStoreJanitor, pageStoreJanitorPeriod); + pageStoreJanitorTimer.on(pageStoreJanitorPeriod); }; - vAPI.setTimeout(pageStoreJanitor, pageStoreJanitorPeriod); + const pageStoreJanitorTimer = vAPI.defer.create(pageStoreJanitor); + const pageStoreJanitorPeriod = { min: 15 }; + + pageStoreJanitorTimer.on(pageStoreJanitorPeriod); } /******************************************************************************/ diff --git a/src/js/traffic.js b/src/js/traffic.js index 6b0531474..1ec48e780 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -439,23 +439,23 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { // request. { + const pageStores = new Set(); let hostname = ''; - let pageStores = new Set(); let pageStoresToken = 0; - let gcTimer; const reset = function() { hostname = ''; - pageStores = new Set(); + pageStores.clear(); pageStoresToken = 0; }; const gc = ( ) => { - gcTimer = undefined; if ( pageStoresToken !== µb.pageStoresToken ) { return reset(); } - gcTimer = vAPI.setTimeout(gc, 30011); + gcTimer.on(30011); }; + const gcTimer = vAPI.defer.create(gc); + onBeforeBehindTheSceneRequest.journalAddRequest = (fctxt, result) => { const docHostname = fctxt.getDocHostname(); if ( @@ -463,16 +463,13 @@ const onBeforeBehindTheSceneRequest = function(fctxt) { pageStoresToken !== µb.pageStoresToken ) { hostname = docHostname; - pageStores = new Set(); + pageStores.clear(); for ( const pageStore of µb.pageStores.values() ) { if ( pageStore.tabHostname !== docHostname ) { continue; } pageStores.add(pageStore); } pageStoresToken = µb.pageStoresToken; - if ( gcTimer !== undefined ) { - clearTimeout(gcTimer); - } - gcTimer = vAPI.setTimeout(gc, 30011); + gcTimer.offon(30011); } for ( const pageStore of pageStores ) { pageStore.journalAddRequest(fctxt, result); @@ -1057,7 +1054,9 @@ const headerValueFromName = function(headerName, headers) { const strictBlockBypasser = { hostnameToDeadlineMap: new Map(), - cleanupTimer: undefined, + cleanupTimer: vAPI.defer.create(( ) => { + strictBlockBypasser.cleanup(); + }), cleanup: function() { for ( const [ hostname, deadline ] of this.hostnameToDeadlineMap ) { @@ -1067,35 +1066,23 @@ const strictBlockBypasser = { } }, + revokeTime: function() { + return Date.now() + µb.hiddenSettings.strictBlockingBypassDuration * 1000; + }, + bypass: function(hostname) { if ( typeof hostname !== 'string' || hostname === '' ) { return; } - this.hostnameToDeadlineMap.set( - hostname, - Date.now() + µb.hiddenSettings.strictBlockingBypassDuration * 1000 - ); + this.hostnameToDeadlineMap.set(hostname, this.revokeTime()); }, isBypassed: function(hostname) { if ( this.hostnameToDeadlineMap.size === 0 ) { return false; } - let bypassDuration = - µb.hiddenSettings.strictBlockingBypassDuration * 1000; - if ( this.cleanupTimer === undefined ) { - this.cleanupTimer = vAPI.setTimeout( - ( ) => { - this.cleanupTimer = undefined; - this.cleanup(); - }, - bypassDuration + 10000 - ); - } + this.cleanupTimer.on({ sec: µb.hiddenSettings.strictBlockingBypassDuration + 10 }); for (;;) { const deadline = this.hostnameToDeadlineMap.get(hostname); if ( deadline !== undefined ) { if ( deadline > Date.now() ) { - this.hostnameToDeadlineMap.set( - hostname, - Date.now() + bypassDuration - ); + this.hostnameToDeadlineMap.set(hostname, this.revokeTime()); return true; } this.hostnameToDeadlineMap.delete(hostname);