diff --git a/src/background.html b/src/background.html index 9e006ec0c..65b6ea016 100644 --- a/src/background.html +++ b/src/background.html @@ -23,6 +23,7 @@ + diff --git a/src/js/logger.js b/src/js/logger.js new file mode 100644 index 000000000..eb77e0765 --- /dev/null +++ b/src/js/logger.js @@ -0,0 +1,207 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015 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 */ + +/******************************************************************************/ +/******************************************************************************/ + +µBlock.logger = (function() { + +'use strict'; + +/******************************************************************************/ +/******************************************************************************/ + +var LogEntry = function(details, result) { + this.init(details, result); +}; + +/******************************************************************************/ + +var logEntryFactory = function(details, result) { + var entry = logEntryJunkyard.pop(); + if ( entry ) { + return entry.init(details, result); + } + return new LogEntry(details, result); +}; + +var logEntryJunkyard = []; +var logEntryJunkyardMax = 100; + +/******************************************************************************/ + +LogEntry.prototype.init = function(details, result) { + this.tstamp = Date.now(); + this.url = details.requestURL; + this.hostname = details.requestHostname; + this.type = details.requestType; + this.result = result; + return this; +}; + +/******************************************************************************/ + +LogEntry.prototype.dispose = function() { + this.url = this.hostname = this.type = this.result = ''; + if ( logEntryJunkyard.length < logEntryJunkyardMax ) { + logEntryJunkyard.push(this); + } +}; + +/******************************************************************************/ +/******************************************************************************/ + +var LogBuffer = function() { + this.lastReadTime = 0; + this.size = 50; + this.buffer = new Array(this.size); + this.readPtr = 0; + this.writePtr = 0; +}; + +/******************************************************************************/ + +LogBuffer.prototype.dispose = function() { + var entry; + var i = this.buffer.length; + while ( i-- ) { + entry = this.buffer[i]; + if ( entry instanceof LogEntry ) { + entry.dispose(); + } + } + this.buffer = null; + return null; +}; + +/******************************************************************************/ + +LogBuffer.prototype.writeOne = function(details, result) { + // Reusing log entry = less memory churning + var entry = this.buffer[this.writePtr]; + if ( entry instanceof LogEntry === false ) { + this.buffer[this.writePtr] = logEntryFactory(details, result); + } else { + entry.init(details, result); + } + this.writePtr += 1; + if ( this.writePtr === this.size ) { + this.writePtr = 0; + } + // Grow the buffer between 1.5x-2x the current size + if ( this.writePtr === this.readPtr ) { + var toMove = this.buffer.slice(0, this.writePtr); + var minSize = Math.ceil(this.size * 1.5); + this.size += toMove.length; + if ( this.size < minSize ) { + this.buffer = this.buffer.concat(toMove, new Array(minSize - this.size)); + this.writePtr = this.size; + } else { + this.buffer = this.buffer.concat(toMove); + this.writePtr = 0; + } + this.size = this.buffer.length; + } +}; + +/******************************************************************************/ + +LogBuffer.prototype.readAll = function() { + var out; + if ( this.readPtr < this.writePtr ) { + out = this.buffer.slice(this.readPtr, this.writePtr); + } else if ( this.writePtr < this.readPtr ) { + out = this.buffer.slice(this.readPtr).concat(this.buffer.slice(0, this.writePtr)); + } else { + out = []; + } + this.readPtr = this.writePtr; + this.lastReadTime = Date.now(); + return out; +}; + +/******************************************************************************/ +/******************************************************************************/ + +// Tab id to log buffer instances +var logBuffers = {}; + +// After 30 seconds without being read, a buffer will be considered unused, and +// thus removed from memory. +var logBufferObsoleteAfter = 30 * 1000; + +/******************************************************************************/ + +var loggerWriteOne = function(tabId, details, result) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + return; + } + var logBuffer = logBuffers[tabId]; + logBuffer.writeOne(details, result); +}; + +/******************************************************************************/ + +var loggerReadAll = function(tabId) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + logBuffers[tabId] = new LogBuffer(); + } + return logBuffers[tabId].readAll(); +}; + +/******************************************************************************/ + +var loggerJanitor = function() { + var logBuffer; + var obsolete = Date.now() - logBufferObsoleteAfter; + for ( var tabId in logBuffers ) { + if ( logBuffers.hasOwnProperty(tabId) === false ) { + continue; + } + logBuffer = logBuffers[tabId]; + if ( logBuffer.lastReadTime < obsolete ) { + logBuffer.dispose(); + delete logBuffers[tabId]; + } + } + setTimeout(loggerJanitor, loggerJanitorPeriod); +}; + +// The janitor will look for stale log buffer every 2 minutes. +var loggerJanitorPeriod = 2 * 60 * 1000; + +setTimeout(loggerJanitor, loggerJanitorPeriod); + +/******************************************************************************/ + +return { + writeOne: loggerWriteOne, + readAll: loggerReadAll +}; + +/******************************************************************************/ +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 3e2c93b17..3b77bb80f 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -1248,10 +1248,7 @@ var onMessage = function(request, sender, callback) { switch ( request.what ) { case 'readLogBuffer': - var pageStore = µb.pageStoreFromTabId(request.tabId); - if ( pageStore ) { - response = pageStore.logBuffer.readAll(); - } + response = µb.logger.readAll(request.tabId); break; default: diff --git a/src/js/pagestore.js b/src/js/pagestore.js index 2903491cc..4bf580465 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -19,7 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* jshint bitwise: false */ /* global vAPI, µBlock */ /******************************************************************************* @@ -45,178 +44,6 @@ var µb = µBlock; /******************************************************************************/ /******************************************************************************/ -var LogEntry = function(details, result) { - this.init(details, result); -}; - -/******************************************************************************/ - -var logEntryFactory = function(details, result) { - var entry = logEntryJunkyard.pop(); - if ( entry ) { - return entry.init(details, result); - } - return new LogEntry(details, result); -}; - -var logEntryJunkyard = []; -var logEntryJunkyardMax = 100; - -/******************************************************************************/ - -LogEntry.prototype.init = function(details, result) { - this.tstamp = Date.now(); - this.url = details.requestURL; - this.hostname = details.requestHostname; - this.type = details.requestType; - this.result = result; - return this; -}; - -/******************************************************************************/ - -LogEntry.prototype.dispose = function() { - this.url = this.hostname = this.type = this.result = ''; - if ( logEntryJunkyard.length < logEntryJunkyardMax ) { - logEntryJunkyard.push(this); - } -}; - -/******************************************************************************/ - -var LogBuffer = function() { - this.lastReadTime = 0; - this.size = 50; - this.buffer = null; - this.readPtr = 0; - this.writePtr = 0; -}; - -/******************************************************************************/ - -var logBufferFactory = function() { - return new LogBuffer(); -}; - -var liveLogBuffers = []; - -/******************************************************************************/ - -LogBuffer.prototype.dispose = function() { - if ( this.buffer === null ) { - return null; - } - var entry; - var i = this.buffer.length; - while ( i-- ) { - entry = this.buffer[i]; - if ( entry instanceof LogEntry ) { - entry.dispose(); - } - } - this.buffer = null; - return null; -}; - -/******************************************************************************/ - -LogBuffer.prototype.start = function() { - if ( this.buffer === null ) { - this.buffer = new Array(this.size); - this.readPtr = 0; - this.writePtr = 0; - liveLogBuffers.push(this); - } -}; - -/******************************************************************************/ - -LogBuffer.prototype.stop = function() { - this.dispose(); - this.buffer = null; - // The janitor will remove us from the live pool eventually. -}; - -/******************************************************************************/ - -LogBuffer.prototype.writeOne = function(details, result) { - if ( this.buffer === null ) { - return; - } - // Reusing log entry = less memory churning - var entry = this.buffer[this.writePtr]; - if ( entry instanceof LogEntry === false ) { - this.buffer[this.writePtr] = logEntryFactory(details, result); - } else { - entry.init(details, result); - } - this.writePtr += 1; - if ( this.writePtr === this.size ) { - this.writePtr = 0; - } - // Grow the buffer between 1.5x-2x the current size - if ( this.writePtr === this.readPtr ) { - var toMove = this.buffer.slice(0, this.writePtr); - var minSize = Math.ceil(this.size * 1.5); - this.size += toMove.length; - if ( this.size < minSize ) { - this.buffer = this.buffer.concat(toMove, new Array(minSize - this.size)); - this.writePtr = this.size; - } else { - this.buffer = this.buffer.concat(toMove); - this.writePtr = 0; - } - this.size = this.buffer.length; - } -}; - -/******************************************************************************/ - -LogBuffer.prototype.readAll = function() { - var out; - if ( this.buffer === null ) { - this.start(); - out = []; - } else if ( this.readPtr < this.writePtr ) { - out = this.buffer.slice(this.readPtr, this.writePtr); - } else if ( this.writePtr < this.readPtr ) { - out = this.buffer.slice(this.readPtr).concat(this.buffer.slice(0, this.writePtr)); - } else { - out = []; - } - this.readPtr = this.writePtr; - this.lastReadTime = Date.now(); - return out; -}; - -/******************************************************************************/ - -var logBufferJanitor = function() { - var logBuffer; - var obsolete = Date.now() - logBufferObsoleteAfter; - var i = liveLogBuffers.length; - while ( i-- ) { - logBuffer = liveLogBuffers[i]; - if ( logBuffer.lastReadTime < obsolete ) { - logBuffer.stop(); - liveLogBuffers.splice(i, 1); - } - } - setTimeout(logBufferJanitor, logBufferJanitorPeriod); -}; - -// The janitor will look for stale log buffer every 2 minutes. -var logBufferJanitorPeriod = 2 * 60 * 1000; - -// After 30 seconds without being read, a buffer will be considered unused, and -// thus removed from memory. -var logBufferObsoleteAfter = 30 * 1000; - -setTimeout(logBufferJanitor, logBufferJanitorPeriod); - -/******************************************************************************/ -/******************************************************************************/ - // To mitigate memory churning var netFilteringResultCacheEntryJunkyard = []; var netFilteringResultCacheEntryJunkyardMax = 200; @@ -485,12 +312,6 @@ PageStore.prototype.init = function(tabId) { .matchStringExactType(context, tabContext.normalURL, 'cosmetic-filtering') .charAt(1) === 'b'; - // Preserve old buffer if there is one already, it may be in use, and - // overwritting it would required another read to restart it. - if ( this.logBuffer instanceof LogBuffer === false ) { - this.logBuffer = logBufferFactory(); - } - return this; }; @@ -536,7 +357,6 @@ PageStore.prototype.dispose = function() { this.hostnameToCountMap = null; this.disposeFrameStores(); this.netFilteringCache = this.netFilteringCache.dispose(); - this.logBuffer = this.logBuffer.dispose(); if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) { pageStoreJunkyard.push(this); } @@ -705,7 +525,6 @@ PageStore.prototype.filterRequest = function(context) { return result; }; -// Cache only what is worth it if logging is disabled // http://jsperf.com/string-indexof-vs-object var collapsibleRequestTypes = 'image sub_frame object'; @@ -768,7 +587,6 @@ PageStore.prototype.logRequest = function(context, result) { µb.localSettings.blockedRequestCount++; } µb.localSettingsModifyTime = now; - this.logBuffer.writeOne(context, result); }; /******************************************************************************/ diff --git a/src/js/tab.js b/src/js/tab.js index e416bd93b..ef44a3334 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -471,6 +471,7 @@ vAPI.tabs.onPopup = function(details) { if ( pageStore ) { pageStore.logRequest(context, result); } + µb.logger.writeOne(details.openerTabId, context, result); // Not blocked if ( µb.isAllowResult(result) ) { diff --git a/src/js/traffic.js b/src/js/traffic.js index e93a404aa..f49222d3d 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -92,6 +92,7 @@ var onBeforeRequest = function(details) { // Possible outcomes: blocked, allowed-passthru, allowed-mirror pageStore.logRequest(requestContext, result); + µb.logger.writeOne(tabId, requestContext, result); // Not blocked if ( µb.isAllowResult(result) ) { @@ -186,6 +187,7 @@ var onBeforeRootFrameRequest = function(details) { if ( pageStore ) { pageStore.logRequest(context, result); } + µb.logger.writeOne(tabId, context, result); // Not blocked if ( µb.isAllowResult(result) ) { @@ -277,6 +279,7 @@ var onBeforeBehindTheSceneRequest = function(details) { } pageStore.logRequest(context, result); + µb.logger.writeOne(vAPI.noTabId, context, result); // Not blocked if ( µb.isAllowResult(result) ) { @@ -325,6 +328,7 @@ var onHeadersReceived = function(details) { var result = pageStore.filterRequestNoCache(context); pageStore.logRequest(context, result); + µb.logger.writeOne(tabId, context, result); // Don't block if ( µb.isAllowResult(result) ) { @@ -368,6 +372,7 @@ var onRootFrameHeadersReceived = function(details) { var result = pageStore.filterRequestNoCache(context); pageStore.logRequest(context, result); + µb.logger.writeOne(tabId, context, result); // Don't block if ( µb.isAllowResult(result) ) {