This commit is contained in:
gorhill 2015-05-16 10:15:02 -04:00
parent 20f5794b37
commit e0284b8908
7 changed files with 256 additions and 45 deletions

View File

@ -403,6 +403,10 @@
"message":"No non-blocked requests logged for this page", "message":"No non-blocked requests logged for this page",
"description":"English: No non-blocked requests logged for this page" "description":"English: No non-blocked requests logged for this page"
}, },
"logAll":{
"message":"All",
"description":"Appears in the logger's tab selector"
},
"logBehindTheScene":{ "logBehindTheScene":{
"message":"Behind the scene", "message":"Behind the scene",
"description":"Pretty name for behind-the-scene network requests" "description":"Pretty name for behind-the-scene network requests"

View File

@ -7,7 +7,6 @@ body {
margin: 0; margin: 0;
overflow-x: hidden; overflow-x: hidden;
padding: 0; padding: 0;
white-space: nowrap;
width: 100%; width: 100%;
} }
#toolbar { #toolbar {
@ -16,7 +15,7 @@ body {
box-sizing: border-box; box-sizing: border-box;
left: 0; left: 0;
margin: 0; margin: 0;
padding: 0 1em; padding: 0.5em 1em;
position: fixed; position: fixed;
top: 0; top: 0;
width: 100%; width: 100%;
@ -28,7 +27,7 @@ body {
box-sizing: border-box; box-sizing: border-box;
cursor: pointer; cursor: pointer;
display: inline-block; display: inline-block;
font-size: 20px; font-size: 150%;
margin: 0; margin: 0;
padding: 8px; padding: 8px;
} }
@ -39,6 +38,19 @@ body {
#toolbar .button:hover { #toolbar .button:hover {
background-color: #eee; background-color: #eee;
} }
#toolbar > div {
white-space: nowrap;
}
#toolbar > div:first-of-type {
font-size: 120%;
}
#toolbar > div > * {
vertical-align: middle;
}
#pageSelector {
width: 28em;
padding: 0.2em 0;
}
body #compactViewToggler.button:before { body #compactViewToggler.button:before {
content: '\f102'; content: '\f102';
} }
@ -55,14 +67,13 @@ body.f #filterButton {
background-color: #fee; background-color: #fee;
} }
#maxEntries { #maxEntries {
margin-left: 3em; margin: 0 2em;
} }
input:focus { input:focus {
background-color: #ffe; background-color: #ffe;
} }
#content { #content {
font: 13px sans-serif; font: 13px sans-serif;
margin-top: 3.5em;
width: 100%; width: 100%;
} }

View File

