mirror of https://github.com/gorhill/uBlock.git
importing uMatrix unified logger
This commit is contained in:
parent
eba1161f0d
commit
f9652b5f57
|
@ -1908,7 +1908,7 @@ var optionsObserver = {
|
|||
}
|
||||
|
||||
this.setupOptionsButton(doc, 'showDashboardButton', 'dashboard.html');
|
||||
this.setupOptionsButton(doc, 'showNetworkLogButton', 'devtools.html');
|
||||
this.setupOptionsButton(doc, 'showNetworkLogButton', 'logger-ui.html');
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
body {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar {
|
||||
background-color: white;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar .button {
|
||||
background-color: white;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
#toolbar .button:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
body.filterOff #toolbar #filterButton {
|
||||
opacity: 0.25;
|
||||
}
|
||||
#filterExpression.bad {
|
||||
background-color: #fee;
|
||||
}
|
||||
#maxEntries {
|
||||
margin-left: 3em;
|
||||
}
|
||||
input:focus {
|
||||
background-color: #ffe;
|
||||
}
|
||||
#content {
|
||||
margin-top: 40px;
|
||||
}
|
||||
#content table {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
direction: ltr;
|
||||
font: 12px monospace;
|
||||
width: 100%;
|
||||
}
|
||||
#content table tr.docBoundary {
|
||||
background-color: #666;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
#content table tr.docBoundary > td:first-child {
|
||||
padding: 1em 0;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#content table tr.blocked {
|
||||
background-color: rgba(192, 0, 0, 0.1);
|
||||
}
|
||||
body.colorBlind #content table tr.blocked {
|
||||
background-color: rgba(0, 19, 110, 0.1);
|
||||
}
|
||||
#content table tr.allowed {
|
||||
background-color: rgba(0, 160, 0, 0.1);
|
||||
}
|
||||
body.colorBlind #content table tr.allowed {
|
||||
background-color: rgba(255, 194, 57, 0.1)
|
||||
}
|
||||
#content table tr.maindoc {
|
||||
}
|
||||
#content table tr.cosmetic {
|
||||
background-color: rgba(255, 255, 0, 0.1);
|
||||
}
|
||||
body:not(.filterOff) #content table tr.hidden {
|
||||
display: none;
|
||||
}
|
||||
#content table tr td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 3px;
|
||||
vertical-align: top;
|
||||
}
|
||||
#content table tr td:nth-of-type(1) {
|
||||
padding: 3px 0;
|
||||
text-align: center;
|
||||
white-space: pre;
|
||||
width: 1em;
|
||||
}
|
||||
#content table tr td:nth-of-type(2) {
|
||||
white-space: normal;
|
||||
width: 25%;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#content table tr td:nth-of-type(3) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
#content table tr td:nth-of-type(4) {
|
||||
border-right: none;
|
||||
white-space: normal;
|
||||
width: 60%;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#content table tr td:nth-of-type(4) b {
|
||||
font-weight: normal;
|
||||
}
|
||||
#content table tr.blocked td:nth-of-type(4) b {
|
||||
background-color: rgba(192, 0, 0, 0.2);
|
||||
}
|
||||
body.colorBlind #content table tr.blocked td:nth-of-type(4) b {
|
||||
background-color: rgba(0, 19, 110, 0.2);
|
||||
}
|
||||
#content table tr.allowed td:nth-of-type(4) b {
|
||||
background-color: rgba(0, 160, 0, 0.2);
|
||||
}
|
||||
body.colorBlind #content table tr.allowed td:nth-of-type(4) b {
|
||||
background-color: rgba(255, 194, 57, 0.2);
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
button {
|
||||
opacity: 0.25;
|
||||
}
|
||||
button:hover {
|
||||
opacity: 0.75;
|
||||
}
|
||||
#toolbar {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
height: 4em;
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar > * {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#toolbar button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 2em;
|
||||
margin: 0 0 0 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#pageSelector {
|
||||
max-width: 70%;
|
||||
}
|
||||
#toolbar #refresh {
|
||||
margin-left: 4px;
|
||||
}
|
||||
select {
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
min-width: 20em;
|
||||
max-width: 40em;
|
||||
}
|
||||
select option {
|
||||
max-width: 40em;
|
||||
}
|
||||
#extras {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
#extras > span {
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
#extras > span > button {
|
||||
margin-left: 0.2em;
|
||||
}
|
||||
#extras > span > div {
|
||||
background-color: white;
|
||||
border: 0;
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
}
|
||||
#extras > span > div > * {
|
||||
border: 1px solid gray;
|
||||
}
|
||||
#extras > span > button.enabled {
|
||||
opacity: 1;
|
||||
}
|
||||
#extras > span > button.enabled + div {
|
||||
display: block;
|
||||
}
|
||||
#filterMatcher {
|
||||
border: 0;
|
||||
padding: 0.5em;
|
||||
margin: 0;
|
||||
}
|
||||
#filteringResult {
|
||||
background-color: #eee;
|
||||
font-family: monospace;
|
||||
}
|
||||
#filteringResult.empty {
|
||||
background-color: transparent;
|
||||
}
|
||||
#popup {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#content {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
height: calc(100vh - 4em);
|
||||
margin-top: 4em;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
body {
|
||||
background-color: white;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
color: black;
|
||||
-moz-box-sizing: border-box;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar {
|
||||
background-color: white;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
left: 0;
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
}
|
||||
#toolbar .button {
|
||||
background-color: white;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
margin: 0;
|
||||
padding: 8px;
|
||||
}
|
||||
#toolbar .button.disabled {
|
||||
opacity: 0.2;
|
||||
pointer-events: none;
|
||||
}
|
||||
#toolbar .button:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
body #compactViewToggler.button:before {
|
||||
content: '\f102';
|
||||
}
|
||||
body.compactView #compactViewToggler.button:before {
|
||||
content: '\f103';
|
||||
}
|
||||
#filterButton {
|
||||
opacity: 0.25;
|
||||
}
|
||||
body.f #filterButton {
|
||||
opacity: 1;
|
||||
}
|
||||
#filterInput.bad {
|
||||
background-color: #fee;
|
||||
}
|
||||
#maxEntries {
|
||||
margin-left: 3em;
|
||||
}
|
||||
input:focus {
|
||||
background-color: #ffe;
|
||||
}
|
||||
#content {
|
||||
font: 13px sans-serif;
|
||||
margin-top: 3.5em;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#content table {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
direction: ltr;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(1) {
|
||||
width: 5em;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(2) {
|
||||
width: 2.5em;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(3) {
|
||||
width: 2.5em;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(4) {
|
||||
width: 20%;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(5) {
|
||||
width: 6em;
|
||||
}
|
||||
#content table > colgroup > col:nth-of-type(6) {
|
||||
width: calc(100% - 16em - 20%);
|
||||
}
|
||||
body.f table tr.f {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#content table tr.cat_info {
|
||||
color: #00f;
|
||||
}
|
||||
#content table tr.blocked {
|
||||
background-color: rgba(192, 0, 0, 0.1);
|
||||
}
|
||||
body.colorBlind #content table tr.blocked {
|
||||
background-color: rgba(0, 19, 110, 0.1);
|
||||
}
|
||||
#content table tr.allowed {
|
||||
background-color: rgba(0, 160, 0, 0.1);
|
||||
}
|
||||
body.colorBlind #content table tr.allowed {
|
||||
background-color: rgba(255, 194, 57, 0.1)
|
||||
}
|
||||
#content table tr.cosmetic {
|
||||
background-color: rgba(255, 255, 0, 0.1);
|
||||
}
|
||||
#content table tr.maindoc {
|
||||
background-color: #666;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
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 td {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
#content table tr td:first-of-type {
|
||||
border-left: none;
|
||||
}
|
||||
#content table tr td:last-of-type {
|
||||
border-right: none;
|
||||
}
|
||||
body.compactView #content td {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#content table tr td:nth-of-type(1) {
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#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.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),
|
||||
#content table tr.cat_cosmetic td:nth-of-type(3) {
|
||||
font: 12px monospace;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
#content table tr.cat_net td:nth-of-type(6) > span > b {
|
||||
font-weight: bold;
|
||||
}
|
||||
#content table tr td:nth-of-type(6) b {
|
||||
font-weight: normal;
|
||||
}
|
||||
#content table tr.blocked td:nth-of-type(6) b {
|
||||
background-color: rgba(192, 0, 0, 0.2);
|
||||
}
|
||||
body.colorBlind #content table tr.blocked td:nth-of-type(6) b {
|
||||
background-color: rgba(0, 19, 110, 0.2);
|
||||
}
|
||||
#content table tr.allowed td:nth-of-type(6) b {
|
||||
background-color: rgba(0, 160, 0, 0.2);
|
||||
}
|
||||
body.colorBlind #content table tr.allowed td:nth-of-type(6) b {
|
||||
background-color: rgba(255, 194, 57, 0.2);
|
||||
}
|
||||
|
||||
#popupContainer {
|
||||
background: white;
|
||||
border: 1px solid gray;
|
||||
border-radius: 3px;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
}
|
||||
body.popupOn #popupContainer {
|
||||
display: block;
|
||||
}
|
||||
#popupContainer > div {
|
||||
background: #aaa;
|
||||
border: 0;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
height: 1.2em;
|
||||
}
|
||||
body[dir="ltr"] #popupContainer > div {
|
||||
direction: rtl;
|
||||
}
|
||||
body[dir="rtl"] #popupContainer > div {
|
||||
direction: ltr;
|
||||
}
|
||||
#popupContainer > div > span {
|
||||
color: #eee;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font: 14px FontAwesome;
|
||||
padding: 0 3px;
|
||||
}
|
||||
#popupContainer > div > span:hover {
|
||||
color: white;
|
||||
}
|
||||
#popupContainer > iframe {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#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;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/devtool-log.css">
|
||||
<title>uBlock log</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="toolbar">
|
||||
<span id="reload" class="button fa"></span>
|
||||
<span id="clear" class="button fa"></span>
|
||||
<span id="filterButton" class="button fa"></span><input id="filterExpression" type="text" placeholder="logFilterPrompt">
|
||||
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip">
|
||||
</div>
|
||||
<div id="content">
|
||||
<table><tbody></tbody></table>
|
||||
</div>
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/devtool-log.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,50 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
|
||||
<title data-i18n="statsPageName"></title>
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/devtools.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="toolbar">
|
||||
<select id="pageSelector"></select>
|
||||
<button id="refresh" class="fa" type="button"></button>
|
||||
<span id="extras">
|
||||
<span>
|
||||
<button class="fa toolToggler" type="button"></button>
|
||||
<div>
|
||||
<div id="filterMatcher">
|
||||
<p><label>URL of context</label><br>
|
||||
<input type="text" size="40">
|
||||
<p><label>URL of resource</label><br>
|
||||
<input type="text" size="40">
|
||||
<p><label>Type of resource</label><br>
|
||||
<input type="text" size="40">
|
||||
<p><label>Matching static filter</label><br>
|
||||
<span id="filteringResult" class="empty"> </span>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
<span>
|
||||
<button id="popupToggler" class="fa toolToggler" type="button"></button>
|
||||
<div>
|
||||
<iframe id="popup" src=""></iframe>
|
||||
</div>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<iframe id="content"></iframe>
|
||||
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/devtools.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -34,7 +34,12 @@
|
|||
// https://github.com/chrisaljoudi/uBlock/issues/464
|
||||
if ( document instanceof HTMLDocument === false ) {
|
||||
//console.debug('contentscript-end.js > not a HTLMDocument');
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// I've seen this happens on Firefox
|
||||
if ( window.location === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This can happen
|
||||
|
|
|
@ -1,448 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
sessbench - a browser extension to benchmark browser session.
|
||||
Copyright (C) 2013 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/sessbench
|
||||
|
||||
TODO: cleanup/refactor
|
||||
*/
|
||||
|
||||
/* global vAPI, uDom */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('devtool-log.js');
|
||||
|
||||
var inspectedTabId = '';
|
||||
var doc = document;
|
||||
var body = doc.body;
|
||||
var tbody = doc.querySelector('#content tbody');
|
||||
var row1Junkyard = [];
|
||||
var row4Junkyard = [];
|
||||
var reFilter = null;
|
||||
var filterTargetTestResult = true;
|
||||
var maxEntries = 0;
|
||||
|
||||
var prettyRequestTypes = {
|
||||
'main_frame': 'doc',
|
||||
'stylesheet': 'css',
|
||||
'sub_frame': 'frame',
|
||||
'xmlhttprequest': 'xhr'
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var escapeHTML = function(s) {
|
||||
return s.replace(reEscapeLeftBracket, '<')
|
||||
.replace(reEscapeRightBracket, '>');
|
||||
};
|
||||
|
||||
var reEscapeLeftBracket = /</g;
|
||||
var reEscapeRightBracket = />/g;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderURL = function(url, filter) {
|
||||
if ( filter.charAt(0) !== 's' ) {
|
||||
return escapeHTML(url);
|
||||
}
|
||||
// make a regex out of the filter
|
||||
var reText = filter.slice(3);
|
||||
var pos = reText.indexOf('$');
|
||||
if ( pos > 0 ) {
|
||||
reText = reText.slice(0, pos);
|
||||
}
|
||||
if ( reText === '*' ) {
|
||||
reText = '\\*';
|
||||
} else if ( reText.charAt(0) === '/' && reText.slice(-1) === '/' ) {
|
||||
reText = reText.slice(1, -1);
|
||||
} else {
|
||||
reText = reText
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\?/g, '\\?')
|
||||
.replace('||', '')
|
||||
.replace(/\^/g, '.')
|
||||
.replace(/^\|/g, '^')
|
||||
.replace(/\|$/g, '$')
|
||||
.replace(/\*/g, '.*')
|
||||
;
|
||||
}
|
||||
var re = new RegExp(reText, 'gi');
|
||||
var matches = re.exec(url);
|
||||
var renderedURL = url;
|
||||
|
||||
if ( matches && matches[0].length ) {
|
||||
renderedURL = escapeHTML(url.slice(0, matches.index)) +
|
||||
'<b>' +
|
||||
escapeHTML(url.slice(matches.index, re.lastIndex)) +
|
||||
'</b>' +
|
||||
escapeHTML(url.slice(re.lastIndex));
|
||||
} else {
|
||||
renderedURL = escapeHTML(renderedURL);
|
||||
}
|
||||
|
||||
return renderedURL;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createRow = function() {
|
||||
var tr = row4Junkyard.pop();
|
||||
if ( tr ) {
|
||||
tr.className = '';
|
||||
return tr;
|
||||
}
|
||||
tr = doc.createElement('tr');
|
||||
tr.appendChild(doc.createElement('td'));
|
||||
tr.appendChild(doc.createElement('td'));
|
||||
tr.appendChild(doc.createElement('td'));
|
||||
tr.appendChild(doc.createElement('td'));
|
||||
return tr;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createGap = function(url) {
|
||||
var tr = row1Junkyard.pop();
|
||||
if ( !tr ) {
|
||||
tr = doc.createElement('tr');
|
||||
tr.classList.add('docBoundary');
|
||||
tr.appendChild(doc.createElement('td'));
|
||||
tr.cells[0].setAttribute('colspan', '4');
|
||||
}
|
||||
tr.cells[0].textContent = url;
|
||||
tbody.insertBefore(tr, tbody.firstChild);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogEntry = function(entry) {
|
||||
var tr = createRow();
|
||||
|
||||
// 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.type === 'main_frame' ) {
|
||||
createGap(entry.url);
|
||||
tr.classList.add('maindoc');
|
||||
}
|
||||
|
||||
// Cosmetic filter?
|
||||
if ( entry.result.charAt(0) === 'c' ) {
|
||||
tr.classList.add('cosmetic');
|
||||
}
|
||||
|
||||
if ( entry.result.charAt(1) === 'b' ) {
|
||||
tr.classList.add('blocked');
|
||||
tr.cells[0].textContent = ' -\u00A0';
|
||||
} else if ( entry.result.charAt(1) === 'a' ) {
|
||||
tr.classList.add('allowed');
|
||||
tr.cells[0].textContent = ' +\u00A0';
|
||||
} else {
|
||||
tr.cells[0].textContent = '';
|
||||
}
|
||||
|
||||
var filterText = entry.result.slice(3);
|
||||
if ( entry.result.lastIndexOf('sa', 0) === 0 ) {
|
||||
filterText = '@@' + filterText;
|
||||
}
|
||||
|
||||
tr.cells[1].textContent = filterText + '\t';
|
||||
tr.cells[2].textContent = (prettyRequestTypes[entry.type] || entry.type) + '\t';
|
||||
vAPI.insertHTML(tr.cells[3], renderURL(entry.url, entry.result));
|
||||
applyFilterToRow(tr);
|
||||
tbody.insertBefore(tr, tbody.firstChild);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogBuffer = function(response) {
|
||||
body.classList.toggle('colorBlind', response.colorBlind);
|
||||
|
||||
var buffer = response.entries;
|
||||
if ( buffer.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve scroll position
|
||||
var height = tbody.offsetHeight;
|
||||
|
||||
var n = buffer.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
renderLogEntry(buffer[i]);
|
||||
}
|
||||
|
||||
// Prevent logger from growing infinitely and eating all memory. For
|
||||
// instance someone could forget that it is left opened for some
|
||||
// dynamically refreshed pages.
|
||||
truncateLog(maxEntries);
|
||||
|
||||
var yDelta = tbody.offsetHeight - height;
|
||||
if ( yDelta === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chromium:
|
||||
// body.scrollTop = good value
|
||||
// body.parentNode.scrollTop = 0
|
||||
if ( body.scrollTop !== 0 ) {
|
||||
body.scrollTop += yDelta;
|
||||
return;
|
||||
}
|
||||
|
||||
// Firefox:
|
||||
// body.scrollTop = 0
|
||||
// body.parentNode.scrollTop = good value
|
||||
var parentNode = body.parentNode;
|
||||
if ( parentNode && parentNode.scrollTop !== 0 ) {
|
||||
parentNode.scrollTop += yDelta;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var truncateLog = function(size) {
|
||||
if ( size === 0 ) {
|
||||
size = 25000;
|
||||
}
|
||||
size = Math.min(size, 25000);
|
||||
var tr;
|
||||
while ( tbody.childElementCount > size ) {
|
||||
tr = tbody.lastElementChild;
|
||||
// https://github.com/gorhill/uBlock/issues/123
|
||||
// Triage according to row type.
|
||||
if ( tr.cells.length === 1 ) {
|
||||
row1Junkyard.push(tr);
|
||||
} else {
|
||||
row4Junkyard.push(tr);
|
||||
}
|
||||
tbody.removeChild(tr);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onBufferRead = function(response) {
|
||||
renderLogBuffer(response);
|
||||
setTimeout(readLogBuffer, 1000);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This can be called only once, at init time. After that, this will be called
|
||||
// automatically. If called after init time, this will be messy, and this would
|
||||
// require a bit more code to ensure no multi time out events.
|
||||
|
||||
var readLogBuffer = function() {
|
||||
messager.send({ what: 'readLogBuffer', tabId: inspectedTabId }, onBufferRead);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var clearBuffer = function() {
|
||||
var tr;
|
||||
while ( tbody.firstChild !== null ) {
|
||||
tr = tbody.lastElementChild;
|
||||
// https://github.com/gorhill/uBlock/issues/123
|
||||
// Triage according to row type.
|
||||
if ( tr.cells.length === 1 ) {
|
||||
row1Junkyard.push(tr);
|
||||
} else {
|
||||
row4Junkyard.push(tr);
|
||||
}
|
||||
tbody.removeChild(tr);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var reloadTab = function() {
|
||||
messager.send({ what: 'reloadTab', tabId: inspectedTabId });
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var applyFilterToRow = function(row) {
|
||||
var re = reFilter;
|
||||
if ( re === null || re.test(row.textContent) === filterTargetTestResult ) {
|
||||
row.classList.remove('hidden');
|
||||
} else {
|
||||
row.classList.add('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var applyFilter = function() {
|
||||
if ( reFilter === null ) {
|
||||
unapplyFilter();
|
||||
return;
|
||||
}
|
||||
var row = document.querySelector('#content tr');
|
||||
if ( row === null ) {
|
||||
return;
|
||||
}
|
||||
var re = reFilter;
|
||||
var target = filterTargetTestResult;
|
||||
while ( row !== null ) {
|
||||
if ( re.test(row.textContent) === target ) {
|
||||
row.classList.remove('hidden');
|
||||
} else {
|
||||
row.classList.add('hidden');
|
||||
}
|
||||
row = row.nextSibling;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var unapplyFilter = function() {
|
||||
var row = document.querySelector('#content tr');
|
||||
if ( row === null ) {
|
||||
return;
|
||||
}
|
||||
while ( row !== null ) {
|
||||
row.classList.remove('hidden');
|
||||
row = row.nextSibling;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onFilterButton = function() {
|
||||
uDom('body').toggleClass('filterOff');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onFilterChanged = function() {
|
||||
var filterExpression = uDom('#filterExpression');
|
||||
var filterRaw = filterExpression.val().trim();
|
||||
|
||||
// Assume good filter expression
|
||||
filterExpression.removeClass('bad');
|
||||
|
||||
// Invert resultset?
|
||||
filterTargetTestResult = filterRaw.charAt(0) !== '!';
|
||||
if ( filterTargetTestResult === false ) {
|
||||
filterRaw = filterRaw.slice(1);
|
||||
}
|
||||
|
||||
// No filter
|
||||
if ( filterRaw === '') {
|
||||
reFilter = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// Regex?
|
||||
if ( filterRaw.length > 1 && filterRaw.charAt(0) === '/' && filterRaw.slice(-1) === '/' ) {
|
||||
try {
|
||||
reFilter = new RegExp(filterRaw.slice(1, -1));
|
||||
} catch (e) {
|
||||
reFilter = null;
|
||||
filterExpression.addClass('bad');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Plain filtering
|
||||
var filterParts = filterRaw
|
||||
.replace(/^\s*-(\s+|$)/, '-\xA0 ')
|
||||
.replace(/^\s*\\+(\s+|$)/, '+\xA0 ')
|
||||
.split(/[ \f\n\r\t\v]+/);
|
||||
var n = filterParts.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
filterParts[i] = filterParts[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
reFilter = new RegExp(filterParts.join('.*\\s+.*'));
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onFilterChangedAsync = (function() {
|
||||
var timer = null;
|
||||
|
||||
var commit = function() {
|
||||
timer = null;
|
||||
onFilterChanged();
|
||||
applyFilter();
|
||||
};
|
||||
|
||||
var changed = function() {
|
||||
if ( timer !== null ) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(commit, 750);
|
||||
};
|
||||
|
||||
return changed;
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMaxEntriesChanged = function() {
|
||||
var raw = uDom(this).val();
|
||||
try {
|
||||
maxEntries = parseInt(raw, 10);
|
||||
if ( isNaN(maxEntries) ) {
|
||||
maxEntries = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
maxEntries = 0;
|
||||
}
|
||||
|
||||
messager.send({
|
||||
what: 'userSettings',
|
||||
name: 'requestLogMaxEntries',
|
||||
value: maxEntries
|
||||
});
|
||||
|
||||
truncateLog(maxEntries);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.onLoad(function() {
|
||||
// Extract the tab id of the page we need to pull the log
|
||||
var matches = window.location.search.match(/[\?&]tabId=([^&]+)/);
|
||||
if ( matches && matches.length === 2 ) {
|
||||
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('#clear').on('click', clearBuffer);
|
||||
uDom('#filterButton').on('click', onFilterButton);
|
||||
uDom('#filterExpression').on('input', onFilterChangedAsync);
|
||||
uDom('#maxEntries').on('change', onMaxEntriesChanged);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
|
@ -1,231 +0,0 @@
|
|||
/*******************************************************************************
|
||||
|
||||
µBlock - a browser extension to block requests.
|
||||
Copyright (C) 2014 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
|
||||
*/
|
||||
|
||||
/* jshint bitwise: false */
|
||||
/* global vAPI, uDom */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('devtools.js');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderPageSelector = function(targetTabId) {
|
||||
var selectedTabId = targetTabId || uDom('#pageSelector').val();
|
||||
var onDataReceived = function(pageTitles) {
|
||||
if ( pageTitles.hasOwnProperty(selectedTabId) === false ) {
|
||||
selectedTabId = pageTitles[0];
|
||||
}
|
||||
var select = uDom('#pageSelector').empty();
|
||||
var option;
|
||||
for ( var tabId in pageTitles ) {
|
||||
if ( pageTitles.hasOwnProperty(tabId) === false ) {
|
||||
continue;
|
||||
}
|
||||
option = uDom('<option>').text(pageTitles[tabId])
|
||||
.prop('value', tabId);
|
||||
if ( tabId === selectedTabId ) {
|
||||
option.prop('selected', true);
|
||||
}
|
||||
select.append(option);
|
||||
}
|
||||
// This must be done after inserting all option tags, or else Firefox
|
||||
// will refuse values which do not exist yet.
|
||||
select.prop('value', selectedTabId);
|
||||
selectPage();
|
||||
};
|
||||
messager.send({ what: 'getPageDetails' }, onDataReceived);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var pageSelectorChanged = function() {
|
||||
selectPage();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var selectPage = function() {
|
||||
var tabId = uDom('#pageSelector').val() || '';
|
||||
var inspector = uDom('#content');
|
||||
var currentSrc = inspector.attr('src');
|
||||
var targetSrc = 'devtool-log.html?tabId=' + tabId;
|
||||
if ( targetSrc === currentSrc ) {
|
||||
return;
|
||||
}
|
||||
inspector.attr('src', targetSrc);
|
||||
uDom('#popup').attr('src', tabId ? 'popup.html?tabId=' + tabId : '');
|
||||
|
||||
// This is useful for when the user force-refresh the page: this will
|
||||
// prevent a reset to the original request log.
|
||||
// This is also useful for an outside observer to find out which tab is
|
||||
// being logged, i.e. the popup menu can initialize itself according to
|
||||
// what tab is currently being logged.
|
||||
window.history.pushState(
|
||||
{},
|
||||
'',
|
||||
window.location.href.replace(/^(.+[\?&])tabId=([^&]+)(.*)$/, '$1tabId=' + tabId + '$3')
|
||||
);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleTool = function() {
|
||||
var button = uDom(this);
|
||||
button.toggleClass('enabled', !button.hasClass('enabled'));
|
||||
|
||||
// Special case: we want the frame of the popup to be filled-in if and
|
||||
// only if the popup is visible.
|
||||
if ( this.id === 'popupToggler' ) {
|
||||
var tabId = uDom('#pageSelector').val() || '';
|
||||
var body = uDom('body');
|
||||
body.toggleClass('popupEnabled');
|
||||
if ( body.hasClass('popupEnabled') === false ) {
|
||||
tabId = '';
|
||||
}
|
||||
uDom('#popup').attr(
|
||||
'src',
|
||||
button.hasClass('enabled') && tabId ? 'popup.html?tabId=' + tabId : ''
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var evaluateStaticFiltering = (function() {
|
||||
var uglyTypeNames = {
|
||||
'css': 'stylesheet',
|
||||
'doc': 'main_frame',
|
||||
'frame': 'sub_frame',
|
||||
'xhr': 'xmlhttprequest'
|
||||
};
|
||||
|
||||
var onResultReceived = function(response) {
|
||||
var result = response && response.result.slice(3);
|
||||
uDom('#filteringResult')
|
||||
.text(result || '\u00A0')
|
||||
.toggleClass('empty', result === '');
|
||||
|
||||
var input = uDom('#filterMatcher input').at(0);
|
||||
if ( input.val().trim() === '' ) {
|
||||
input.val(response && response.contextURL || '');
|
||||
}
|
||||
};
|
||||
|
||||
var timer = null;
|
||||
var onTimerElapsed = function() {
|
||||
timer = null;
|
||||
|
||||
var inputs = uDom('#filterMatcher input');
|
||||
var prettyTypeName = inputs.at(2).val().trim();
|
||||
|
||||
messager.send({
|
||||
what: 'evaluateStaticFiltering',
|
||||
tabId: uDom('#pageSelector').val() || '',
|
||||
contextURL: inputs.at(0).val().trim(),
|
||||
requestURL: inputs.at(1).val().trim(),
|
||||
requestType: uglyTypeNames[prettyTypeName] || prettyTypeName,
|
||||
}, onResultReceived);
|
||||
};
|
||||
|
||||
return function() {
|
||||
if ( timer === null ) {
|
||||
setTimeout(onTimerElapsed, 750);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var resizePopup = function() {
|
||||
var popup = document.getElementById('popup');
|
||||
popup.style.width = popup.contentWindow.document.body.clientWidth + 'px';
|
||||
popup.style.height = popup.contentWindow.document.body.clientHeight + 'px';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onPopupLoaded = function() {
|
||||
resizePopup();
|
||||
|
||||
if ( popupObserver !== null ) {
|
||||
popupObserver.disconnect();
|
||||
}
|
||||
|
||||
var popup = document.getElementById('popup');
|
||||
if ( popup.contentDocument === null ) {
|
||||
return;
|
||||
}
|
||||
var popupBody = popup.contentDocument.body;
|
||||
if ( popupBody === null ) {
|
||||
return;
|
||||
}
|
||||
var popupPanes = popup.contentDocument.getElementById('panes');
|
||||
if ( popupPanes === null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( popupObserver === null ) {
|
||||
popupObserver = new MutationObserver(resizePopup);
|
||||
}
|
||||
|
||||
var details = {
|
||||
childList: false,
|
||||
attributes: true,
|
||||
attributeFilter: ['class']
|
||||
};
|
||||
popupObserver.observe(popupBody, details);
|
||||
popupObserver.observe(popupPanes, details);
|
||||
};
|
||||
|
||||
var popupObserver = null;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.onLoad(function() {
|
||||
var tabId;
|
||||
|
||||
// Extract the tab id of the page we need to pull the log
|
||||
var matches = window.location.search.match(/[\?&]tabId=([^&]+)/);
|
||||
if ( matches && matches.length === 2 ) {
|
||||
tabId = matches[1];
|
||||
}
|
||||
|
||||
uDom('.toolToggler').on('click', toggleTool);
|
||||
uDom('#popup').on('load', onPopupLoaded);
|
||||
|
||||
renderPageSelector(tabId);
|
||||
|
||||
uDom('#pageSelector').on('change', pageSelectorChanged);
|
||||
uDom('#refresh').on('click', function() { renderPageSelector(); });
|
||||
uDom('#filterMatcher input').on('input', evaluateStaticFiltering);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,784 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uMatrix - a browser extension to benchmark browser session.
|
||||
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/sessbench
|
||||
*/
|
||||
|
||||
/* jshint boss: true */
|
||||
/* global vAPI, uDom */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('logger-ui.js');
|
||||
var tbody = document.querySelector('#content tbody');
|
||||
var trJunkyard = [];
|
||||
var tdJunkyard = [];
|
||||
var firstVarDataCol = 2; // currently, column 2 (0-based index)
|
||||
var lastVarDataIndex = 4; // currently, d0-d3
|
||||
var maxEntries = 5000;
|
||||
var noTabId = '';
|
||||
var allTabIds = {};
|
||||
|
||||
var prettyRequestTypes = {
|
||||
'main_frame': 'doc',
|
||||
'stylesheet': 'css',
|
||||
'sub_frame': 'frame',
|
||||
'xmlhttprequest': 'xhr'
|
||||
};
|
||||
|
||||
var timeOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
};
|
||||
|
||||
var dateOptions = {
|
||||
month: 'short',
|
||||
day: '2-digit'
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Emphasize hostname in URL, as this is what matters in uMatrix's rules.
|
||||
|
||||
var nodeFromURL = function(url, filter) {
|
||||
if ( filter.charAt(0) !== 's' ) {
|
||||
return document.createTextNode(url);
|
||||
}
|
||||
|
||||
// make a regex out of the filter
|
||||
var reText = filter.slice(3);
|
||||
var pos = reText.indexOf('$');
|
||||
if ( pos > 0 ) {
|
||||
reText = reText.slice(0, pos);
|
||||
}
|
||||
if ( reText === '*' ) {
|
||||
reText = '\\*';
|
||||
} else if ( reText.charAt(0) === '/' && reText.slice(-1) === '/' ) {
|
||||
reText = reText.slice(1, -1);
|
||||
} else {
|
||||
reText = reText
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\?/g, '\\?')
|
||||
.replace('||', '')
|
||||
.replace(/\^/g, '.')
|
||||
.replace(/^\|/g, '^')
|
||||
.replace(/\|$/g, '$')
|
||||
.replace(/\*/g, '.*')
|
||||
;
|
||||
}
|
||||
var re = new RegExp(reText, 'gi');
|
||||
var matches = re.exec(url);
|
||||
if ( matches === null || matches[0].length === 0 ) {
|
||||
return document.createTextNode(url);
|
||||
}
|
||||
|
||||
var node = renderedURLTemplate.cloneNode(true);
|
||||
node.childNodes[0].textContent = url.slice(0, matches.index);
|
||||
node.childNodes[1].textContent = url.slice(matches.index, re.lastIndex);
|
||||
node.childNodes[2].textContent = url.slice(re.lastIndex);
|
||||
return node;
|
||||
};
|
||||
|
||||
var renderedURLTemplate = document.querySelector('#renderedURLTemplate > span');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createCellAt = function(tr, index) {
|
||||
var td = tr.cells[index];
|
||||
var mustAppend = !td;
|
||||
if ( mustAppend ) {
|
||||
td = tdJunkyard.pop();
|
||||
}
|
||||
if ( td ) {
|
||||
td.removeAttribute('colspan');
|
||||
td.textContent = '';
|
||||
} else {
|
||||
td = document.createElement('td');
|
||||
}
|
||||
if ( mustAppend ) {
|
||||
tr.appendChild(td);
|
||||
}
|
||||
return td;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createRow = function(layout) {
|
||||
var tr = trJunkyard.pop();
|
||||
if ( tr ) {
|
||||
tr.className = '';
|
||||
} else {
|
||||
tr = document.createElement('tr');
|
||||
}
|
||||
for ( var index = 0; index < firstVarDataCol; index++ ) {
|
||||
createCellAt(tr, index);
|
||||
}
|
||||
var i = 1, span = 1, td;
|
||||
for (;;) {
|
||||
td = createCellAt(tr, index);
|
||||
if ( i === lastVarDataIndex ) {
|
||||
break;
|
||||
}
|
||||
if ( layout.charAt(i) !== '1' ) {
|
||||
span += 1;
|
||||
} else {
|
||||
if ( span !== 1 ) {
|
||||
td.setAttribute('colspan', span);
|
||||
}
|
||||
index += 1;
|
||||
span = 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if ( span !== 1 ) {
|
||||
td.setAttribute('colspan', span);
|
||||
}
|
||||
index += 1;
|
||||
while ( td = tr.cells[index] ) {
|
||||
tdJunkyard.push(tr.removeChild(td));
|
||||
}
|
||||
return tr;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createGap = function(tabId, url) {
|
||||
var tr = createRow('1');
|
||||
tr.classList.add('tab');
|
||||
tr.classList.add('canMtx');
|
||||
tr.classList.add('tab_' + tabId);
|
||||
tr.classList.add('maindoc');
|
||||
tr.cells[firstVarDataCol].textContent = url;
|
||||
tbody.insertBefore(tr, tbody.firstChild);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderNetLogEntry = function(tr, entry) {
|
||||
var filter = entry.d0;
|
||||
var type = entry.d1;
|
||||
var url = entry.d2;
|
||||
|
||||
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 ( type === 'main_frame' ) {
|
||||
createGap(entry.tab, url);
|
||||
}
|
||||
|
||||
// Cosmetic filter?
|
||||
if ( filter.charAt(0) === 'c' ) {
|
||||
tr.classList.add('cosmetic');
|
||||
}
|
||||
|
||||
if ( filter.charAt(1) === 'b' ) {
|
||||
tr.classList.add('blocked');
|
||||
tr.cells[2].textContent = ' --';
|
||||
} else if ( filter.charAt(1) === 'a' ) {
|
||||
tr.classList.add('allowed');
|
||||
tr.cells[2].textContent = ' ++';
|
||||
} else {
|
||||
tr.cells[2].textContent = '';
|
||||
}
|
||||
|
||||
var filterText = filter.slice(3);
|
||||
if ( filter.lastIndexOf('sa', 0) === 0 ) {
|
||||
filterText = '@@' + filterText;
|
||||
}
|
||||
|
||||
tr.cells[3].textContent = filterText + '\t';
|
||||
tr.cells[4].textContent = (prettyRequestTypes[type] || type) + '\t';
|
||||
tr.cells[5].appendChild(nodeFromURL(url, filter));
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogEntry = function(entry) {
|
||||
var tr;
|
||||
var fvdc = firstVarDataCol;
|
||||
|
||||
switch ( entry.cat ) {
|
||||
case 'error':
|
||||
case 'info':
|
||||
tr = createRow('1');
|
||||
tr.cells[fvdc].textContent = entry.d0;
|
||||
break;
|
||||
|
||||
case 'cosmetic':
|
||||
case 'net':
|
||||
tr = createRow('1111');
|
||||
renderNetLogEntry(tr, entry);
|
||||
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 ) {
|
||||
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);
|
||||
}
|
||||
|
||||
rowFilterer.filterOne(tr, true);
|
||||
|
||||
tbody.insertBefore(tr, tbody.firstChild);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogEntries = function(response) {
|
||||
document.body.classList.toggle('colorBlind', response.colorBlind);
|
||||
|
||||
var entries = response.entries;
|
||||
if ( entries.length === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Preserve scroll position
|
||||
var height = tbody.offsetHeight;
|
||||
|
||||
var tabIds = response.tabIds;
|
||||
var n = entries.length;
|
||||
var entry;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
entry = entries[i];
|
||||
// Unlikely, but it may happen
|
||||
if ( entry.tab && tabIds.hasOwnProperty(entry.tab) === false ) {
|
||||
continue;
|
||||
}
|
||||
renderLogEntry(entries[i]);
|
||||
}
|
||||
|
||||
// Prevent logger from growing infinitely and eating all memory. For
|
||||
// instance someone could forget that it is left opened for some
|
||||
// dynamically refreshed pages.
|
||||
truncateLog(maxEntries);
|
||||
|
||||
var yDelta = tbody.offsetHeight - height;
|
||||
if ( yDelta === 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chromium:
|
||||
// body.scrollTop = good value
|
||||
// body.parentNode.scrollTop = 0
|
||||
if ( document.body.scrollTop !== 0 ) {
|
||||
document.body.scrollTop += yDelta;
|
||||
return;
|
||||
}
|
||||
|
||||
// Firefox:
|
||||
// body.scrollTop = 0
|
||||
// body.parentNode.scrollTop = good value
|
||||
var parentNode = document.body.parentNode;
|
||||
if ( parentNode && parentNode.scrollTop !== 0 ) {
|
||||
parentNode.scrollTop += yDelta;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var truncateLog = function(size) {
|
||||
if ( size === 0 ) {
|
||||
size = 5000;
|
||||
}
|
||||
var tbody = document.querySelector('#content tbody');
|
||||
size = Math.min(size, 10000);
|
||||
var tr;
|
||||
while ( tbody.childElementCount > size ) {
|
||||
tr = tbody.lastElementChild;
|
||||
trJunkyard.push(tbody.removeChild(tr));
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onLogBufferRead = function(response) {
|
||||
// This tells us the behind-the-scene tab id
|
||||
noTabId = response.noTabId;
|
||||
|
||||
// This may have changed meanwhile
|
||||
if ( response.maxEntries !== maxEntries ) {
|
||||
maxEntries = response.maxEntries;
|
||||
uDom('#maxEntries').val(maxEntries || '');
|
||||
}
|
||||
|
||||
// Neuter rows for which a tab does not exist anymore
|
||||
// TODO: sort to avoid using indexOf
|
||||
var rowVoided = false;
|
||||
for ( var tabId in allTabIds ) {
|
||||
if ( allTabIds.hasOwnProperty(tabId) === false ) {
|
||||
continue;
|
||||
}
|
||||
if ( response.tabIds.hasOwnProperty(tabId) ) {
|
||||
continue;
|
||||
}
|
||||
toJunkyard(uDom('.tab_' + tabId));
|
||||
if ( tabId === popupManager.tabId ) {
|
||||
popupManager.toggleOff();
|
||||
}
|
||||
rowVoided = true;
|
||||
}
|
||||
allTabIds = response.tabIds;
|
||||
|
||||
renderLogEntries(response);
|
||||
|
||||
// Synchronize toolbar with content of log
|
||||
uDom('#clear').toggleClass(
|
||||
'disabled',
|
||||
tbody.querySelector('tr') === null
|
||||
);
|
||||
|
||||
setTimeout(readLogBuffer, 1200);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// This can be called only once, at init time. After that, this will be called
|
||||
// automatically. If called after init time, this will be messy, and this would
|
||||
// require a bit more code to ensure no multi time out events.
|
||||
|
||||
var readLogBuffer = function() {
|
||||
messager.send({ what: 'readAll' }, onLogBufferRead);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMaxEntriesChanged = function() {
|
||||
var raw = uDom(this).val();
|
||||
try {
|
||||
maxEntries = parseInt(raw, 10);
|
||||
if ( isNaN(maxEntries) ) {
|
||||
maxEntries = 0;
|
||||
}
|
||||
} catch (e) {
|
||||
maxEntries = 0;
|
||||
}
|
||||
|
||||
messager.send({
|
||||
what: 'userSettings',
|
||||
name: 'requestLogMaxEntries',
|
||||
value: maxEntries
|
||||
});
|
||||
|
||||
truncateLog(maxEntries);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var rowFilterer = (function() {
|
||||
var filters = [];
|
||||
|
||||
var parseInput = function() {
|
||||
filters = [];
|
||||
|
||||
var rawPart, not, hardBeg, hardEnd, reStr;
|
||||
var raw = uDom('#filterInput').val().trim();
|
||||
var rawParts = raw.split(/\s+/);
|
||||
var i = rawParts.length;
|
||||
while ( i-- ) {
|
||||
rawPart = rawParts[i];
|
||||
not = rawPart.charAt(0) === '!';
|
||||
if ( not ) {
|
||||
rawPart = rawPart.slice(1);
|
||||
}
|
||||
hardBeg = rawPart.charAt(0) === '|';
|
||||
if ( hardBeg ) {
|
||||
rawPart = rawPart.slice(1);
|
||||
}
|
||||
hardEnd = rawPart.slice(-1) === '|';
|
||||
if ( hardEnd ) {
|
||||
rawPart = rawPart.slice(0, -1);
|
||||
}
|
||||
if ( rawPart === '' ) {
|
||||
continue;
|
||||
}
|
||||
// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions
|
||||
reStr = rawPart.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
||||
.replace(/\*/g, '.*');
|
||||
if ( hardBeg ) {
|
||||
reStr = '(?:^|\\s)' + reStr;
|
||||
}
|
||||
if ( hardEnd ) {
|
||||
reStr += '(?:\\s|$)';
|
||||
}
|
||||
filters.push({
|
||||
re: new RegExp(reStr, 'i'),
|
||||
r: !not
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var filterOne = function(tr, clean) {
|
||||
var ff = filters;
|
||||
var fcount = ff.length;
|
||||
if ( fcount === 0 && clean === true ) {
|
||||
return;
|
||||
}
|
||||
// do not filter out doc boundaries, they help separate important
|
||||
// section of log.
|
||||
var cl = tr.classList;
|
||||
if ( cl.contains('maindoc') ) {
|
||||
return;
|
||||
}
|
||||
if ( fcount === 0 ) {
|
||||
cl.remove('f');
|
||||
return;
|
||||
}
|
||||
var cc = tr.cells;
|
||||
var ccount = cc.length;
|
||||
var hit, j, f;
|
||||
// each filter expression must hit (implicit and-op)
|
||||
// if...
|
||||
// positive filter expression = there must one hit on any field
|
||||
// negative filter expression = there must be no hit on all fields
|
||||
for ( var i = 0; i < fcount; i++ ) {
|
||||
f = ff[i];
|
||||
hit = !f.r;
|
||||
for ( j = 0; j < ccount; j++ ) {
|
||||
if ( f.re.test(cc[j].textContent) ) {
|
||||
hit = f.r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !hit ) {
|
||||
cl.add('f');
|
||||
return;
|
||||
}
|
||||
}
|
||||
cl.remove('f');
|
||||
};
|
||||
|
||||
var filterAll = function() {
|
||||
// Special case: no filter
|
||||
if ( filters.length === 0 ) {
|
||||
uDom('#content tr').removeClass('f');
|
||||
return;
|
||||
}
|
||||
var tbody = document.querySelector('#content tbody');
|
||||
var rows = tbody.rows;
|
||||
var i = rows.length;
|
||||
while ( i-- ) {
|
||||
filterOne(rows[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var onFilterChangedAsync = (function() {
|
||||
var timer = null;
|
||||
var commit = function() {
|
||||
timer = null;
|
||||
parseInput();
|
||||
filterAll();
|
||||
};
|
||||
return function() {
|
||||
if ( timer !== null ) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
timer = setTimeout(commit, 750);
|
||||
};
|
||||
})();
|
||||
|
||||
var onFilterButton = function() {
|
||||
var cl = document.body.classList;
|
||||
cl.toggle('f', cl.contains('f') === false);
|
||||
};
|
||||
|
||||
uDom('#filterButton').on('click', onFilterButton);
|
||||
uDom('#filterInput').on('input', onFilterChangedAsync);
|
||||
|
||||
return {
|
||||
filterOne: filterOne,
|
||||
filterAll: filterAll
|
||||
};
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toJunkyard = function(trs) {
|
||||
trs.remove();
|
||||
var i = trs.length;
|
||||
while ( i-- ) {
|
||||
trJunkyard.push(trs.nodeAt(i));
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var clearBuffer = function() {
|
||||
var tbody = document.querySelector('#content tbody');
|
||||
var tr;
|
||||
while ( tbody.firstChild !== null ) {
|
||||
tr = tbody.lastElementChild;
|
||||
trJunkyard.push(tbody.removeChild(tr));
|
||||
}
|
||||
uDom('#clear').addClass('disabled');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleCompactView = function() {
|
||||
document.body.classList.toggle(
|
||||
'compactView',
|
||||
document.body.classList.contains('compactView') === false
|
||||
);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var popupManager = (function() {
|
||||
var realTabId = null;
|
||||
var localTabId = null;
|
||||
var container = null;
|
||||
var movingOverlay = null;
|
||||
var popup = null;
|
||||
var popupObserver = null;
|
||||
var style = null;
|
||||
var styleTemplate = [
|
||||
'tr:not(.tab_{{tabId}}) {',
|
||||
'cursor: not-allowed;',
|
||||
'opacity: 0.2;',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
// Related to moving the popup around
|
||||
var xnormal, ynormal, crect, dx, dy, vw, vh;
|
||||
|
||||
// Viewport data assumed to be properly set up
|
||||
var positionFromNormal = function(x, y) {
|
||||
if ( typeof x === 'number' ) {
|
||||
if ( x < 0.5 ) {
|
||||
container.style.setProperty('left', (x * vw) + 'px');
|
||||
container.style.removeProperty('right');
|
||||
} else {
|
||||
container.style.removeProperty('left');
|
||||
container.style.setProperty('right', ((1 - x) * vw) + 'px');
|
||||
}
|
||||
}
|
||||
if ( typeof y === 'number' ) {
|
||||
if ( y < 0.5 ) {
|
||||
container.style.setProperty('top', (y * vh) + 'px');
|
||||
container.style.removeProperty('bottom');
|
||||
} else {
|
||||
container.style.removeProperty('top');
|
||||
container.style.setProperty('bottom', ((1 - y) * vh) + 'px');
|
||||
}
|
||||
}
|
||||
// TODO: adjust size
|
||||
};
|
||||
var updateViewportData = function() {
|
||||
crect = container.getBoundingClientRect();
|
||||
vw = document.documentElement.clientWidth - crect.width;
|
||||
vh = document.documentElement.clientHeight - crect.height;
|
||||
};
|
||||
var toNormalX = function(x) {
|
||||
return xnormal = Math.max(Math.min(x / vw, 1), 0);
|
||||
};
|
||||
var toNormalY = function(y) {
|
||||
return ynormal = Math.max(Math.min(y / vh, 1), 0);
|
||||
};
|
||||
|
||||
var onMouseMove = function(ev) {
|
||||
updateViewportData();
|
||||
positionFromNormal(
|
||||
toNormalX(ev.clientX + dx),
|
||||
toNormalY(ev.clientY + dy)
|
||||
);
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
var onMouseUp = function(ev) {
|
||||
updateViewportData();
|
||||
positionFromNormal(
|
||||
toNormalX(ev.clientX + dx),
|
||||
toNormalY(ev.clientY + dy)
|
||||
);
|
||||
movingOverlay.removeEventListener('mouseup', onMouseUp);
|
||||
movingOverlay.removeEventListener('mousemove', onMouseMove);
|
||||
movingOverlay = null;
|
||||
container.classList.remove('moving');
|
||||
vAPI.localStorage.setItem('popupLastPosition', JSON.stringify({
|
||||
xnormal: xnormal,
|
||||
ynormal: ynormal
|
||||
}));
|
||||
ev.stopPropagation();
|
||||
ev.preventDefault();
|
||||
};
|
||||
|
||||
var onMouseDown = function(ev) {
|
||||
if ( ev.target !== ev.currentTarget ) {
|
||||
return;
|
||||
}
|
||||
container.classList.add('moving');
|
||||
updateViewportData();
|
||||
dx = crect.left - ev.clientX;
|
||||
dy = crect.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;
|
||||
}
|
||||
realTabId = localTabId = matches[1];
|
||||
if ( localTabId === 'bts' ) {
|
||||
realTabId = noTabId;
|
||||
}
|
||||
|
||||
// Use last normalized position if one is defined.
|
||||
// Default to top-right.
|
||||
var x = 1, y = 0;
|
||||
var json = vAPI.localStorage.getItem('popupLastPosition');
|
||||
if ( json ) {
|
||||
try {
|
||||
var popupLastPosition = JSON.parse(json);
|
||||
x = popupLastPosition.xnormal;
|
||||
y = popupLastPosition.ynormal;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
container = document.getElementById('popupContainer');
|
||||
updateViewportData();
|
||||
positionFromNormal(x, y);
|
||||
|
||||
// Window controls
|
||||
container.querySelector('div > span:first-child').addEventListener('click', toggleOff);
|
||||
container.querySelector('div').addEventListener('mousedown', onMouseDown);
|
||||
|
||||
popup = document.createElement('iframe');
|
||||
popup.addEventListener('load', onLoad);
|
||||
popup.setAttribute('src', 'popup.html?tabId=' + realTabId);
|
||||
popupObserver = new MutationObserver(resizePopup);
|
||||
container.appendChild(popup);
|
||||
|
||||
style = document.querySelector('#content > style');
|
||||
style.textContent = styleTemplate.replace('{{tabId}}', localTabId);
|
||||
|
||||
document.body.classList.add('popupOn');
|
||||
};
|
||||
|
||||
var toggleOff = function() {
|
||||
document.body.classList.remove('popupOn');
|
||||
|
||||
// Just in case
|
||||
if ( movingOverlay !== null ) {
|
||||
movingOverlay.removeEventListener('mousemove', onMouseMove, true);
|
||||
movingOverlay.removeEventListener('mouseup', onMouseUp, true);
|
||||
movingOverlay = null;
|
||||
}
|
||||
|
||||
// Window controls
|
||||
container.querySelector('div > span:first-child').removeEventListener('click', toggleOff);
|
||||
container.querySelector('div').removeEventListener('mousedown', onMouseDown);
|
||||
|
||||
popup.removeEventListener('load', onLoad);
|
||||
popupObserver.disconnect();
|
||||
popupObserver = null;
|
||||
popup.setAttribute('src', '');
|
||||
container.removeChild(popup);
|
||||
popup = null;
|
||||
|
||||
style.textContent = '';
|
||||
style = null;
|
||||
|
||||
container = null;
|
||||
realTabId = null;
|
||||
};
|
||||
|
||||
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;
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.onLoad(function() {
|
||||
readLogBuffer();
|
||||
|
||||
uDom('#compactViewToggler').on('click', toggleCompactView);
|
||||
uDom('#clear').on('click', clearBuffer);
|
||||
uDom('#maxEntries').on('change', onMaxEntriesChanged);
|
||||
uDom('#content table').on('click', 'tr.canMtx > td:nth-of-type(2)', popupManager.toggleOn);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
100
src/js/logger.js
100
src/js/logger.js
|
@ -1,6 +1,6 @@
|
|||
/*******************************************************************************
|
||||
|
||||
uBlock Origin - a browser extension to block requests.
|
||||
uBlock - a browser extension to block requests.
|
||||
Copyright (C) 2015 Raymond Hill
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
@ -31,18 +31,18 @@
|
|||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
var LogEntry = function(details, result) {
|
||||
this.init(details, result);
|
||||
var LogEntry = function(args) {
|
||||
this.init(args);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logEntryFactory = function(details, result) {
|
||||
var logEntryFactory = function(args) {
|
||||
var entry = logEntryJunkyard.pop();
|
||||
if ( entry ) {
|
||||
return entry.init(details, result);
|
||||
return entry.init(args);
|
||||
}
|
||||
return new LogEntry(details, result);
|
||||
return new LogEntry(args);
|
||||
};
|
||||
|
||||
var logEntryJunkyard = [];
|
||||
|
@ -50,19 +50,23 @@ var logEntryJunkyardMax = 100;
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
LogEntry.prototype.init = function(details, result) {
|
||||
LogEntry.prototype.init = function(args) {
|
||||
this.tstamp = Date.now();
|
||||
this.url = details.requestURL;
|
||||
this.hostname = details.requestHostname;
|
||||
this.type = details.requestType;
|
||||
this.result = result;
|
||||
this.tab = args[0] || '';
|
||||
this.cat = args[1] || '';
|
||||
this.d0 = args[2];
|
||||
this.d1 = args[3];
|
||||
this.d2 = args[4];
|
||||
this.d3 = args[5];
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogEntry.prototype.dispose = function() {
|
||||
this.url = this.hostname = this.type = this.result = '';
|
||||
this.tstamp = 0;
|
||||
this.tab = this.cat = '';
|
||||
this.d0 = this.d1 = this.d2 = this.d3 = undefined;
|
||||
if ( logEntryJunkyard.length < logEntryJunkyardMax ) {
|
||||
logEntryJunkyard.push(this);
|
||||
}
|
||||
|
@ -96,13 +100,13 @@ LogBuffer.prototype.dispose = function() {
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.writeOne = function(details, result) {
|
||||
LogBuffer.prototype.writeOne = function(args) {
|
||||
// Reusing log entry = less memory churning
|
||||
var entry = this.buffer[this.writePtr];
|
||||
if ( entry instanceof LogEntry === false ) {
|
||||
this.buffer[this.writePtr] = logEntryFactory(details, result);
|
||||
this.buffer[this.writePtr] = logEntryFactory(args);
|
||||
} else {
|
||||
entry.init(details, result);
|
||||
entry.init(args);
|
||||
}
|
||||
this.writePtr += 1;
|
||||
if ( this.writePtr === this.size ) {
|
||||
|
@ -144,59 +148,49 @@ LogBuffer.prototype.readAll = function() {
|
|||
/******************************************************************************/
|
||||
|
||||
// Tab id to log buffer instances
|
||||
var logBuffers = {};
|
||||
var logBuffer = null;
|
||||
|
||||
// After 30 seconds without being read, a buffer will be considered unused, and
|
||||
// After 60 seconds without being read, a buffer will be considered unused, and
|
||||
// thus removed from memory.
|
||||
var logBufferObsoleteAfter = 30 * 1000;
|
||||
var logBufferObsoleteAfter = 60 * 1000;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var writeOne = function(tabId, details, result) {
|
||||
if ( logBuffers.hasOwnProperty(tabId) === false ) {
|
||||
return;
|
||||
var janitor = function() {
|
||||
if (
|
||||
logBuffer !== null &&
|
||||
logBuffer.lastReadTime < (Date.now() - logBufferObsoleteAfter)
|
||||
) {
|
||||
logBuffer = logBuffer.dispose();
|
||||
}
|
||||
var logBuffer = logBuffers[tabId];
|
||||
logBuffer.writeOne(details, result);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var readAll = function(tabId) {
|
||||
if ( logBuffers.hasOwnProperty(tabId) === false ) {
|
||||
logBuffers[tabId] = new LogBuffer();
|
||||
if ( logBuffer !== null ) {
|
||||
setTimeout(janitor, logBufferObsoleteAfter);
|
||||
}
|
||||
return logBuffers[tabId].readAll();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var isObserved = function(tabId) {
|
||||
return logBuffers.hasOwnProperty(tabId);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
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];
|
||||
}
|
||||
var writeOne = function() {
|
||||
if ( logBuffer !== null ) {
|
||||
logBuffer.writeOne(arguments);
|
||||
}
|
||||
setTimeout(loggerJanitor, loggerJanitorPeriod);
|
||||
};
|
||||
|
||||
// The janitor will look for stale log buffer every 2 minutes.
|
||||
var loggerJanitorPeriod = 2 * 60 * 1000;
|
||||
/******************************************************************************/
|
||||
|
||||
setTimeout(loggerJanitor, loggerJanitorPeriod);
|
||||
var readAll = function() {
|
||||
if ( logBuffer === null ) {
|
||||
logBuffer = new LogBuffer();
|
||||
setTimeout(janitor, logBufferObsoleteAfter);
|
||||
}
|
||||
return logBuffer.readAll();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var isObserved = function() {
|
||||
return logBuffer !== null;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ var onMessage = function(request, sender, callback) {
|
|||
/* falls through */
|
||||
case 'cosmeticFiltersActivated':
|
||||
// Net-based cosmetic filters are of no interest for logging purpose.
|
||||
if ( µb.logger.isObserved(tabId) && request.type !== 'net' ) {
|
||||
if ( µb.logger.isObserved() && request.type !== 'net' ) {
|
||||
µb.logCosmeticFilters(tabId);
|
||||
}
|
||||
break;
|
||||
|
@ -275,10 +275,6 @@ var getTargetTabId = function(tab) {
|
|||
return '';
|
||||
}
|
||||
|
||||
if ( tab.url.lastIndexOf(vAPI.getURL('devtools.html'), 0) !== 0 ) {
|
||||
return tab.id;
|
||||
}
|
||||
|
||||
// If the URL is that of the network request logger, fill the popup with
|
||||
// the data from the tab being observed by the logger.
|
||||
// This allows a user to actually modify filtering profile for
|
||||
|
@ -574,22 +570,22 @@ var µb = µBlock;
|
|||
/******************************************************************************/
|
||||
|
||||
var logCosmeticFilters = function(tabId, details) {
|
||||
if ( µb.logger.isObserved(tabId) === false ) {
|
||||
if ( µb.logger.isObserved() === false ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var context = {
|
||||
requestURL: details.pageURL,
|
||||
requestHostname: µb.URI.hostnameFromURI(details.pageURL),
|
||||
requestType: 'dom'
|
||||
};
|
||||
|
||||
var selectors = details.matchedSelectors;
|
||||
|
||||
selectors.sort();
|
||||
|
||||
for ( var i = 0; i < selectors.length; i++ ) {
|
||||
µb.logger.writeOne(tabId, context, 'cb:##' + selectors[i]);
|
||||
µb.logger.writeOne(
|
||||
tabId,
|
||||
'cosmetic',
|
||||
'cb:##' + selectors[i],
|
||||
'dom',
|
||||
details.pageURL
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1009,126 +1005,6 @@ vAPI.messaging.listen('whitelist.js', onMessage);
|
|||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// devtools.js
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getPageDetails = function(callback) {
|
||||
var out = {};
|
||||
var tabIds = Object.keys(µb.pageStores);
|
||||
|
||||
// Special case: behind-the-scene virtual tab (does not really exist)
|
||||
var pos = tabIds.indexOf(vAPI.noTabId);
|
||||
if ( pos !== -1 ) {
|
||||
tabIds.splice(pos, 1);
|
||||
out[vAPI.noTabId] = vAPI.i18n('logBehindTheScene');
|
||||
}
|
||||
|
||||
// This can happen
|
||||
if ( tabIds.length === 0 ) {
|
||||
callback(out);
|
||||
return;
|
||||
}
|
||||
|
||||
var countdown = tabIds.length;
|
||||
var doCountdown = function() {
|
||||
countdown -= 1;
|
||||
if ( countdown === 0 ) {
|
||||
callback(out);
|
||||
}
|
||||
};
|
||||
|
||||
// Let's not populate the page selector with reference to self
|
||||
var devtoolsURL = vAPI.getURL('devtools.html');
|
||||
|
||||
var onTabDetails = function(tab) {
|
||||
if ( tab && tab.url.lastIndexOf(devtoolsURL, 0) !== 0 ) {
|
||||
out[tab.id] = tab.title;
|
||||
}
|
||||
doCountdown();
|
||||
};
|
||||
|
||||
var i = countdown;
|
||||
while ( i-- ) {
|
||||
vAPI.tabs.get(tabIds[i], onTabDetails);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var evaluateStaticFiltering = function(details) {
|
||||
// URL of context not provided, try to use the one for the given tab id.
|
||||
var contextURL = details.contextURL;
|
||||
if ( contextURL === '' ) {
|
||||
var tabContext = µb.tabContextManager.lookup(details.tabId || 0);
|
||||
if ( tabContext ) {
|
||||
contextURL = tabContext.rawURL;
|
||||
}
|
||||
}
|
||||
|
||||
var pageHostname = µb.URI.hostnameFromURI(contextURL);
|
||||
var pageDomain = µb.URI.domainFromHostname(pageHostname);
|
||||
|
||||
var context = {
|
||||
rootHostname: pageHostname,
|
||||
rootDomain: pageDomain,
|
||||
pageHostname: pageHostname,
|
||||
pageDomain: pageDomain,
|
||||
requestURL: details.requestURL,
|
||||
requestHostname: µb.URI.hostnameFromURI(details.requestURL),
|
||||
requestType: details.requestType
|
||||
};
|
||||
|
||||
return {
|
||||
contextURL: contextURL,
|
||||
result: µb.staticNetFilteringEngine.matchString(context)
|
||||
};
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
case 'getPageDetails':
|
||||
getPageDetails(callback);
|
||||
return;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sync
|
||||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'evaluateStaticFiltering':
|
||||
response = evaluateStaticFiltering(request);
|
||||
break;
|
||||
|
||||
default:
|
||||
return vAPI.messaging.UNHANDLED;
|
||||
}
|
||||
|
||||
callback(response);
|
||||
};
|
||||
|
||||
vAPI.messaging.listen('devtools.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
@ -1300,7 +1176,7 @@ vAPI.messaging.listen('settings.js', onMessage);
|
|||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// devtool-log.js
|
||||
// logger-ui.js
|
||||
|
||||
(function() {
|
||||
|
||||
|
@ -1323,10 +1199,19 @@ var onMessage = function(request, sender, callback) {
|
|||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'readLogBuffer':
|
||||
case 'readAll':
|
||||
var tabIds = {};
|
||||
for ( var tabId in µb.pageStores ) {
|
||||
if ( µb.pageStores.hasOwnProperty(tabId) ) {
|
||||
tabIds[tabId] = true;
|
||||
}
|
||||
}
|
||||
response = {
|
||||
colorBlind: µb.userSettings.colorBlindFriendly,
|
||||
entries: µb.logger.readAll(request.tabId)
|
||||
entries: µb.logger.readAll(),
|
||||
maxEntries: µb.userSettings.requestLogMaxEntries,
|
||||
noTabId: vAPI.noTabId,
|
||||
tabIds: tabIds
|
||||
};
|
||||
break;
|
||||
|
||||
|
@ -1337,7 +1222,7 @@ var onMessage = function(request, sender, callback) {
|
|||
callback(response);
|
||||
};
|
||||
|
||||
vAPI.messaging.listen('devtool-log.js', onMessage);
|
||||
vAPI.messaging.listen('logger-ui.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
|
|
@ -393,7 +393,7 @@ var renderPopup = function() {
|
|||
|
||||
// If you think the `=== true` is pointless, you are mistaken
|
||||
uDom('#gotoLog').toggleClass('enabled', popupData.canRequestLog === true)
|
||||
.attr('href', 'devtools.html?tabId=' + popupData.tabId);
|
||||
.attr('href', 'logger-ui.html');
|
||||
uDom('#gotoPick').toggleClass('enabled', popupData.canElementPicker === true);
|
||||
|
||||
var text;
|
||||
|
|
|
@ -477,7 +477,13 @@ vAPI.tabs.onPopup = function(details) {
|
|||
if ( pageStore ) {
|
||||
pageStore.logRequest(context, result);
|
||||
}
|
||||
µb.logger.writeOne(details.openerTabId, context, result);
|
||||
µb.logger.writeOne(
|
||||
details.openerTabId,
|
||||
'net',
|
||||
result,
|
||||
'popup',
|
||||
targetURL
|
||||
);
|
||||
|
||||
// Not blocked
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
|
|
@ -92,7 +92,7 @@ var onBeforeRequest = function(details) {
|
|||
// Possible outcomes: blocked, allowed-passthru, allowed-mirror
|
||||
|
||||
pageStore.logRequest(requestContext, result);
|
||||
µb.logger.writeOne(tabId, requestContext, result);
|
||||
µb.logger.writeOne(tabId, 'net', result, requestType, requestURL);
|
||||
|
||||
// Not blocked
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
@ -187,7 +187,7 @@ var onBeforeRootFrameRequest = function(details) {
|
|||
if ( pageStore ) {
|
||||
pageStore.logRequest(context, result);
|
||||
}
|
||||
µb.logger.writeOne(tabId, context, result);
|
||||
µb.logger.writeOne(tabId, 'net', result, 'main_frame', requestURL);
|
||||
|
||||
// Not blocked
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
@ -279,7 +279,7 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||
}
|
||||
|
||||
pageStore.logRequest(context, result);
|
||||
µb.logger.writeOne(vAPI.noTabId, context, result);
|
||||
µb.logger.writeOne(vAPI.noTabId, 'net', result, details.type, details.url);
|
||||
|
||||
// Not blocked
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
@ -328,7 +328,7 @@ var onHeadersReceived = function(details) {
|
|||
var result = pageStore.filterRequestNoCache(context);
|
||||
|
||||
pageStore.logRequest(context, result);
|
||||
µb.logger.writeOne(tabId, context, result);
|
||||
µb.logger.writeOne(tabId, 'net', result, 'inline-script', details.url);
|
||||
|
||||
// Don't block
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
@ -378,7 +378,7 @@ var onRootFrameHeadersReceived = function(details) {
|
|||
var result = pageStore.filterRequestNoCache(context);
|
||||
|
||||
pageStore.logRequest(context, result);
|
||||
µb.logger.writeOne(tabId, context, result);
|
||||
µb.logger.writeOne(tabId, 'net', result, 'inline-script', details.url);
|
||||
|
||||
// Don't block
|
||||
if ( µb.isAllowResult(result) ) {
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/logger-ui.css">
|
||||
<title>uMatrix log</title>
|
||||
</head>
|
||||
<body class="compactView f">
|
||||
|
||||
<div id="toolbar">
|
||||
<span id="compactViewToggler" class="button fa"></span>
|
||||
<span id="clear" class="button fa disabled"></span>
|
||||
<span id="filterButton" class="button fa"></span><input id="filterInput" type="text" placeholder="logFilterPrompt">
|
||||
<input id="maxEntries" type="text" size="5" title="logMaxEntriesTip">
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
<style></style>
|
||||
<table>
|
||||
<colgroup><col><col><col><col><col></colgroup>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="popupContainer">
|
||||
<div><span></span></div>
|
||||
</div>
|
||||
<div id="movingOverlay"></div>
|
||||
|
||||
<div style="display: none;">
|
||||
<div id="renderedURLTemplate"><span><span></span><b></b><span></span></span></div>
|
||||
</div>
|
||||
|
||||
<script src="js/vapi-common.js"></script>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/logger-ui.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue