From 6f786635f49b1067cdd91ac9efb65071d8fc8ad0 Mon Sep 17 00:00:00 2001 From: gorhill Date: Wed, 6 May 2015 00:30:45 -0400 Subject: [PATCH] extremely early draft of matrix integration within the logger --- platform/chromium/vapi-background.js | 82 +-------- platform/firefox/vapi-background.js | 52 ------ src/css/logger-ui.css | 146 ++++++++++++--- src/js/background.js | 2 +- src/js/contentscript-end.js | 22 +-- src/js/cookies.js | 14 +- src/js/httpsb.js | 12 +- src/js/logger-ui.js | 260 +++++++++++++++++++++++---- src/js/messaging.js | 29 +-- src/js/pagestats.js | 2 - src/js/popup.js | 12 +- src/js/storage.js | 5 + src/js/tab.js | 6 +- src/js/traffic.js | 21 ++- src/js/useragent.js | 5 +- src/logger-ui.html | 24 ++- 16 files changed, 422 insertions(+), 272 deletions(-) diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js index d3bbe38..2725c49 100644 --- a/platform/chromium/vapi-background.js +++ b/platform/chromium/vapi-background.js @@ -103,7 +103,6 @@ vAPI.noTabId = '-1'; vAPI.tabs.registerListeners = function() { var onNavigationClient = this.onNavigation || noopFunc; - var onPopupClient = this.onPopup || noopFunc; var onUpdatedClient = this.onUpdated || noopFunc; var onClosedClient = this.onClosed || noopFunc; @@ -114,59 +113,6 @@ vAPI.tabs.registerListeners = function() { // onDOMContentLoaded -> // onCompleted - var popupCandidates = Object.create(null); - - var PopupCandidate = function(details) { - this.targetTabId = details.tabId; - this.openerTabId = details.sourceTabId; - this.targetURL = details.url; - this.selfDestructionTimer = null; - }; - - PopupCandidate.prototype.selfDestruct = function() { - if ( this.selfDestructionTimer !== null ) { - clearTimeout(this.selfDestructionTimer); - } - delete popupCandidates[this.targetTabId]; - }; - - PopupCandidate.prototype.launchSelfDestruction = function() { - if ( this.selfDestructionTimer !== null ) { - clearTimeout(this.selfDestructionTimer); - } - this.selfDestructionTimer = setTimeout(this.selfDestruct.bind(this), 10000); - }; - - var popupCandidateCreate = function(details) { - var popup = popupCandidates[details.tabId]; - // This really should not happen... - if ( popup !== undefined ) { - return; - } - popup = popupCandidates[details.tabId] = new PopupCandidate(details); - return popup; - }; - - var popupCandidateTest = function(details) { - var popup = popupCandidates[details.tabId]; - if ( popup === undefined ) { - return; - } - popup.targetURL = details.url; - if ( onPopupClient(popup) !== true ) { - return; - } - popup.selfDestruct(); - return true; - }; - - var popupCandidateDestroy = function(details) { - var popup = popupCandidates[details.tabId]; - if ( popup instanceof PopupCandidate ) { - popup.launchSelfDestruction(); - } - }; - // The chrome.webRequest.onBeforeRequest() won't be called for everything // else than `http`/`https`. Thus, in such case, we will bind the tab as // early as possible in order to increase the likelihood of a context @@ -176,30 +122,17 @@ vAPI.tabs.registerListeners = function() { var reGoodForWebRequestAPI = /^https?:\/\//; var onCreatedNavigationTarget = function(details) { - details.tabId = details.tabId.toString(); - //console.debug('onCreatedNavigationTarget: popup candidate tab id %d = "%s"', details.tabId, details.url); - if ( reGoodForWebRequestAPI.test(details.url) === false ) { - details.frameId = 0; - onNavigationClient(details); - } - popupCandidateCreate(details); - popupCandidateTest(details); - }; - - var onBeforeNavigate = function(details) { - if ( details.frameId !== 0 ) { + //console.debug('onCreatedNavigationTarget: tab id %d = "%s"', details.tabId, details.url); + if ( reGoodForWebRequestAPI.test(details.url) ) { return; } - //console.debug('onBeforeNavigate: popup candidate tab id %d = "%s"', details.tabId, details.url); details.tabId = details.tabId.toString(); - popupCandidateTest(details); + details.frameId = 0; + onNavigationClient(details); }; var onUpdated = function(tabId, changeInfo, tab) { tabId = tabId.toString(); - if ( changeInfo.url && popupCandidateTest({ tabId: tabId, url: changeInfo.url }) ) { - return; - } onUpdatedClient(tabId, changeInfo, tab); }; @@ -209,11 +142,7 @@ vAPI.tabs.registerListeners = function() { } details.tabId = details.tabId.toString(); onNavigationClient(details); - //console.debug('onCommitted: popup candidate tab id %d = "%s"', details.tabId, details.url); - if ( popupCandidateTest(details) === true ) { - return; - } - popupCandidateDestroy(details); + //console.debug('onCommitted: tab id %d = "%s"', details.tabId, details.url); }; var onClosed = function(tabId) { @@ -221,7 +150,6 @@ vAPI.tabs.registerListeners = function() { }; chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); - chrome.webNavigation.onBeforeNavigate.addListener(onBeforeNavigate); chrome.webNavigation.onCommitted.addListener(onCommitted); chrome.tabs.onUpdated.addListener(onUpdated); chrome.tabs.onRemoved.addListener(onClosed); diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index e7e1870..9245406 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -390,7 +390,6 @@ vAPI.tabs = {}; vAPI.tabs.registerListeners = function() { // onClosed - handled in tabWatcher.onTabClose - // onPopup - handled in httpObserver.handlePopup for ( var win of this.getWindows() ) { windowWatcher.onReady.call(win); @@ -1101,24 +1100,6 @@ var httpObserver = { ); }, - handlePopup: function(URI, tabId, sourceTabId) { - if ( !sourceTabId ) { - return false; - } - - if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) { - return false; - } - - var result = vAPI.tabs.onPopup({ - targetTabId: tabId, - openerTabId: sourceTabId, - targetURL: URI.asciiSpec - }); - - return result === true; - }, - handleRequest: function(channel, URI, details) { var type = this.typeMap[details.type] || 'other'; var result; @@ -1297,11 +1278,6 @@ var httpObserver = { var channelData = oldChannel.getProperty(this.REQDATAKEY); - if ( this.handlePopup(URI, channelData[3], channelData[2]) ) { - result = this.ABORT; - return; - } - var details = { frameId: channelData[0], parentFrameId: channelData[1], @@ -1345,34 +1321,6 @@ vAPI.net.registerListeners = function() { var details = e.data; var tabId = vAPI.tabs.getTabId(e.target); var sourceTabId = null; - - // Popup candidate - if ( details.openerURL ) { - for ( var tab of vAPI.tabs.getAllSync() ) { - var URI = getBrowserForTab(tab).currentURI; - - // Probably isn't the best method to identify the source tab - if ( URI.spec !== details.openerURL ) { - continue; - } - - sourceTabId = vAPI.tabs.getTabId(tab); - - if ( sourceTabId === tabId ) { - sourceTabId = null; - continue; - } - - URI = Services.io.newURI(details.url, null, null); - - if ( httpObserver.handlePopup(URI, tabId, sourceTabId) ) { - return; - } - - break; - } - } - var lastRequest = httpObserver.lastRequest; lastRequest[1] = lastRequest[0]; lastRequest[0] = { diff --git a/src/css/logger-ui.css b/src/css/logger-ui.css index c67bf16..94355c0 100644 --- a/src/css/logger-ui.css +++ b/src/css/logger-ui.css @@ -21,6 +21,7 @@ body { position: fixed; top: 0; width: 100%; + z-index: 10; } #toolbar .button { background-color: white; @@ -35,6 +36,12 @@ body { #toolbar .button:hover { background-color: #eee; } +body #compactViewToggler.button:before { + content: '\f102'; + } +body.compactView #compactViewToggler.button:before { + content: '\f103'; + } body.filterOff #toolbar #filterButton { opacity: 0.25; } @@ -48,73 +55,154 @@ input:focus { background-color: #ffe; } #content { + font: 13px sans-serif; margin-top: 40px; + width: 100%; } + #content table { border: 0; border-collapse: collapse; direction: ltr; - font: 12px monospace; + table-layout: fixed; width: 100%; } +#content table > colgroup > col:nth-of-type(1) { + width: 6em; + } +#content table > colgroup > col:nth-of-type(2) { + width: 3em; + } +#content table > colgroup > col:nth-of-type(3) { + width: 3em; + } +#content table > colgroup > col:nth-of-type(4) { + width: 8em; + } +#content table > colgroup > col:nth-of-type(5) { + width: calc(100% - 20em); + } #content table tr { background-color: #fafafa; - color: #444; } +#content table tr:nth-of-type(2n+1) { + background-color: #eee; + } + #content table tr.cat_info { color: #00f; } #content table tr.blocked { color: #f00; } -#content table tr:nth-of-type(2n+1) { - background-color: #eee; - } #content table tr.doc { background-color: #666; color: white; text-align: center; } + body:not(.filterOff) #content table tr.hidden { display: none; } -#content table tr td { + +body #content td { border: 1px solid #ccc; + border-top: none; min-width: 0.5em; padding: 3px; vertical-align: top; + white-space: normal; + word-break: break-all; + word-wrap: break-word; } -#content table tr.doc > td { - border: 0; +#content table tr td { + border-top: 1px solid #ccc; } -#content table tr td:nth-of-type(1) { - text-align: center; - white-space: pre; - width: 8em; +#content table tr td:first-of-type { + border-left: none; } -#content table tr td:nth-of-type(2) { - width: 1em; +#content table tr td:last-of-type { + border-right: none; } -#content table tr td:nth-of-type(3) { - white-space: pre; - width: 2em; +body.compactView #content td { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -#content table tr td:nth-of-type(4) { - white-space: pre; - width: 8em; + +#content table tr td:nth-of-type(1) { + text-align: center; + white-space: nowrap; } -#content table tr td:nth-of-type(5) { - border-right: none; - white-space: normal; - word-break: break-all; - word-wrap: break-word; +#content table tr td:nth-of-type(2) { + text-align: center; + white-space: nowrap; } #content table tr.tab_bts > td:nth-of-type(2):before { content: '\f070'; font: 1em FontAwesome; } -#content table tr > td[colspan="3"]:nth-of-type(3) { - white-space: normal; - word-break: break-all; - word-wrap: break-word; +#content table tr.cat_net td:nth-of-type(2) { + cursor: zoom-in; + } +#content table tr.cat_net td:nth-of-type(3) { + font: 12px monospace; + text-align: center; + white-space: nowrap; + } +#content table tr.cat_net td:nth-of-type(5) { + } +#content table tr.cat_net td:nth-of-type(5) > span > span { + opacity: 0.6; + } +#content table tr.cat_net td:nth-of-type(5) > span > b { + font-weight: bold; + opacity: 1; + } + +#popupContainer { + background: #444; + border: 1px solid gray; + cursor: -webkit-grab; + cursor: grab; + display: none; + overflow: hidden; + position: fixed; + z-index: 200; + } +#popupContainer.show { + display: block; + } +#popupContainer > iframe { + border: 0; + padding: 0; + margin: 1.5em 0 0 0; + width: 100%; + } +#focusOverlay { + bottom: 0; + cursor: not-allowed; + display: none; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 100; + } +#popupContainer.show ~ #focusOverlay { + display: block; + } +#movingOverlay { + bottom: 0; + display: none; + left: 0; + position: fixed; + right: 0; + top: 0; + z-index: 300; + } +#popupContainer.moving ~ #movingOverlay { + cursor: -webkit-grabbing; + cursor: grabbing; + display: block; } diff --git a/src/js/background.js b/src/js/background.js index d606d3e..d1bc7e1 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -62,7 +62,7 @@ return { displayTextSize: '13px', externalHostsFiles: '', iconBadgeEnabled: true, - maxLoggedRequests: 50, + maxLoggedRequests: 2000, popupCollapseDomains: false, popupCollapseSpecificDomains: {}, popupHideBlacklisted: false, diff --git a/src/js/contentscript-end.js b/src/js/contentscript-end.js index 013a3fd..caf51d0 100644 --- a/src/js/contentscript-end.js +++ b/src/js/contentscript-end.js @@ -381,7 +381,7 @@ var nodesAddedHandler = function(nodeList, summary) { case 'script': // https://github.com/gorhill/httpswitchboard/issues/252 - // Do not count µMatrix's own script tags, they are not required + // Do not count uMatrix's own script tags, they are not required // to "unbreak" a web page if ( typeof node.id === 'string' && node.id.lastIndexOf('uMatrix-', 0) === 0 ) { break; @@ -404,22 +404,6 @@ var nodesAddedHandler = function(nodeList, summary) { summary.mustReport = true; } break; - - case 'object': - src = (node.data || '').trim(); - if ( src !== '' ) { - summary.pluginSources[src] = true; - summary.mustReport = true; - } - break; - - case 'embed': - src = (node.src || '').trim(); - if ( src !== '' ) { - summary.pluginSources[src] = true; - summary.mustReport = true; - } - break; } } }; @@ -435,7 +419,6 @@ var nodeListsAddedHandler = function(nodeLists) { what: 'contentScriptSummary', locationURL: window.location.href, scriptSources: {}, // to avoid duplicates - pluginSources: {}, // to avoid duplicates mustReport: false }; while ( i-- ) { @@ -458,14 +441,13 @@ var nodeListsAddedHandler = function(nodeLists) { what: 'contentScriptSummary', locationURL: window.location.href, scriptSources: {}, // to avoid duplicates - pluginSources: {}, // to avoid duplicates mustReport: true }; // https://github.com/gorhill/httpswitchboard/issues/25 // & // Looks for inline javascript also in at least one a[href] element. // https://github.com/gorhill/httpswitchboard/issues/131 - nodesAddedHandler(document.querySelectorAll('a[href^="javascript:"],embed,object,script'), summary); + nodesAddedHandler(document.querySelectorAll('a[href^="javascript:"],script'), summary); //console.debug('contentscript-end.js > firstObservationHandler(): found %d script tags in "%s"', Object.keys(summary.scriptSources).length, window.location.href); diff --git a/src/js/cookies.js b/src/js/cookies.js index 94ab05a..ebacfb0 100644 --- a/src/js/cookies.js +++ b/src/js/cookies.js @@ -224,22 +224,22 @@ var recordPageCookie = function(pageStore, cookieKey) { } var cookieEntry = cookieDict[cookieKey]; - var block = µm.mustBlock(pageStore.pageHostname, cookieEntry.hostname, 'cookie'); + var pageHostname = pageStore.pageHostname; + var block = µm.mustBlock(pageHostname, cookieEntry.hostname, 'cookie'); cookieLogEntryBuilder[0] = cookieURLFromCookieEntry(cookieEntry); cookieLogEntryBuilder[2] = cookieEntry.session ? 'session' : 'persistent'; cookieLogEntryBuilder[4] = encodeURIComponent(cookieEntry.name); + var cookieURL = cookieLogEntryBuilder.join(''); + // rhill 2013-11-20: // https://github.com/gorhill/httpswitchboard/issues/60 // Need to URL-encode cookie name - pageStore.recordRequest( - 'cookie', - cookieLogEntryBuilder.join(''), - block - ); + pageStore.recordRequest('cookie', cookieURL, block); + µm.logger.writeOne(pageStore.tabId, 'net', pageHostname, cookieURL, 'cookie', block); - cookieEntry.usedOn[pageStore.pageHostname] = true; + cookieEntry.usedOn[pageHostname] = true; // rhill 2013-11-21: // https://github.com/gorhill/httpswitchboard/issues/65 diff --git a/src/js/httpsb.js b/src/js/httpsb.js index b2fd0aa..667a738 100644 --- a/src/js/httpsb.js +++ b/src/js/httpsb.js @@ -134,17 +134,7 @@ } // Blocked by matrix filtering? - if ( this.mustBlock(srcHostname, desHostname, type) ) { - return true; - } - - // Cookies are not really requests, but are conveniently treated - // as such from matrix filtering point of view only. - if ( type === 'cookie' ) { - return false; - } - - return false; + return this.mustBlock(srcHostname, desHostname, type); }; /******************************************************************************/ diff --git a/src/js/logger-ui.js b/src/js/logger-ui.js index e931785..2c1b105 100644 --- a/src/js/logger-ui.js +++ b/src/js/logger-ui.js @@ -35,7 +35,6 @@ var messager = vAPI.messaging.channel('logger-ui.js'); var inspectedTabId = ''; -var maxEntries = 0; var doc = document; var body = doc.body; var tbody = doc.querySelector('#content tbody'); @@ -43,7 +42,9 @@ var trJunkyard = []; var tdJunkyard = []; var firstVarDataCol = 2; // currently, column 2 (0-based index) var lastVarDataIndex = 3; // currently, d0-d3 +var maxEntries = 5000; var noTabId = ''; +var popupTabId; var prettyRequestTypes = { 'main_frame': 'doc', @@ -53,15 +54,59 @@ var prettyRequestTypes = { }; var timeOptions = { - month: 'short', - day: '2-digit', hour: '2-digit', minute: '2-digit', - second: '2-digit' + second: '2-digit', + hour12: false +}; + +var dateOptions = { + month: 'short', + day: '2-digit' }; /******************************************************************************/ +var escapeHTML = function(s) { + return s.replace(reEscapeLeftBracket, '<') + .replace(reEscapeRightBracket, '>'); +}; + +var reEscapeLeftBracket = //g; + +/******************************************************************************/ + +// Emphasize hostname in URL, as this is what matters in uMatrix's rules. + +var nodeFromURL = function(url) { + var hnbeg = url.indexOf('://'); + if ( hnbeg === -1 ) { + return document.createTextNode(url); + } + hnbeg += 3; + + var hnend = url.indexOf('/', hnbeg); + if ( hnend === -1 ) { + hnend = url.slice(hnbeg).search(/\?#/); + if ( hnend !== -1 ) { + hnend += hnbeg; + } else { + hnend = url.length; + } + } + + var node = renderedURLTemplate.cloneNode(true); + node.childNodes[0].textContent = url.slice(0, hnbeg); + node.childNodes[1].textContent = url.slice(hnbeg, hnend); + node.childNodes[2].textContent = url.slice(hnend); + return node; +}; + +var renderedURLTemplate = document.querySelector('#renderedURLTemplate > span'); + +/******************************************************************************/ + var createCellAt = function(tr, index) { var td = tr.cells[index]; var mustAppend = !td; @@ -82,7 +127,7 @@ var createCellAt = function(tr, index) { /******************************************************************************/ -var createRow = function(entry) { +var createRow = function(layout) { var tr = trJunkyard.pop(); if ( tr ) { tr.className = ''; @@ -98,7 +143,7 @@ var createRow = function(entry) { if ( i === lastVarDataIndex ) { break; } - if ( entry['d' + i] === undefined ) { + if ( layout.charAt(i) !== '1' ) { span += 1; } else { if ( span !== 1 ) { @@ -122,7 +167,7 @@ var createRow = function(entry) { /******************************************************************************/ var createGap = function(url) { - var tr = createRow({ d0: '' }); + var tr = createRow('1'); tr.classList.add('doc'); tr.cells[firstVarDataCol].textContent = url; tbody.insertBefore(tr, tbody.firstChild); @@ -131,48 +176,53 @@ var createGap = function(url) { /******************************************************************************/ var renderLogEntry = function(entry) { + var tr; var fvdc = firstVarDataCol; - var tr = createRow(entry); - - 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); - } - - var time = new Date(entry.tstamp); - tr.cells[0].textContent = time.toLocaleString('fullwide', timeOptions); switch ( entry.cat ) { case 'error': case 'info': + tr = createRow('1'); tr.cells[fvdc].textContent = entry.d0; break; case 'net': + tr = createRow('111'); // 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.d1 === 'doc' ) { - createGap(entry.d2); + if ( entry.d2 === 'doc' ) { + createGap(entry.d1); } - if ( entry.d0 ) { + if ( entry.d3 ) { tr.classList.add('blocked'); tr.cells[fvdc].textContent = '---'; } else { tr.cells[fvdc].textContent = ''; } - tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d1] || entry.d1) + '\t'; - tr.cells[fvdc+2].textContent = entry.d2 + '\t'; + tr.cells[fvdc+1].textContent = (prettyRequestTypes[entry.d2] || entry.d2); + tr.cells[fvdc+2].appendChild(nodeFromURL(entry.d1)); break; default: + tr = createRow('1'); tr.cells[fvdc].textContent = entry.d0; break; } + // Fields common to all rows. + var time = new Date(entry.tstamp); + 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.cat !== '' ) { + tr.classList.add('cat_' + entry.cat); + } + tbody.insertBefore(tr, tbody.firstChild); }; @@ -225,9 +275,9 @@ var renderLogBuffer = function(response) { var truncateLog = function(size) { if ( size === 0 ) { - size = 25000; + size = 5000; } - size = Math.min(size, 25000); + size = Math.min(size, 5000); var tr; while ( tbody.childElementCount > size ) { tr = tbody.lastElementChild; @@ -238,6 +288,10 @@ var truncateLog = function(size) { /******************************************************************************/ var onBufferRead = function(response) { + if ( response.maxLoggedRequests !== maxEntries ) { + maxEntries = response.maxLoggedRequests; + uDom('#maxEntries').val(maxEntries || ''); + } renderLogBuffer(response); setTimeout(readLogBuffer, 1000); }; @@ -264,12 +318,148 @@ var clearBuffer = function() { /******************************************************************************/ -var reloadTab = function() { - messager.send({ what: 'reloadTab', tabId: inspectedTabId }); +var toggleCompactView = function() { + body.classList.toggle( + 'compactView', + body.classList.contains('compactView') === false + ); }; /******************************************************************************/ +var togglePopup = (function() { + var container = null; + var movingOverlay = null; + var popup = null; + var popupObserver = null; + var style = null; + var styleTemplate = 'tr:not(.tab_{{tabId}}) { opacity: 0.1; }'; + var dx, dy; + + var moveTo = function(ev) { + container.style.left = (ev.clientX + dx) + 'px'; + container.style.top = (ev.clientY + dy) + 'px'; + }; + + var onMouseMove = function(ev) { + moveTo(ev); + ev.stopPropagation(); + ev.preventDefault(); + }; + + var onMouseUp = function(ev) { + moveTo(ev); + movingOverlay.removeEventListener('mouseup', onMouseUp); + movingOverlay.removeEventListener('mousemove', onMouseMove); + movingOverlay = null; + container.classList.remove('moving'); + var rect = container.getBoundingClientRect(); + vAPI.localStorage.setItem('popupLastPosition', JSON.stringify({ + x: rect.left, + y: rect.top + })); + ev.stopPropagation(); + ev.preventDefault(); + }; + + var onMove = function(ev) { + container.classList.add('moving'); + var rect = container.getBoundingClientRect(); + dx = rect.left - ev.clientX; + dy = rect.top - ev.clientY; + movingOverlay = document.getElementById('movingOverlay'); + movingOverlay.addEventListener('mousemove', onMouseMove, true); + movingOverlay.addEventListener('mouseup', onMouseUp, true); + ev.stopPropagation(); + ev.preventDefault(); + }; + + var resizePopup = function() { + var popupBody = popup.contentWindow.document.body; + if ( popupBody.clientWidth !== 0 && container.clientWidth !== popupBody.clientWidth ) { + container.style.width = popupBody.clientWidth + 'px'; + } + if ( popupBody.clientHeight !== 0 && popup.clientHeight !== popupBody.clientHeight ) { + popup.style.height = popupBody.clientHeight + 'px'; + } + }; + + var onLoad = function() { + resizePopup(); + popupObserver.observe(popup.contentDocument.body, { + subtree: true, + attributes: true + }); + }; + + var toggleOn = function(td) { + var tr = td.parentNode; + var matches = tr.className.match(/(?:^| )tab_([^ ]+)/); + if ( matches === null ) { + return; + } + var tabId = matches[1]; + if ( tabId === 'bts' ) { + tabId = noTabId; + } + + // Use last position if one is defined + var x, y; + var json = vAPI.localStorage.getItem('popupLastPosition'); + if ( json ) { + try { + var popupLastPosition = JSON.parse(json); + x = popupLastPosition.x; + y = popupLastPosition.y; + } + catch (e) { + } + } + // Fall back to cell position if no position defined + if ( x === undefined ) { + var rect = td.getBoundingClientRect(); + x = rect.left; + y = rect.bottom; + } + container = document.getElementById('popupContainer'); + container.style.left = x + 'px'; + container.style.top = y + 'px'; + container.addEventListener('mousedown', onMove); + popup = container.querySelector('iframe'); + popup.setAttribute('src', 'popup.html?tabId=' + tabId); + popup.addEventListener('load', onLoad); + popupObserver = new MutationObserver(resizePopup); + style = document.querySelector('#content > style'); + style.textContent = styleTemplate.replace('{{tabId}}', tabId); + container.classList.add('show'); + popupTabId = tabId; + }; + + var toggleOff = function() { + style.textContent = ''; + style = null; + popupObserver.disconnect(); + popupObserver = null; + popup.removeEventListener('load', onLoad); + popup.setAttribute('src', ''); + popup = null; + container.classList.remove('show'); + container.removeEventListener('mousedown', onMove); + container = null; + popupTabId = undefined; + }; + + return function(ev) { + if ( popupTabId !== undefined ) { + toggleOff(); + } else { + toggleOn(ev.target); + } + }; +})(); + +/******************************************************************************/ + var onMaxEntriesChanged = function() { var raw = uDom(this).val(); try { @@ -283,7 +473,7 @@ var onMaxEntriesChanged = function() { messager.send({ what: 'userSettings', - name: 'requestLogMaxEntries', + name: 'maxLoggedRequests', value: maxEntries }); @@ -299,17 +489,13 @@ uDom.onLoad(function() { inspectedTabId = matches[1]; } - var onSettingsReady = function(settings) { - maxEntries = settings.requestLogMaxEntries || 0; - uDom('#maxEntries').val(maxEntries || ''); - }; - messager.send({ what: 'getUserSettings' }, onSettingsReady); - readLogBuffer(); - uDom('#reload').on('click', reloadTab); + 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('#focusOverlay').on('click', togglePopup); }); /******************************************************************************/ diff --git a/src/js/messaging.js b/src/js/messaging.js index 7f6d9de..522064d 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -269,11 +269,13 @@ var matrixSnapshot = function(tabId, details) { /******************************************************************************/ var matrixSnapshotFromTabId = function(details, callback) { + // Specific tab id requested? if ( details.tabId ) { callback(matrixSnapshot(details.tabId, details)); return; } + // Otherwise use tab id of current tab vAPI.tabs.get(null, function(tab) { callback(matrixSnapshot(tab.id, details)); }); @@ -373,6 +375,7 @@ var contentScriptSummaryHandler = function(tabId, details) { } var pageStore = µm.pageStoreFromTabId(tabId); var pageURL = pageStore.pageUrl; + var pageHostname = pageStore.pageHostname; var µmuri = µm.URI.set(details.locationURL); var frameURL = µmuri.normalizedURI(); var frameHostname = µmuri.hostname; @@ -380,7 +383,7 @@ var contentScriptSummaryHandler = function(tabId, details) { // https://github.com/gorhill/httpswitchboard/issues/333 // Look-up here whether inline scripting is blocked for the frame. - var inlineScriptBlocked = µm.mustBlock(µm.scopeFromURL(pageURL), frameHostname, 'script'); + var inlineScriptBlocked = µm.mustBlock(pageHostname, frameHostname, 'script'); // scripts // https://github.com/gorhill/httpswitchboard/issues/25 @@ -394,23 +397,8 @@ var contentScriptSummaryHandler = function(tabId, details) { url = frameURL + '{inline_script}'; } r = µm.filterRequest(pageURL, 'script', url); - pageStore.recordRequest('script', url, r !== false, r); - } - } - - // TODO: as of 2014-05-26, not sure this is needed anymore, since µMatrix - // no longer uses chrome.contentSettings API (I think that was the reason - // this code was put in). - // plugins - // https://github.com/gorhill/httpswitchboard/issues/25 - if ( pageStore ) { - urls = details.pluginSources; - for ( url in urls ) { - if ( !urls.hasOwnProperty(url) ) { - continue; - } - r = µm.filterRequest(pageURL, 'plugin', url); - pageStore.recordRequest('plugin', url, r !== false, r); + pageStore.recordRequest('script', url, r !== false); + µm.logger.writeOne(tabId, 'net', pageHostname, url, 'script', r); } } @@ -974,8 +962,9 @@ var onMessage = function(request, sender, callback) { case 'readMany': response = { colorBlind: false, - noTabId: vAPI.noTabId, - entries: µm.logger.readAll(request.tabId) + entries: µm.logger.readAll(request.tabId), + maxLoggedRequests: µm.userSettings.maxLoggedRequests, + noTabId: vAPI.noTabId }; break; diff --git a/src/js/pagestats.js b/src/js/pagestats.js index 47a7c4e..0ffcd48 100644 --- a/src/js/pagestats.js +++ b/src/js/pagestats.js @@ -385,8 +385,6 @@ PageStore.prototype.recordRequest = function(type, url, block) { this.perLoadAllowedRequestCount++; } - µm.logger.writeOne(this.tabId, 'net', block ? '---' : '', type, url); - if ( !this.requests.createEntryIfNotExists(url, type, block) ) { return; } diff --git a/src/js/popup.js b/src/js/popup.js index a7d854a..74dea0f 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1177,9 +1177,19 @@ var onMatrixSnapshotReady = function(response) { /******************************************************************************/ var queryMatrixSnapshot = function(callback) { + 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 request = { what: 'matrixSnapshot', - tabId: matrixSnapshot.tabId, + tabId: tabId, tabURL: matrixSnapshot.url }; var snapshotReceived = function(response) { diff --git a/src/js/storage.js b/src/js/storage.js index 72972e4..1e301da 100644 --- a/src/js/storage.js +++ b/src/js/storage.js @@ -406,6 +406,11 @@ µMatrix.assetUpdatedHandler = function(details) { var path = details.path || ''; + + if ( path !== '' ) { + this.logger.writeOne('', 'info', 'asset updated: ' + path); + } + if ( this.liveHostsFiles.hasOwnProperty(path) === false ) { return; } diff --git a/src/js/tab.js b/src/js/tab.js index e36e5c3..77fd2bc 100644 --- a/src/js/tab.js +++ b/src/js/tab.js @@ -607,9 +607,11 @@ vAPI.tabs.registerListeners(); µm.recordFromTabId = function(tabId, type, url, blocked) { var pageStore = this.pageStoreFromTabId(tabId); - if ( pageStore ) { - pageStore.recordRequest(type, url, blocked); + if ( pageStore === null ) { + return; } + pageStore.recordRequest(type, url, blocked); + this.logger.writeOne(tabId, 'net', pageStore.pageHostname, url, type, blocked); }; /******************************************************************************/ diff --git a/src/js/traffic.js b/src/js/traffic.js index 2dd19c9..a77107e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -40,17 +40,18 @@ var onBeforeRootFrameRequestHandler = function(details) { µm.tabContextManager.push(tabId, requestURL); var tabContext = µm.tabContextManager.mustLookup(tabId); + var rootHostname = tabContext.rootHostname; var pageStore = µm.bindTabToPageStats(tabId); // Disallow request as per matrix? - var block = µm.mustBlock(tabContext.rootHostname, details.hostname, 'doc'); + var block = µm.mustBlock(rootHostname, details.hostname, 'doc'); pageStore.recordRequest('doc', requestURL, block); + µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, 'doc', block); // Not blocked if ( !block ) { // rhill 2013-11-07: Senseless to do this for behind-the-scene requests. - // rhill 2013-12-03: Do this here only for root frames. µm.cookieHunter.recordPageCookies(pageStore); return; } @@ -114,20 +115,21 @@ var onBeforeRequestHandler = function(details) { // https://github.com/gorhill/httpswitchboard/issues/91#issuecomment-37180275 var tabContext = µm.tabContextManager.mustLookup(details.tabId); var tabId = tabContext.tabId; + var rootHostname = tabContext.rootHostname; // Enforce strict secure connection? var block = false; if ( tabContext.secure && µm.URI.isSecureScheme(requestScheme) === false && - µm.tMatrix.evaluateSwitchZ('https-strict', tabContext.rootHostname) + µm.tMatrix.evaluateSwitchZ('https-strict', rootHostname) ) { block = true; } // Disallow request as per temporary matrix? if ( block === false ) { - block = µm.mustBlock(tabContext.rootHostname, details.hostname, requestType); + block = µm.mustBlock(rootHostname, details.hostname, requestType); } // Record request. @@ -136,8 +138,9 @@ var onBeforeRequestHandler = function(details) { // processing has already been performed, and that a synthetic URL has // been constructed for logging purpose. Use this synthetic URL if // it is available. - var pageStore = µm.mustPageStoreFromTabId(tabContext.tabId); + var pageStore = µm.mustPageStoreFromTabId(tabId); pageStore.recordRequest(requestType, requestURL, block); + µm.logger.writeOne(tabId, 'net', rootHostname, requestURL, requestType, block); // Allowed? if ( !block ) { @@ -198,6 +201,7 @@ var onBeforeSendHeadersHandler = function(details) { if ( linkAuditor !== '' ) { var block = µm.userSettings.processHyperlinkAuditing; pageStore.recordRequest('other', requestURL + '{Ping-To:' + linkAuditor + '}', block); + µm.logger.writeOne(tabId, 'net', '', requestURL, 'ping', block); if ( block ) { µm.hyperlinkAuditingFoiledCounter += 1; return { 'cancel': true }; @@ -293,7 +297,8 @@ var onMainDocHeadersReceived = function(details) { // console.debug('onMainDocHeadersReceived()> "%s": %o', requestURL, details); - var blockScript = µm.mustBlock(tabContext.rootHostname, tabContext.rootHostname, 'script'); + var rootHostname = tabContext.rootHostname; + var blockScript = µm.mustBlock(rootHostname, rootHostname, 'script'); // https://github.com/gorhill/httpswitchboard/issues/181 var pageStore = µm.pageStoreFromTabId(tabId); @@ -305,7 +310,7 @@ var onMainDocHeadersReceived = function(details) { return; } - µm.logger.writeOne(tabId, 'net', '---', 'inline-script', requestURL); + µm.logger.writeOne(tabId, 'net', rootHostname, requestURL + '{inline_script}', 'script', true); // If javascript not allowed, say so through a `Content-Security-Policy` directive. details.responseHeaders.push({ @@ -356,7 +361,7 @@ var onSubDocHeadersReceived = function(details) { // console.debug('onSubDocHeadersReceived()> FRAME CSP "%s": %o, scope="%s"', details.url, details, pageURL); - µm.logger.writeOne(tabId, 'net', '---', 'inline-script', details.url); + µm.logger.writeOne(tabId, 'net', tabContext.rootHostname, details.url + '{inline_script}', 'script', true); // If javascript not allowed, say so through a `Content-Security-Policy` directive. details.responseHeaders.push({ diff --git a/src/js/useragent.js b/src/js/useragent.js index ae1bfed..7ca264c 100644 --- a/src/js/useragent.js +++ b/src/js/useragent.js @@ -1,7 +1,7 @@ /******************************************************************************* - µMatrix - a Chromium browser extension to black/white list requests. - Copyright (C) 2014 Raymond Hill + uMatrix - a Chromium browser extension to black/white list requests. + Copyright (C) 2014-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 @@ -60,6 +60,7 @@ var userAgentSpoofer = function(force) { if ( uaStr === '' ) { µm.userAgentReplaceStr = userAgentRandomPicker(); µm.userAgentReplaceStrBirth = Date.now(); + µm.logger.writeOne('', 'info', 'spoofing user agent with: ' + µm.userAgentReplaceStr); } }; diff --git a/src/logger-ui.html b/src/logger-ui.html index 5d91057..faa78ed 100644 --- a/src/logger-ui.html +++ b/src/logger-ui.html @@ -6,20 +6,38 @@ uMatrix log - + +
- +
+
-
+ + + + +
+ +
+ +
+
+
+ +
+
+
+ +