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