@ -30,6 +30,15 @@
/******************************************************************************/ /******************************************************************************/
// Adjust top padding of content table, to match that of toolbar height.
document.getElementById('content').style.setProperty(
'margin-top',
document.getElementById('toolbar').offsetHeight + 'px'
);
/******************************************************************************/
var messager = vAPI.messaging.channel('logger-ui.js'); var messager = vAPI.messaging.channel('logger-ui.js');
var tbody = document.querySelector('#content tbody'); var tbody = document.querySelector('#content tbody');
var trJunkyard = []; var trJunkyard = [];
@ -40,6 +49,8 @@ var maxEntries = 5000;
var noTabId = ''; var noTabId = '';
var allTabIds = {}; var allTabIds = {};
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
var prettyRequestTypes = { var prettyRequestTypes = {
'main_frame': 'doc', 'main_frame': 'doc',
'stylesheet': 'css', 'stylesheet': 'css',
@ -61,6 +72,18 @@ var dateOptions = {
/******************************************************************************/ /******************************************************************************/
var classNameFromTabId = function(tabId) {
if ( tabId === noTabId ) {
return 'tab_bts';
}
if ( tabId !== '' ) {
return 'tab_' + tabId;
}
return '';
};
/******************************************************************************/
// Emphasize hostname in URL, as this is what matters in uMatrix's rules. // Emphasize hostname in URL, as this is what matters in uMatrix's rules.
var nodeFromURL = function(url, filter) { var nodeFromURL = function(url, filter) {
@ -165,6 +188,14 @@ var createRow = function(layout) {
/******************************************************************************/ /******************************************************************************/
var createHiddenTextNode = function(text) {
var node = hiddenTemplate.cloneNode(true);
node.textContent = text;
return node;
};
/******************************************************************************/
var createGap = function(tabId, url) { var createGap = function(tabId, url) {
var tr = createRow('1'); var tr = createRow('1');
tr.classList.add('tab'); tr.classList.add('tab');
@ -246,11 +277,9 @@ var renderLogEntry = function(entry) {
tr.cells[0].title = time.toLocaleDateString('fullwide', dateOptions); tr.cells[0].title = time.toLocaleDateString('fullwide', dateOptions);
if ( entry.tab ) { if ( entry.tab ) {
tr.classList.add('tab'); tr.classList.add('tab', classNameFromTabId(entry.tab));
if ( entry.tab === noTabId ) { if ( entry.tab === noTabId ) {
tr.classList.add('tab_bts'); tr.cells[1].appendChild(createHiddenTextNode('bts'));
} else if ( entry.tab !== '' ) {
tr.classList.add('tab_' + entry.tab);
} }
} }
if ( entry.cat !== '' ) { if ( entry.cat !== '' ) {
@ -316,6 +345,78 @@ var renderLogEntries = function(response) {
/******************************************************************************/ /******************************************************************************/
var synchronizeTabIds = function(newTabIds) {
var oldTabIds = allTabIds;
// Neuter rows for which a tab does not exist anymore
// TODO: sort to avoid using indexOf
var autoDeleteVoidRows = !!vAPI.localStorage.getItem('loggerAutoDeleteVoidRows');
var rowVoided = false;
var trs;
for ( var tabId in oldTabIds ) {
if ( oldTabIds.hasOwnProperty(tabId) === false ) {
continue;
}
if ( newTabIds.hasOwnProperty(tabId) ) {
continue;
}
// Mark or remove voided rows
trs = uDom('.tab_' + tabId);
if ( autoDeleteVoidRows ) {
toJunkyard(trs);
} else {
trs.removeClass('canMtx');
rowVoided = true;
}
// Remove popup if it is currently bound to a removed tab.
if ( tabId === popupManager.tabId ) {
popupManager.toggleOff();
}
}
var select = document.getElementById('pageSelector');
var selectValue = select.value;
var tabIds = Object.keys(newTabIds).sort(function(a, b) {
return newTabIds[a].localeCompare(newTabIds[b]);
});
var option;
for ( var i = 0, j = 2; i < tabIds.length; i++ ) {
tabId = tabIds[i];
if ( tabId === noTabId ) {
continue;
}
option = select.options[j];
j += 1;
if ( !option ) {
option = document.createElement('option');
select.appendChild(option);
}
option.textContent = newTabIds[tabId];
option.value = classNameFromTabId(tabId);
if ( option.value === selectValue ) {
option.setAttribute('selected', '');
} else {
option.removeAttribute('selected');
}
}
while ( j < select.options.length ) {
select.removeChild(select.options[j]);
}
if ( select.value !== selectValue ) {
select.selectedIndex = 0;
select.value = '';
select.options[0].setAttribute('selected', '');
pageSelectorChanged();
}
allTabIds = newTabIds;
return rowVoided;
};
/******************************************************************************/
var truncateLog = function(size) { var truncateLog = function(size) {
if ( size === 0 ) { if ( size === 0 ) {
size = 5000; size = 5000;
@ -343,28 +444,7 @@ var onLogBufferRead = function(response) {
// Neuter rows for which a tab does not exist anymore // Neuter rows for which a tab does not exist anymore
// TODO: sort to avoid using indexOf // TODO: sort to avoid using indexOf
var autoDeleteVoidRows = vAPI.localStorage.getItem('loggerAutoDeleteVoidRows'); var rowVoided = synchronizeTabIds(response.tabIds);
var rowVoided = false, trs;
for ( var tabId in allTabIds ) {
if ( allTabIds.hasOwnProperty(tabId) === false ) {
continue;
}
if ( response.tabIds.hasOwnProperty(tabId) ) {
continue;
}
trs = uDom('.tab_' + tabId);
if ( autoDeleteVoidRows ) {
toJunkyard(trs);
} else {
trs.removeClass('canMtx');
rowVoided = true;
}
if ( tabId === popupManager.tabId ) {
popupManager.toggleOff();
}
}
allTabIds = response.tabIds;
renderLogEntries(response); renderLogEntries(response);
if ( rowVoided ) { if ( rowVoided ) {
@ -395,6 +475,41 @@ var readLogBuffer = function() {
/******************************************************************************/ /******************************************************************************/
var pageSelectorChanged = function() {
var style = document.getElementById('tabFilterer');
var tabClass = document.getElementById('pageSelector').value;
var sheet = style.sheet;
while ( sheet.cssRules.length !== 0 ) {
sheet.deleteRule(0);
}
if ( tabClass !== '' ) {
sheet.insertRule(
'#content table tr:not(.' + tabClass + ') { display: none; }',
0
);
}
uDom('#refresh').toggleClass(
'disabled',
tabClass === '' || tabClass === 'tab_bts'
);
};
/******************************************************************************/
var reloadTab = function() {
var tabClass = document.getElementById('pageSelector').value;
var matches = tabClass.match(/^tab_(.+)$/);
if ( matches === null ) {
return;
}
if ( matches[1] === 'bts' ) {
return;
}
messager.send({ what: 'reloadTab', tabId: matches[1] });
};
/******************************************************************************/
var onMaxEntriesChanged = function() { var onMaxEntriesChanged = function() {
var raw = uDom(this).val(); var raw = uDom(this).val();
try { try {
@ -649,7 +764,7 @@ var popupManager = (function() {
popupObserver = new MutationObserver(resizePopup); popupObserver = new MutationObserver(resizePopup);
container.appendChild(popup); container.appendChild(popup);
style = document.querySelector('#content > style'); style = document.getElementById('popupFilterer');
style.textContent = styleTemplate.replace('{{tabId}}', localTabId); style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
document.body.classList.add('popupOn'); document.body.classList.add('popupOn');
@ -701,6 +816,8 @@ var popupManager = (function() {
uDom.onLoad(function() { uDom.onLoad(function() {
readLogBuffer(); readLogBuffer();
uDom('#pageSelector').on('change', pageSelectorChanged);
uDom('#refresh').on('click', reloadTab);
uDom('#compactViewToggler').on('click', toggleCompactView); uDom('#compactViewToggler').on('click', toggleCompactView);
uDom('#clean').on('click', cleanBuffer); uDom('#clean').on('click', cleanBuffer);
uDom('#clear').on('click', clearBuffer); uDom('#clear').on('click', clearBuffer);

View File

@ -1193,11 +1193,17 @@ var onMessage = function(request, sender, callback) {
switch ( request.what ) { switch ( request.what ) {
case 'readAll': case 'readAll':
var tabIds = {}; var tabIds = {}, pageStore;
var loggerURL = vAPI.getURL('logger-ui.html');
for ( var tabId in µb.pageStores ) { for ( var tabId in µb.pageStores ) {
if ( µb.pageStores.hasOwnProperty(tabId) ) { pageStore = µb.pageStoreFromTabId(tabId);
tabIds[tabId] = true; if ( pageStore === null ) {
continue;
} }
if ( pageStore.rawURL.lastIndexOf(loggerURL, 0) === 0 ) {
continue;
}
tabIds[tabId] = pageStore.title;
} }
response = { response = {
colorBlind: µb.userSettings.colorBlindFriendly, colorBlind: µb.userSettings.colorBlindFriendly,

View File

@ -295,6 +295,8 @@ PageStore.prototype.init = function(tabId) {
var tabContext = µb.tabContextManager.lookup(tabId); var tabContext = µb.tabContextManager.lookup(tabId);
this.tabId = tabId; this.tabId = tabId;
this.tabHostname = tabContext.rootHostname; this.tabHostname = tabContext.rootHostname;
this.title = tabContext.rawURL;
this.rawURL = tabContext.rawURL;
this.hostnameToCountMap = {}; this.hostnameToCountMap = {};
this.contentLastModified = 0; this.contentLastModified = 0;
this.frames = {}; this.frames = {};
@ -334,6 +336,7 @@ PageStore.prototype.reuse = function(context) {
if ( context === 'tabUpdated' ) { if ( context === 'tabUpdated' ) {
// As part of https://github.com/chrisaljoudi/uBlock/issues/405 // As part of https://github.com/chrisaljoudi/uBlock/issues/405
// URL changed, force a re-evaluation of filtering switch // URL changed, force a re-evaluation of filtering switch
this.rawURL = tabContext.rawURL;
this.netFilteringReadTime = 0; this.netFilteringReadTime = 0;
return this; return this;
} }
@ -354,6 +357,9 @@ PageStore.prototype.dispose = function() {
// need to release the memory taken by these, which can amount to // need to release the memory taken by these, which can amount to
// sizeable enough chunks (especially requests, through the request URL // sizeable enough chunks (especially requests, through the request URL
// used as a key). // used as a key).
this.tabHostname = '';
this.title = '';
this.rawURL = '';
this.hostnameToCountMap = null; this.hostnameToCountMap = null;
this.disposeFrameStores(); this.disposeFrameStores();
this.netFilteringCache = this.netFilteringCache.dispose(); this.netFilteringCache = this.netFilteringCache.dispose();

View File

@ -510,9 +510,7 @@ vAPI.tabs.registerListeners();
// Create an entry for the tab if it doesn't exist. // Create an entry for the tab if it doesn't exist.
µb.bindTabToPageStats = function(tabId, context) { µb.bindTabToPageStats = function(tabId, context) {
if ( vAPI.isBehindTheSceneTabId(tabId) === false ) { this.updateBadgeAsync(tabId);
this.updateBadgeAsync(tabId);
}
// Do not create a page store for URLs which are of no interests // Do not create a page store for URLs which are of no interests
if ( µb.tabContextManager.exists(tabId) === false ) { if ( µb.tabContextManager.exists(tabId) === false ) {
@ -540,6 +538,8 @@ vAPI.tabs.registerListeners();
return pageStore; return pageStore;
} }
this.updateTitle(tabId);
// Rebind according to context. We rebind even if the URL did not change, // Rebind according to context. We rebind even if the URL did not change,
// as maybe the tab was force-reloaded, in which case the page stats must // as maybe the tab was force-reloaded, in which case the page stats must
// be all reset. // be all reset.
@ -570,8 +570,65 @@ vAPI.tabs.registerListeners();
// Permanent page store for behind-the-scene requests. Must never be removed. // Permanent page store for behind-the-scene requests. Must never be removed.
µb.pageStores[vAPI.noTabId] = µb.PageStore.factory(vAPI.noTabId); µb.pageStores[vAPI.noTabId] = µb.PageStore.factory(vAPI.noTabId);
µb.pageStores[vAPI.noTabId].title = vAPI.i18n('logBehindTheScene');
/******************************************************************************/ /******************************************************************************/
µb.updateTitle = (function() {
var tabIdToTimer = Object.create(null);
var tabIdToTryCount = Object.create(null);
var delay = 499;
var tryNoMore = function(tabId) {
delete tabIdToTryCount[tabId];
};
var tryAgain = function(tabId) {
var count = tabIdToTryCount[tabId];
if ( count === undefined ) {
return false;
}
if ( count === 1 ) {
delete tabIdToTryCount[tabId];
return false;
}
tabIdToTryCount[tabId] = count - 1;
tabIdToTimer[tabId] = setTimeout(updateTitle.bind(µb, tabId), delay);
return true;
};
var onTabReady = function(tabId, tab) {
if ( !tab ) {
return tryNoMore(tabId);
}
var pageStore = this.pageStoreFromTabId(tabId);
if ( pageStore === null ) {
return tryNoMore(tabId);
}
if ( !tab.title && tryAgain(tabId) ) {
return;
}
tryNoMore(tabId);
pageStore.title = tab.title || tab.url || '';
};
var updateTitle = function(tabId) {
delete tabIdToTimer[tabId];
vAPI.tabs.get(tabId, onTabReady.bind(this, tabId));
};
return function(tabId) {
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
return;
}
if ( tabIdToTimer[tabId] ) {
clearTimeout(tabIdToTimer[tabId]);
}
tabIdToTimer[tabId] = setTimeout(updateTitle.bind(this, tabId), delay);
tabIdToTryCount[tabId] = 5;
};
})();
/******************************************************************************/ /******************************************************************************/
// Stale page store entries janitor // Stale page store entries janitor
@ -612,7 +669,6 @@ var pageStoreJanitor = function() {
setTimeout(pageStoreJanitor, pageStoreJanitorPeriod); setTimeout(pageStoreJanitor, pageStoreJanitorPeriod);
/******************************************************************************/
/******************************************************************************/ /******************************************************************************/
})(); })();

View File

@ -9,15 +9,25 @@
<body class="compactView f"> <body class="compactView f">
<div id="toolbar"> <div id="toolbar">
<span id="compactViewToggler" class="button fa"></span> <div>
<span id="clean" class="button fa disabled">&#xf00d;</span> <select id="pageSelector">
<span id="clear" class="button fa disabled">&#xf12d;</span> <option value="" data-i18n="logAll">
<span id="filterButton" class="button fa">&#xf0b0;</span><input id="filterInput" type="text" placeholder="logFilterPrompt"> <option value="tab_bts" data-i18n="logBehindTheScene">
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip"> </select>
<span id="refresh" class="button disabled fa">&#xf021;</span>
</div>
<div>
<span id="compactViewToggler" class="button fa"></span>
<span id="clean" class="button fa disabled">&#xf00d;</span>
<span id="clear" class="button fa disabled">&#xf12d;</span>
<span id="filterButton" class="button fa">&#xf0b0;</span><input id="filterInput" type="text" placeholder="logFilterPrompt">
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip">
</div>
</div> </div>
<div id="content"> <div id="content">
<style></style> <style id="tabFilterer"></style>
<style id="popupFilterer"></style>
<table> <table>
<colgroup><col><col><col><col><col></colgroup> <colgroup><col><col><col><col><col></colgroup>
<tbody></tbody> <tbody></tbody>
@ -30,6 +40,7 @@
<div style="display: none;"> <div style="display: none;">
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div> <div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
<div id="hiddenTemplate"><span style="display:none;"></span></div>
</div> </div>
<script src="js/vapi-common.js"></script> <script src="js/vapi-common.js"></script>