From af57d6d9e78db6dbf556e8a740758ad51100c080 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 6 May 2015 13:01:02 -0400 Subject: [PATCH] one pulling if needed is better than always pushing to many + polishing logger --- src/css/logger-ui.css | 14 +++++- src/js/async.js | 19 -------- src/js/logger-ui.js | 75 ++++++++++++++++++++++++------- src/js/logger.js | 2 +- src/js/messaging.js | 85 ++++++++++++++++++++--------------- src/js/pagestats.js | 6 ++- src/js/popup.js | 100 +++++++++++++++++++++++++++--------------- 7 files changed, 192 insertions(+), 109 deletions(-) diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index d952053..f01a03c 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -141,9 +141,19 @@ body.compactView #content td { content: '\f070'; font: 1em FontAwesome; } -body:not(.popupOn) #content table tr.cat_net td:nth-of-type(2) { +#content table tr.tab:not(.canMtx) { + opacity: 0.2; + } +#content table tr.tab:not(.canMtx) > td:nth-of-type(2):before { + content: '\f00d'; + font: 1em FontAwesome; + } +body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2) { cursor: zoom-in; } +body:not(.popupOn) #content table tr.canMtx td:nth-of-type(2):hover { + background: white; + } #content table tr.cat_net td:nth-of-type(3) { font: 12px monospace; text-align: center; @@ -172,6 +182,8 @@ body.popupOn #popupContainer { } #popupContainer > div { background: #444; + border: 0; + border-bottom: 1px solid white; cursor: -webkit-grab; cursor: grab; height: 2em; diff --git a/src/js/async.js b/src/js/async.js index 5366eb8..e22ff5b 100644 --- a/src/js/async.js +++ b/src/js/async.js @@ -172,22 +172,3 @@ return asyncJobManager; tabIdToTimer[tabId] = setTimeout(updateBadge.bind(this, tabId), 500); }; })(); - -/******************************************************************************/ - -// Notify whoever care that url stats have changed (they need to -// rebuild their matrix). - -µMatrix.urlStatsChanged = function(pageUrl) { - // rhill 2013-11-17: No point in sending this message if the popup menu - // does not exist. I suspect this could be related to - // https://github.com/gorhill/httpswitchboard/issues/58 - var urlStatsChangedCallback = function(pageUrl) { - vAPI.messaging.broadcast({ - what: 'urlStatsChanged', - pageURL: pageUrl - }); - }; - - this.asyncJobs.add('urlStatsChanged-' + pageUrl, pageUrl, urlStatsChangedCallback, 1000); -}; diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index 31aa83f..880a250 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -43,6 +43,7 @@ var firstVarDataCol = 2; // currently, column 2 (0-based index) var lastVarDataIndex = 3; // currently, d0-d3 var maxEntries = 5000; var noTabId = ''; +var allTabIds = {}; var prettyRequestTypes = { 'main_frame': 'doc', @@ -167,6 +168,8 @@ var createRow = function(layout) { var createGap = function(tabId, url) { var tr = createRow('1'); tr.classList.add('doc'); + tr.classList.add('tab'); + tr.classList.add('canMtx'); tr.classList.add('tab_' + tabId); tr.cells[firstVarDataCol].textContent = url; tbody.insertBefore(tr, tbody.firstChild); @@ -187,6 +190,7 @@ var renderLogEntry = function(entry) { case 'net': tr = createRow('111'); + tr.classList.add('canMtx'); // If the request is that of a root frame, insert a gap in the table // in order to visually separate entries for different documents. if ( entry.d2 === 'doc' && entry.tab !== noTabId ) { @@ -213,10 +217,13 @@ var renderLogEntry = function(entry) { tr.cells[0].textContent = time.toLocaleTimeString('fullwide', timeOptions); tr.cells[0].title = time.toLocaleDateString('fullwide', dateOptions); - if ( entry.tab === noTabId ) { - tr.classList.add('tab_bts'); - } else if ( entry.tab !== '' ) { - tr.classList.add('tab_' + entry.tab); + if ( entry.tab ) { + tr.classList.add('tab'); + if ( entry.tab === noTabId ) { + tr.classList.add('tab_bts'); + } else if ( entry.tab !== '' ) { + tr.classList.add('tab_' + entry.tab); + } } if ( entry.cat !== '' ) { tr.classList.add('cat_' + entry.cat); @@ -233,8 +240,6 @@ var renderLogBuffer = function(response) { return; } - noTabId = response.noTabId; - // Preserve scroll position var height = tbody.offsetHeight; @@ -276,7 +281,7 @@ var truncateLog = function(size) { if ( size === 0 ) { size = 5000; } - size = Math.min(size, 5000); + size = Math.min(size, 10000); var tr; while ( tbody.childElementCount > size ) { tr = tbody.lastElementChild; @@ -286,13 +291,38 @@ var truncateLog = function(size) { /******************************************************************************/ -var onBufferRead = function(response) { +var onLogBufferRead = function(response) { + // This tells us the behind-the-scene tab id + noTabId = response.noTabId; + + // This may have changed meanwhile if ( response.maxLoggedRequests !== maxEntries ) { maxEntries = response.maxLoggedRequests; uDom('#maxEntries').val(maxEntries || ''); } + + // Neuter rows for which a tab does not exist anymore + // TODO: sort to avoid using indexOf + var targetTabId; + var i = allTabIds.length; + while ( i-- ) { + targetTabId = allTabIds[i]; + if ( targetTabId === noTabId ) { + continue; + } + if ( response.allTabIds.indexOf(targetTabId) !== -1 ) { + continue; + } + uDom('.tab_' + targetTabId).removeClass('canMtx'); + // Close popup if it is currently inspecting this tab + if ( targetTabId === popupManager.tabId ) { + popupManager.toggleOff(); + } + } + allTabIds = response.allTabIds; + renderLogBuffer(response); - setTimeout(readLogBuffer, 1000); + setTimeout(readLogBuffer, 1200); }; /******************************************************************************/ @@ -302,7 +332,7 @@ var onBufferRead = function(response) { // require a bit more code to ensure no multi time out events. var readLogBuffer = function() { - messager.send({ what: 'readMany' }, onBufferRead); + messager.send({ what: 'readMany' }, onLogBufferRead); }; /******************************************************************************/ @@ -326,7 +356,7 @@ var toggleCompactView = function() { /******************************************************************************/ -var togglePopup = (function() { +var popupManager = (function() { var realTabId = null; var localTabId = null; var container = null; @@ -337,7 +367,7 @@ var togglePopup = (function() { var styleTemplate = [ 'tr:not(.tab_{{tabId}}) {', 'cursor: not-allowed;', - 'opacity: 0.1;', + 'opacity: 0.2;', '}' ].join('\n'); @@ -511,11 +541,24 @@ var togglePopup = (function() { realTabId = null; }; - return function(ev) { - if ( realTabId === null ) { - toggleOn(ev.target); + var exports = { + toggleOn: function(ev) { + if ( realTabId === null ) { + toggleOn(ev.target); + } + }, + toggleOff: function() { + if ( realTabId !== null ) { + toggleOff(); + } } }; + + Object.defineProperty(exports, 'tabId', { + get: function() { return realTabId || 0; } + }); + + return exports; })(); /******************************************************************************/ @@ -548,7 +591,7 @@ uDom.onLoad(function() { uDom('#compactViewToggler').on('click', toggleCompactView); uDom('#clear').on('click', clearBuffer); uDom('#maxEntries').on('change', onMaxEntriesChanged); - uDom('#content table').on('click', 'tr.cat_net > td:nth-of-type(2)', togglePopup); + uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn); }); /******************************************************************************/ diff --git a/src/js/logger.js b/src/js/logger.js index e92a17e..914ac99 100644 --- a/src/js/logger.js +++ b/src/js/logger.js @@ -178,7 +178,7 @@ var writeOne = function() { /******************************************************************************/ -var readAll = function(tabId) { +var readAll = function() { if ( logBuffer === null ) { logBuffer = new LogBuffer(); setTimeout(janitor, logBufferObsoleteAfter); diff --git a/src/js/messaging.js b/src/js/messaging.js index 522064d..f310c9a 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -138,21 +138,23 @@ RowSnapshot.counts = (function() { /******************************************************************************/ -var matrixSnapshot = function(tabId, details) { +var matrixSnapshot = function(pageStore, details) { var µmuser = µm.userSettings; var r = { - tabId: tabId, - url: '', - hostname: '', - domain: '', - blockedCount: 0, - scope: '*', + blockedCount: pageStore.requestStats.blocked.all, + diff: [], + domain: pageStore.pageDomain, headers: µm.Matrix.getColumnHeaders(), - tSwitches: {}, + hostname: pageStore.pageHostname, + mtxContentModifiedTime: pageStore.mtxContentModifiedTime, + mtxCountModifiedTime: pageStore.mtxCountModifiedTime, pSwitches: {}, rows: {}, rowCount: 0, - diff: [], + scope: '*', + tabId: pageStore.tabId, + tSwitches: {}, + url: pageStore.pageUrl, userSettings: { colorBlindFriendly: µmuser.colorBlindFriendly, displayTextSize: µmuser.displayTextSize, @@ -163,28 +165,8 @@ var matrixSnapshot = function(tabId, details) { } }; - var tabContext = µm.tabContextManager.lookup(tabId); - - // Allow examination of behind-the-scene requests - if ( - tabContext.rawURL.lastIndexOf(vAPI.getURL('dashboard.html'), 0) === 0 || - tabContext.rawURL === µm.behindTheSceneURL - ) { - tabId = vAPI.noTabId; - } - - var pageStore = µm.pageStoreFromTabId(tabId); - if ( pageStore === null ) { - return r; - } - var headers = r.headers; - r.url = pageStore.pageUrl; - r.hostname = pageStore.pageHostname; - r.domain = pageStore.pageDomain; - r.blockedCount = pageStore.requestStats.blocked.all; - if ( µmuser.popupScopeLevel === 'site' ) { r.scope = r.hostname; } else if ( µmuser.popupScopeLevel === 'domain' ) { @@ -269,16 +251,46 @@ var matrixSnapshot = function(tabId, details) { /******************************************************************************/ var matrixSnapshotFromTabId = function(details, callback) { + var matrixSnapshotIf = function(tabId, details) { + var pageStore = µm.pageStoreFromTabId(tabId); + if ( pageStore === null ) { + callback('ENOTFOUND'); + return; + } + + // First verify whether we must return data or not. + if ( + pageStore.mtxContentModifiedTime === details.mtxContentModifiedTime && + pageStore.mtxCountModifiedTime === details.mtxCountModifiedTime + ) { + callback('ENOCHANGE'); + return ; + } + + callback(matrixSnapshot(pageStore, details)); + }; + // Specific tab id requested? if ( details.tabId ) { - callback(matrixSnapshot(details.tabId, details)); + matrixSnapshotIf(details.tabId, details); return; } - // Otherwise use tab id of current tab - vAPI.tabs.get(null, function(tab) { - callback(matrixSnapshot(tab.id, details)); - }); + // Fall back to currently active tab + var onTabReady = function(tab) { + if ( typeof tab !== 'object' ) { + callback('ENOTFOUND'); + return; + } + + // Allow examination of behind-the-scene requests + var tabId = tab.url.lastIndexOf(vAPI.getURL('dashboard.html'), 0) !== 0 ? + tab.id : + vAPI.noTabId; + matrixSnapshotIf(tabId, details); + }; + + vAPI.tabs.get(null, onTabReady); }; /******************************************************************************/ @@ -962,9 +974,10 @@ var onMessage = function(request, sender, callback) { case 'readMany': response = { colorBlind: false, - entries: µm.logger.readAll(request.tabId), + entries: µm.logger.readAll(), maxLoggedRequests: µm.userSettings.maxLoggedRequests, - noTabId: vAPI.noTabId + noTabId: vAPI.noTabId, + allTabIds: Object.keys(µm.pageStores) }; break; diff --git a/src/js/pagestats.js b/src/js/pagestats.js index 0ffcd48..0d5964b 100644 --- a/src/js/pagestats.js +++ b/src/js/pagestats.js @@ -344,6 +344,8 @@ PageStore.prototype.init = function(tabContext) { this.perLoadAllowedRequestCount = 0; this.perLoadBlockedRequestCount = 0; this.incinerationTimer = null; + this.mtxContentModifiedTime = 0; + this.mtxCountModifiedTime = 0; return this; }; @@ -397,12 +399,14 @@ PageStore.prototype.recordRequest = function(type, url, block) { } this.distinctRequestCount++; + this.mtxCountModifiedTime = Date.now(); + if ( this.domains.hasOwnProperty(hostname) === false ) { this.domains[hostname] = true; this.allHostnamesString += hostname + ' '; + this.mtxContentModifiedTime = Date.now(); } - µm.urlStatsChanged(this.pageUrl); // console.debug("pagestats.js > PageStore.recordRequest(): %o: %s @ %s", this, type, url); }; diff --git a/src/js/popup.js b/src/js/popup.js index b90ed4f..e03816c 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -67,22 +67,7 @@ var matrixHeaderPrettyNames = { var firstPartyLabel = ''; var blacklistedHostnamesLabel = ''; -/******************************************************************************/ -/******************************************************************************/ - -// https://github.com/gorhill/httpswitchboard/issues/345 - -var onMessage = function(msg) { - if ( msg.what !== 'urlStatsChanged' ) { - return; - } - if ( matrixSnapshot.url !== msg.pageURL ) { - return; - } - queryMatrixSnapshot(makeMenu); -}; - -var messager = vAPI.messaging.channel('popup.js', onMessage); +var messager = vAPI.messaging.channel('popup.js'); /******************************************************************************/ /******************************************************************************/ @@ -108,7 +93,7 @@ function updateMatrixSnapshot() { updateMatrixBehavior(); updateMatrixButtons(); }; - queryMatrixSnapshot(snapshotReady); + matrixSnapshotPoller.mustFetch(snapshotReady); } /******************************************************************************/ @@ -1177,35 +1162,80 @@ var onMatrixSnapshotReady = function(response) { /******************************************************************************/ -var queryMatrixSnapshot = function(callback) { - var tabId = matrixSnapshot.tabId; +var matrixSnapshotPoller = (function() { + var timer = null; - // If no tab id yet, see if there is one specified in our URL - if ( tabId === undefined ) { - var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/); - if ( matches !== null ) { - tabId = matches[1]; + var snapshotPolled = function(response) { + timer = null; + if ( typeof response === 'object' ) { + matrixSnapshot = response; + makeMenu(); } - } + pollSnapshotAsync(); + }; - var request = { - what: 'matrixSnapshot', - tabId: tabId, - tabURL: matrixSnapshot.url + var pollSnapshot = function() { + timer = null; + messager.send({ + what: 'matrixSnapshot', + tabId: matrixSnapshot.tabId, + mtxContentModifiedTime: matrixSnapshot.mtxContentModifiedTime, + mtxCountModifiedTime: matrixSnapshot.mtxCountModifiedTime + }, snapshotPolled); + }; + + var pollSnapshotAsync = function() { + if ( timer !== null ) { + return; + } + timer = setTimeout(pollSnapshot, 1414); }; - var snapshotReceived = function(response) { - matrixSnapshot = response; - callback(); + + var cancelSnapshotAsync = function() { + if ( timer !== null ) { + clearTimeout(timer); + timer = null; + } }; - messager.send(request, snapshotReceived); -}; + + var mustFetch = function(callback) { + cancelSnapshotAsync(); + + var tabId = matrixSnapshot.tabId; + + // If no tab id yet, see if there is one specified in our URL + if ( tabId === undefined ) { + var matches = window.location.search.match(/(?:\?|&)tabId=([^&]+)/); + if ( matches !== null ) { + tabId = matches[1]; + } + } + + var snapshotFetched = function(response) { + if ( typeof response === 'object' ) { + matrixSnapshot = response; + } + callback(); + pollSnapshotAsync(); + }; + + messager.send({ + what: 'matrixSnapshot', + tabId: tabId + }, snapshotFetched); + }; + + return { + mustFetch: mustFetch + }; +})(); /******************************************************************************/ // Make menu only when popup html is fully loaded uDom.onLoad(function() { - queryMatrixSnapshot(onMatrixSnapshotReady); + matrixSnapshotPoller.mustFetch(onMatrixSnapshotReady); // Below is UI stuff which is not key to make the menu, so this can // be done without having to wait for a tab to be bound to the menu.