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",
"description":"English: No non-blocked requests logged for this page"
},
"logAll":{
"message":"All",
"description":"Appears in the logger's tab selector"
},
"logBehindTheScene":{
"message":"Behind the scene",
"description":"Pretty name for behind-the-scene network requests"

View File

@ -7,7 +7,6 @@ body {
margin: 0;
overflow-x: hidden;
padding: 0;
white-space: nowrap;
width: 100%;
}
#toolbar {
@ -16,7 +15,7 @@ body {
box-sizing: border-box;
left: 0;
margin: 0;
padding: 0 1em;
padding: 0.5em 1em;
position: fixed;
top: 0;
width: 100%;
@ -28,7 +27,7 @@ body {
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-size: 20px;
font-size: 150%;
margin: 0;
padding: 8px;
}
@ -39,6 +38,19 @@ body {
#toolbar .button:hover {
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 {
content: '\f102';
}
@ -55,14 +67,13 @@ body.f #filterButton {
background-color: #fee;
}
#maxEntries {
margin-left: 3em;
margin: 0 2em;
}
input:focus {
background-color: #ffe;
}
#content {
font: 13px sans-serif;
margin-top: 3.5em;
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 tbody = document.querySelector('#content tbody');
var trJunkyard = [];
@ -40,6 +49,8 @@ var maxEntries = 5000;
var noTabId = '';
var allTabIds = {};
var hiddenTemplate = document.querySelector('#hiddenTemplate > span');
var prettyRequestTypes = {
'main_frame': 'doc',
'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.
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 tr = createRow('1');
tr.classList.add('tab');
@ -246,11 +277,9 @@ var renderLogEntry = function(entry) {
tr.cells[0].title = time.toLocaleDateString('fullwide', dateOptions);
if ( entry.tab ) {
tr.classList.add('tab');
tr.classList.add('tab', classNameFromTabId(entry.tab));
if ( entry.tab === noTabId ) {
tr.classList.add('tab_bts');
} else if ( entry.tab !== '' ) {
tr.classList.add('tab_' + entry.tab);
tr.cells[1].appendChild(createHiddenTextNode('bts'));
}
}
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) {
if ( size === 0 ) {
size = 5000;
@ -343,28 +444,7 @@ var onLogBufferRead = function(response) {
// 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, 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;
var rowVoided = synchronizeTabIds(response.tabIds);
renderLogEntries(response);
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 raw = uDom(this).val();
try {
@ -649,7 +764,7 @@ var popupManager = (function() {
popupObserver = new MutationObserver(resizePopup);
container.appendChild(popup);
style = document.querySelector('#content > style');
style = document.getElementById('popupFilterer');
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
document.body.classList.add('popupOn');
@ -701,6 +816,8 @@ var popupManager = (function() {
uDom.onLoad(function() {
readLogBuffer();
uDom('#pageSelector').on('change', pageSelectorChanged);
uDom('#refresh').on('click', reloadTab);
uDom('#compactViewToggler').on('click', toggleCompactView);
uDom('#clean').on('click', cleanBuffer);
uDom('#clear').on('click', clearBuffer);

View File

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

View File

@ -295,6 +295,8 @@ PageStore.prototype.init = function(tabId) {
var tabContext = µb.tabContextManager.lookup(tabId);
this.tabId = tabId;
this.tabHostname = tabContext.rootHostname;
this.title = tabContext.rawURL;
this.rawURL = tabContext.rawURL;
this.hostnameToCountMap = {};
this.contentLastModified = 0;
this.frames = {};
@ -334,6 +336,7 @@ PageStore.prototype.reuse = function(context) {
if ( context === 'tabUpdated' ) {
// As part of https://github.com/chrisaljoudi/uBlock/issues/405
// URL changed, force a re-evaluation of filtering switch
this.rawURL = tabContext.rawURL;
this.netFilteringReadTime = 0;
return this;
}
@ -354,6 +357,9 @@ PageStore.prototype.dispose = function() {
// need to release the memory taken by these, which can amount to
// sizeable enough chunks (especially requests, through the request URL
// used as a key).
this.tabHostname = '';
this.title = '';
this.rawURL = '';
this.hostnameToCountMap = null;
this.disposeFrameStores();
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.
µ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
if ( µb.tabContextManager.exists(tabId) === false ) {
@ -540,6 +538,8 @@ vAPI.tabs.registerListeners();
return pageStore;
}
this.updateTitle(tabId);
// 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
// be all reset.
@ -570,8 +570,65 @@ vAPI.tabs.registerListeners();
// 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].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
@ -612,7 +669,6 @@ var pageStoreJanitor = function() {
setTimeout(pageStoreJanitor, pageStoreJanitorPeriod);
/******************************************************************************/
/******************************************************************************/
})();

View File

@ -9,15 +9,25 @@
<body class="compactView f">
<div id="toolbar">
<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>
<select id="pageSelector">
<option value="" data-i18n="logAll">
<option value="tab_bts" data-i18n="logBehindTheScene">
</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 id="content">
<style></style>
<style id="tabFilterer"></style>
<style id="popupFilterer"></style>
<table>
<colgroup><col><col><col><col><col></colgroup>
<tbody></tbody>
@ -30,6 +40,7 @@
<div style="display: none;">
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
<div id="hiddenTemplate"><span style="display:none;"></span></div>
</div>
<script src="js/vapi-common.js"></script>