mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-22 18:32:45 +01:00
lot of work related to dynamic filtering + new net requests logger
This commit is contained in:
parent
81e035589b
commit
1597ce7fd9
@ -208,6 +208,15 @@ vAPI.tabs.remove = function(tabId) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.tabs.reload = function(tabId, flags) {
|
||||
if ( typeof tabId === 'string' ) {
|
||||
tabId = parseInt(tabId, 10);
|
||||
}
|
||||
chrome.tabs.reload(tabId);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.tabs.injectScript = function(tabId, details, callback) {
|
||||
var onScriptExecuted = function() {
|
||||
// https://code.google.com/p/chromium/issues/detail?id=410868#c8
|
||||
|
68
src/css/devtool-log.css
Normal file
68
src/css/devtool-log.css
Normal file
@ -0,0 +1,68 @@
|
||||
body {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
font: 11px monospace;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
padding: 0;
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar {
|
||||
padding: 8px 0;
|
||||
position: fixed;
|
||||
text-align: center;
|
||||
top: 0;
|
||||
width: 4em;
|
||||
}
|
||||
#toolbar .button {
|
||||
background-color: white;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
font-size: large;
|
||||
margin: 0;
|
||||
padding: 0.5em 0;
|
||||
}
|
||||
#toolbar .button:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
#content {
|
||||
margin-left: 4em;
|
||||
width: calc(100% - 4em);
|
||||
}
|
||||
#content table {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
}
|
||||
#content table tr.blocked {
|
||||
background-color: rgba(192, 0, 0, 0.1)
|
||||
}
|
||||
#content table tr.allowed {
|
||||
background-color: rgba(0, 160, 0, 0.1)
|
||||
}
|
||||
#content table tr td {
|
||||
border: 1px solid #ccc;
|
||||
hyphens: none;
|
||||
padding: 3px;
|
||||
vertical-align: top;
|
||||
white-space: normal;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
#content table tr td:nth-of-type(1) {
|
||||
width: 15%;
|
||||
}
|
||||
#content table tr td:nth-of-type(3) {
|
||||
border-right: none;
|
||||
width: 75%;
|
||||
}
|
||||
#content table tr.blocked td:nth-of-type(3) b {
|
||||
background-color: rgba(192, 0, 0, 0.2);
|
||||
font-weight: normal;
|
||||
}
|
||||
#content table tr.allowed td:nth-of-type(3) b {
|
||||
background-color: rgba(0, 160, 0, 0.2);
|
||||
font-weight: normal;
|
||||
}
|
49
src/css/devtools.css
Normal file
49
src/css/devtools.css
Normal file
@ -0,0 +1,49 @@
|
||||
body {
|
||||
font-size: 13px;
|
||||
margin: 0;
|
||||
overflow-y: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
#toolbar {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
height: 4em;
|
||||
padding: 1em;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
}
|
||||
#toolbar > * {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#toolbar button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
font-size: 2em;
|
||||
margin: 0 0 0 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#toolbar #refresh {
|
||||
margin-left: 4px;
|
||||
}
|
||||
select {
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
min-width: 20em;
|
||||
max-width: 40em;
|
||||
}
|
||||
select option {
|
||||
max-width: 40em;
|
||||
}
|
||||
#content {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
height: calc(100vh - 4em);
|
||||
margin-top: 4em;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
@ -28,22 +28,41 @@ a {
|
||||
font-weight: normal;
|
||||
margin-left: 1em;
|
||||
}
|
||||
body > div {
|
||||
background-color: transparent;
|
||||
body[dir="ltr"] #panes {
|
||||
direction: rtl;
|
||||
}
|
||||
body[dir="rtl"] #panes {
|
||||
direction: ltr;
|
||||
}
|
||||
#panes > div {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
body > div:nth-of-type(1) {
|
||||
direction: rtl; /* scroll bar to the left */
|
||||
body[dir="ltr"] #panes > div {
|
||||
direction: ltr;
|
||||
}
|
||||
body[dir="rtl"] #panes > div {
|
||||
direction: rtl;
|
||||
}
|
||||
#panes > div:nth-of-type(2) {
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
width: 0;
|
||||
}
|
||||
body.dynamicFilteringEnabled > div:nth-of-type(1) {
|
||||
body[dir="ltr"] #panes > div:nth-of-type(2) {
|
||||
direction: rtl; /* scroll bar to the left */
|
||||
}
|
||||
body[dir="rtl"] #panes > div:nth-of-type(2) {
|
||||
direction: ltr; /* scroll bar to the right */
|
||||
}
|
||||
#panes.dfEnabled > div:nth-of-type(2) {
|
||||
overflow-y: auto;
|
||||
width: 320px;
|
||||
}
|
||||
body > div:nth-of-type(2) {
|
||||
padding: 4px 12px 0 5px;
|
||||
|
||||
#panes > div:nth-of-type(1) {
|
||||
padding: 4px 5px 0 5px;
|
||||
}
|
||||
p {
|
||||
margin: 16px 0;
|
||||
@ -96,17 +115,14 @@ p {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
#dynamicFilteringToggler {
|
||||
pointer-events: none;
|
||||
}
|
||||
#dynamicFilteringToggler::before {
|
||||
#dfToggler::before {
|
||||
color: gray;
|
||||
content: '+\202F';
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
pointer-events: auto;
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
}
|
||||
body.dynamicFilteringEnabled #dynamicFilteringToggler::before {
|
||||
#panes.dfEnabled #dfToggler::before {
|
||||
content: '\2212\202F';
|
||||
}
|
||||
|
||||
@ -142,24 +158,16 @@ body.dynamicFilteringEnabled #dynamicFilteringToggler::before {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: right;
|
||||
width: 5px;
|
||||
}
|
||||
body.dynamicFilteringEnabled #dynamicFilteringContainer {
|
||||
display: block;
|
||||
width: auto;
|
||||
}
|
||||
#dynamicFilteringContainer > div {
|
||||
background-color: transparent;
|
||||
background-color: #e6e6e6;
|
||||
border: 0;
|
||||
border-bottom: 1px solid white;
|
||||
direction: ltr;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 320px;
|
||||
}
|
||||
body.dynamicFilteringEnabled #dynamicFilteringContainer > div {
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
body.dynamicFilteringEnabled #dynamicFilteringContainer > div:hover {
|
||||
#dynamicFilteringContainer > div:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
#dynamicFilteringContainer > div#privacyInfo {
|
||||
@ -168,30 +176,21 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div:hover {
|
||||
padding: 4px 0;
|
||||
text-align: center;
|
||||
}
|
||||
#dynamicFilteringContainer > div.isDomain {
|
||||
margin-top: 2px;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-bottom: 1px solid white;
|
||||
box-sizing: border-box;
|
||||
color: transparent;
|
||||
color: #000;
|
||||
display: inline-block;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
pointer-events: none;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
|
||||
color: #000;
|
||||
overflow: hidden;
|
||||
pointer-events: auto;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span:nth-of-type(1) {
|
||||
border-right: 1px solid white;
|
||||
padding-right: 4px;
|
||||
padding-right: 2px;
|
||||
text-overflow: ellipsis;
|
||||
width: 70%;
|
||||
}
|
||||
@ -201,6 +200,7 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
|
||||
}
|
||||
#dynamicFilteringContainer > div > span:nth-of-type(3) {
|
||||
border-left: 1px solid white;
|
||||
color: #444;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
width: 15%;
|
||||
@ -208,9 +208,14 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
|
||||
#dynamicFilteringContainer > div.isDomain > span:nth-of-type(1) {
|
||||
font-weight: bold;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span:nth-of-type(3) {
|
||||
color: #666;
|
||||
pointer-events: auto;
|
||||
#dynamicFilteringContainer > div.allowed > span:nth-of-type(1) {
|
||||
background-color: rgba(0, 160, 0, 0.1);
|
||||
}
|
||||
#dynamicFilteringContainer > div.blocked > span:nth-of-type(1) {
|
||||
background-color: rgba(192, 0, 0, 0.1);
|
||||
}
|
||||
#dynamicFilteringContainer > div.allowed.blocked > span:nth-of-type(1) {
|
||||
background-color: rgba(192, 160, 0, 0.1);
|
||||
}
|
||||
#dynamicFilteringContainer > div > span.aRule {
|
||||
background-color: rgba(0, 160, 0, 0.3);
|
||||
@ -221,17 +226,17 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
|
||||
#dynamicFilteringContainer > div > span.nRule {
|
||||
background-color: rgba(96, 96, 96, 0.3);
|
||||
}
|
||||
#dynamicFilteringContainer > div > span.ownRule {
|
||||
color: white;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span.aRule.ownRule {
|
||||
background-color: rgba(0, 160, 0, 1);
|
||||
color: white;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span.bRule.ownRule {
|
||||
background-color: rgba(192, 0, 0, 1);
|
||||
color: white;
|
||||
}
|
||||
#dynamicFilteringContainer > div > span.nRule.ownRule {
|
||||
background-color: rgba(108, 108, 108, 1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#actionSelector {
|
||||
@ -264,3 +269,17 @@ body.dynamicFilteringEnabled #dynamicFilteringContainer > div > span {
|
||||
#dynamicFilteringContainer span.bRule #actionSelector > span:nth-of-type(3) {
|
||||
visibility: hidden;
|
||||
}
|
||||
#hotspotTip {
|
||||
background-color: #ffe;
|
||||
border: 1px dotted #ddb;
|
||||
border-radius: 5px;
|
||||
height: 50vh;
|
||||
opacity: 1;
|
||||
padding: 4px;
|
||||
position: fixed;
|
||||
right: 10px;
|
||||
text-align: center;
|
||||
top: 25vh;
|
||||
width: 20vw;
|
||||
z-index: 100;
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
div {
|
||||
margin: 1em 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
#refresh {
|
||||
margin: 0 0.5em 0 4px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
font-size: 2em;
|
||||
cursor: pointer;
|
||||
}
|
||||
select {
|
||||
padding: 2px 0;
|
||||
font-size: 14px;
|
||||
min-width: 20em;
|
||||
max-width: 40em;
|
||||
}
|
||||
select option {
|
||||
max-width: 40em;
|
||||
}
|
||||
#requests {
|
||||
margin: 2em 0 0 0;
|
||||
display: none;
|
||||
}
|
||||
#requests.logEnabled {
|
||||
display: block;
|
||||
}
|
||||
#requests table {
|
||||
margin: 1em 0;
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
min-width: 600px;
|
||||
}
|
||||
#requests.empty table {
|
||||
display: none;
|
||||
}
|
||||
tr td, tr th {
|
||||
border: 1px solid #aaa;
|
||||
padding: 4px 6px;
|
||||
white-space: pre;
|
||||
}
|
||||
tr.domainHeader td {
|
||||
font: 16px sans-serif;
|
||||
}
|
||||
tr.domainHeader td span {
|
||||
margin-right: 0.5em;
|
||||
font-size: 14px;
|
||||
color: #aaa;
|
||||
cursor: pointer;
|
||||
}
|
||||
tr.requestEntry {
|
||||
font: 12px monospace;
|
||||
}
|
||||
tr.requestEntry td:nth-of-type(1) {
|
||||
border: 0;
|
||||
background-color: white;
|
||||
width: 3em;
|
||||
}
|
||||
tr.requestEntry td:nth-of-type(2) {
|
||||
text-align: right;
|
||||
}
|
||||
tr.requestEntry td:nth-of-type(3),
|
||||
tr.requestEntry td:nth-of-type(4) {
|
||||
direction: ltr;
|
||||
}
|
||||
tr.logBlocked {
|
||||
background-color: #fff8f8;
|
||||
}
|
||||
tr.logBlocked ~ tr td:nth-of-type(3) b {
|
||||
padding: 2px 0;
|
||||
color: #000;
|
||||
background-color: rgba(255,0,0,0.1);
|
||||
}
|
||||
tr.logAllowed {
|
||||
background-color: #f8fff8
|
||||
}
|
||||
tr.logAllowed ~ tr td:nth-of-type(3) b {
|
||||
padding: 2px 0;
|
||||
color: #000;
|
||||
background-color: rgba(0,255,0,0.2);
|
||||
}
|
||||
tr.logMirrored {
|
||||
background-color: #ffffbb !important;
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
<a class="tabButton" href="#dyna-rules.html" data-i18n="rulesPageName"></a>
|
||||
<a class="tabButton" href="#whitelist.html" data-i18n="whitelistPageName"></a>
|
||||
<a class="tabButton" href="#settings.html" data-i18n="settingsPageName"></a>
|
||||
<a class="tabButton" href="#stats.html" data-i18n="statsPageName"></a>
|
||||
<a class="tabButton" href="#about.html" data-i18n="aboutPageName"></a>
|
||||
</div>
|
||||
</div>
|
||||
|
20
src/devtool-log.html
Normal file
20
src/devtool-log.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!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>µBlock log</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="toolbar">
|
||||
<span id="reload" class="button fa"></span>
|
||||
<span id="clear" class="button fa"></span>
|
||||
</div><!-- DO NOT REMOVE --><div id="content">
|
||||
<table><tbody></tbody></table>
|
||||
</div>
|
||||
<script src="js/vapi-client.js"></script>
|
||||
<script src="js/udom.js"></script>
|
||||
<script src="js/devtool-log.js"></script>
|
||||
</body>
|
||||
</html>
|
25
src/devtools.html
Normal file
25
src/devtools.html
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>µBlock — Statistics</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>
|
||||
</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>
|
@ -60,7 +60,6 @@ return {
|
||||
dynamicFilteringEnabled: false,
|
||||
experimentalEnabled: false,
|
||||
externalLists: defaultExternalLists,
|
||||
logRequests: false,
|
||||
parseAllABPHideFilters: true,
|
||||
showIconBadge: true
|
||||
},
|
||||
|
@ -997,8 +997,14 @@ FilterContainer.prototype.removeFromSelectorCache = function(targetHostname, typ
|
||||
if ( this.selectorCache.hasOwnProperty(hostname) === false ) {
|
||||
continue;
|
||||
}
|
||||
if ( targetHostname !== '*' && hostname !== targetHostname ) {
|
||||
continue;
|
||||
if ( targetHostname !== '*' ) {
|
||||
if ( hostname.slice(0 - targetHostname.length) !== targetHostname ) {
|
||||
continue;
|
||||
}
|
||||
if ( hostname.length !== targetHostname.length &&
|
||||
hostname.charAt(0 - targetHostname.length - 1) !== '.' ) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
this.selectorCache[hostname].remove(type);
|
||||
}
|
||||
|
178
src/js/devtool-log.js
Normal file
178
src/js/devtool-log.js
Normal file
@ -0,0 +1,178 @@
|
||||
/*******************************************************************************
|
||||
|
||||
sessbench - a Chromium 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() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('devtool-log.js');
|
||||
|
||||
var inspectedTabId = '';
|
||||
var doc = document;
|
||||
var body = doc.body;
|
||||
var tbody = doc.querySelector('#content tbody');
|
||||
var rowJunkyard = [];
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderURL = function(url, filter) {
|
||||
if ( filter.charAt(0) !== 's' ) {
|
||||
return 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 {
|
||||
reText = reText
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\?/g, '\\?')
|
||||
.replace('||', '')
|
||||
.replace(/\^/g, '.')
|
||||
.replace(/\*/g, '.*')
|
||||
;
|
||||
}
|
||||
var re = new RegExp(reText, 'gi');
|
||||
var matches = re.exec(url);
|
||||
var renderedURL = url;
|
||||
|
||||
if ( matches && matches[0].length ) {
|
||||
renderedURL = url.slice(0, matches.index) +
|
||||
'<b>' +
|
||||
url.slice(matches.index, re.lastIndex) +
|
||||
'</b>' +
|
||||
url.slice(re.lastIndex);
|
||||
}
|
||||
|
||||
return renderedURL;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var createRow = function() {
|
||||
var tr = rowJunkyard.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'));
|
||||
return tr;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogEntry = function(entry) {
|
||||
var tr = createRow();
|
||||
if ( entry.result.charAt(1) === 'b' ) {
|
||||
tr.classList.add('blocked');
|
||||
} else if ( entry.result.charAt(1) === 'a' ) {
|
||||
tr.classList.add('allowed');
|
||||
}
|
||||
tr.cells[0].textContent = entry.result.slice(3);
|
||||
tr.cells[1].textContent = entry.type;
|
||||
tr.cells[2].innerHTML = renderURL(entry.url, entry.result);
|
||||
tbody.insertBefore(tr, tbody.firstChild);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderLogBuffer = function(buffer) {
|
||||
// Preserve scroll position
|
||||
var height = tbody.offsetHeight;
|
||||
|
||||
var n = buffer.length;
|
||||
for ( var i = 0; i < n; i++ ) {
|
||||
renderLogEntry(buffer[i]);
|
||||
}
|
||||
if ( body.scrollTop !== 0 ) {
|
||||
body.scrollTop += tbody.offsetHeight - height;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onBufferRead = function(buffer) {
|
||||
if ( Array.isArray(buffer ) ) {
|
||||
renderLogBuffer(buffer);
|
||||
}
|
||||
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 rows = tbody.rows;
|
||||
var row;
|
||||
var i = rows.length;
|
||||
while ( i-- ) {
|
||||
row = rows[i];
|
||||
row.parentNode.removeChild(row);
|
||||
rowJunkyard.push(row);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var reloadTab = function() {
|
||||
messager.send({ what: 'reloadTab', tabId: inspectedTabId });
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
readLogBuffer();
|
||||
|
||||
uDom('#reload').on('click', reloadTab);
|
||||
uDom('#clear').on('click', clearBuffer);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
98
src/js/devtools.js
Normal file
98
src/js/devtools.js
Normal file
@ -0,0 +1,98 @@
|
||||
/*******************************************************************************
|
||||
|
||||
µBlock - a Chromium 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('stats.js');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderPageSelector = function(targetTabId) {
|
||||
var selectedTabId = targetTabId || uDom('#pageSelector').val();
|
||||
var onTabReceived = function(tabId, tabTitle) {
|
||||
uDom('#pageSelector').append('<option value="' + tabId + '">' + tabTitle);
|
||||
if ( tabId.toString() === selectedTabId ) {
|
||||
uDom('#pageSelector').val(tabId);
|
||||
}
|
||||
};
|
||||
var onDataReceived = function(pageDetails) {
|
||||
uDom('#pageSelector option').remove();
|
||||
if ( pageDetails.hasOwnProperty(selectedTabId) === false ) {
|
||||
selectedTabId = pageDetails[0];
|
||||
}
|
||||
for ( var tabId in pageDetails ) {
|
||||
if ( pageDetails.hasOwnProperty(tabId) ) {
|
||||
onTabReceived(tabId, pageDetails[tabId]);
|
||||
}
|
||||
}
|
||||
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 ) {
|
||||
inspector.attr('src', targetSrc);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
renderPageSelector(tabId);
|
||||
|
||||
uDom('#pageSelector').on('change', pageSelectorChanged);
|
||||
uDom('#refresh').on('click', function() { renderPageSelector(); } );
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
@ -38,13 +38,25 @@ var Matrix = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var supportedTypes = {
|
||||
'*': true,
|
||||
'inline-script': true,
|
||||
'script': true,
|
||||
'1p-script': true,
|
||||
'3p-script': true,
|
||||
'sub_frame': true,
|
||||
'3p-frame': true,
|
||||
'image': true
|
||||
};
|
||||
|
||||
var typeBitOffsets = {
|
||||
'*': 0,
|
||||
'inline-script': 2,
|
||||
'1p-script': 4,
|
||||
'3p-script': 6,
|
||||
'3p-frame': 8,
|
||||
'image': 10
|
||||
'image': 10,
|
||||
'3p-any': 12
|
||||
};
|
||||
|
||||
var actionToNameMap = {
|
||||
@ -192,6 +204,19 @@ Matrix.prototype.clearRegisters = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var isFirstParty = function(srcHostname, desHostname) {
|
||||
if ( desHostname.slice(0 - srcHostname.length) !== srcHostname ) {
|
||||
return false;
|
||||
}
|
||||
// Be sure to not confuse 'example.com' with 'anotherexample.com'
|
||||
if ( desHostname.lenght === srcHostname.lenght ) {
|
||||
return true;
|
||||
}
|
||||
return desHostname.charAt(desHostname.length - srcHostname.length - 1) === '.';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
|
||||
var bitOffset = typeBitOffsets[type];
|
||||
var s = srcHostname;
|
||||
@ -217,32 +242,42 @@ Matrix.prototype.evaluateCellZ = function(srcHostname, desHostname, type) {
|
||||
/******************************************************************************/
|
||||
|
||||
Matrix.prototype.evaluateCellZY = function(srcHostname, desHostname, type) {
|
||||
if ( typeBitOffsets.hasOwnProperty(type) === false ) {
|
||||
this.r = 0;
|
||||
|
||||
if ( supportedTypes.hasOwnProperty(type) === false ) {
|
||||
this.type = '';
|
||||
this.r = 0;
|
||||
return this;
|
||||
}
|
||||
this.type = type;
|
||||
// Specific-hostname specific-type cell
|
||||
|
||||
this.type = '*';
|
||||
|
||||
// Specific-destination + any type
|
||||
this.y = desHostname;
|
||||
this.r = this.evaluateCellZ(srcHostname, desHostname, type);
|
||||
if ( this.r !== 0 ) { return this; }
|
||||
|
||||
var d = desHostname;
|
||||
for (;;) {
|
||||
d = toBroaderHostname(d);
|
||||
if ( d === '*' ) {
|
||||
break;
|
||||
}
|
||||
// specific-hostname specific-type cell
|
||||
this.y = d;
|
||||
this.r = this.evaluateCellZ(srcHostname, d, type);
|
||||
if ( this.r !== 0 ) { return this; }
|
||||
}
|
||||
|
||||
// Any-hostname specific-type cells
|
||||
// Any destination + specific-type
|
||||
this.y = '*';
|
||||
|
||||
if ( type === 'script' ) {
|
||||
type = isFirstParty(srcHostname, desHostname) ? '1p-script' : '3p-script';
|
||||
} else if ( type === 'sub-frame' && isFirstParty(srcHostname, desHostname) === false ) {
|
||||
type = '3p-frame';
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
this.r = this.evaluateCellZ(srcHostname, '*', type);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@ -447,7 +482,7 @@ Matrix.prototype.fromSelfie = function(selfie) {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
return new Matrix;
|
||||
return new Matrix();
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
|
@ -71,6 +71,10 @@ var onMessage = function(request, sender, callback) {
|
||||
µb.reloadPresetBlacklists(request.switches, request.update);
|
||||
break;
|
||||
|
||||
case 'reloadTab':
|
||||
vAPI.tabs.reload(request.tabId);
|
||||
break;
|
||||
|
||||
case 'userSettings':
|
||||
response = µb.changeUserSettings(request.name, request.value);
|
||||
break;
|
||||
@ -177,8 +181,7 @@ var getStats = function(tabId) {
|
||||
pageAllowedRequestCount: 0,
|
||||
netFilteringSwitch: false,
|
||||
cosmeticFilteringSwitch: false,
|
||||
logRequests: µb.userSettings.logRequests,
|
||||
dynamicFilteringEnabled: µb.userSettings.dynamicFilteringEnabled
|
||||
dfEnabled: µb.userSettings.dynamicFilteringEnabled
|
||||
};
|
||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||
if ( pageStore ) {
|
||||
@ -749,50 +752,30 @@ var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var getPageDetails = function(µb, tabId) {
|
||||
var r = {
|
||||
blockedRequests: [],
|
||||
allowedRequests: [],
|
||||
hash: ''
|
||||
};
|
||||
var pageStore = µb.pageStores[tabId];
|
||||
if ( !pageStore ) {
|
||||
return r;
|
||||
var getPageDetails = function(callback) {
|
||||
var out = {};
|
||||
var tabIds = Object.keys(µb.pageStores);
|
||||
|
||||
var countdown = tabIds.length;
|
||||
if ( countdown === 0 ) {
|
||||
callback(out);
|
||||
return;
|
||||
}
|
||||
var prepareRequests = function(wantBlocked, hasher) {
|
||||
var µburi = µb.URI;
|
||||
var dict = pageStore.netFilteringCache.fetchAll();
|
||||
var r = [];
|
||||
var details, hostname, domain;
|
||||
for ( var url in dict ) {
|
||||
if ( dict.hasOwnProperty(url) === false ) {
|
||||
continue;
|
||||
}
|
||||
details = dict[url];
|
||||
if ( wantBlocked !== pageStore.boolFromResult(details.result) ) {
|
||||
continue;
|
||||
}
|
||||
hasher.appendStr(url);
|
||||
hasher.appendStr(details.result);
|
||||
hostname = µburi.hostnameFromURI(url);
|
||||
domain = µburi.domainFromHostname(hostname) || hostname;
|
||||
r.push({
|
||||
url: url,
|
||||
domain: domain,
|
||||
reason: details.result,
|
||||
type: details.type,
|
||||
flags: details.flags
|
||||
});
|
||||
|
||||
var onTabDetails = function(tab) {
|
||||
if ( tab ) {
|
||||
out[tab.id] = tab.title;
|
||||
}
|
||||
countdown -= 1;
|
||||
if ( countdown === 0 ) {
|
||||
callback(out);
|
||||
}
|
||||
return r;
|
||||
};
|
||||
var hasher = new YaMD5();
|
||||
if ( µb.userSettings.logRequests ) {
|
||||
r.blockedRequests = prepareRequests(true, hasher);
|
||||
r.allowedRequests = prepareRequests(false, hasher);
|
||||
|
||||
var i = countdown;
|
||||
while ( i-- ) {
|
||||
vAPI.tabs.get(tabIds[i], onTabDetails);
|
||||
}
|
||||
r.hash = hasher.end();
|
||||
return r;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -800,8 +783,8 @@ var getPageDetails = function(µb, tabId) {
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
case 'getTabForStats':
|
||||
vAPI.tabs.get(request.tabId, callback);
|
||||
case 'getPageDetails':
|
||||
getPageDetails(callback);
|
||||
return;
|
||||
|
||||
default:
|
||||
@ -812,14 +795,6 @@ var onMessage = function(request, sender, callback) {
|
||||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'getPageSelectors':
|
||||
response = Object.keys(µb.pageStores);
|
||||
break;
|
||||
|
||||
case 'getPageDetails':
|
||||
response = getPageDetails(µb, request.tabId);
|
||||
break;
|
||||
|
||||
default:
|
||||
return vAPI.messaging.UNHANDLED;
|
||||
}
|
||||
@ -933,6 +908,52 @@ vAPI.messaging.listen('about.js', onMessage);
|
||||
|
||||
})();
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// devtool-log.js
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var µb = µBlock;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onMessage = function(request, sender, callback) {
|
||||
// Async
|
||||
switch ( request.what ) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Sync
|
||||
var response;
|
||||
|
||||
switch ( request.what ) {
|
||||
case 'readLogBuffer':
|
||||
var pageStore = µb.pageStoreFromTabId(request.tabId);
|
||||
if ( pageStore ) {
|
||||
response = pageStore.logBuffer.readAll();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return vAPI.messaging.UNHANDLED;
|
||||
}
|
||||
|
||||
callback(response);
|
||||
};
|
||||
|
||||
vAPI.messaging.listen('devtool-log.js', onMessage);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
||||
// https://www.youtube.com/watch?v=3_WcygKJP1k
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -45,22 +45,194 @@ var µb = µBlock;
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
var LogEntry = function(details, result) {
|
||||
this.init(details, result);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logEntryFactory = function(details, result) {
|
||||
var entry = logEntryJunkyard.pop();
|
||||
if ( entry ) {
|
||||
return entry.init(details, result);
|
||||
}
|
||||
return new LogEntry(details, result);
|
||||
};
|
||||
|
||||
var logEntryJunkyard = [];
|
||||
var logEntryJunkyardMax = 100;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogEntry.prototype.init = function(details, result) {
|
||||
this.tstamp = Date.now();
|
||||
this.url = details.requestURL;
|
||||
this.domain = details.requestDomain;
|
||||
this.hostname = details.requestHostname;
|
||||
this.type = details.requestType;
|
||||
this.result = result;
|
||||
return this;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogEntry.prototype.dispose = function() {
|
||||
this.url = this.domain = this.hostname = this.type = this.result = '';
|
||||
if ( logEntryJunkyard.length < logEntryJunkyardMax ) {
|
||||
logEntryJunkyard.push(this);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var LogBuffer = function() {
|
||||
this.lastReadTime = 0;
|
||||
this.size = 25;
|
||||
this.buffer = null;
|
||||
this.readPtr = 0;
|
||||
this.writePtr = 0;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logBufferFactory = function() {
|
||||
return new LogBuffer();
|
||||
};
|
||||
|
||||
var liveLogBuffers = [];
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.dispose = function() {
|
||||
if ( this.buffer === null ) {
|
||||
return null;
|
||||
}
|
||||
var entry;
|
||||
var i = this.buffer.length;
|
||||
while ( i-- ) {
|
||||
entry = this.buffer[i];
|
||||
if ( entry instanceof LogEntry ) {
|
||||
entry.dispose();
|
||||
}
|
||||
}
|
||||
this.buffer = null;
|
||||
return null;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.start = function() {
|
||||
if ( this.buffer === null ) {
|
||||
this.buffer = new Array(this.size);
|
||||
this.readPtr = 0;
|
||||
this.writePtr = 0;
|
||||
liveLogBuffers.push(this);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.stop = function() {
|
||||
this.dispose();
|
||||
this.buffer = null;
|
||||
// The janitor will remove us from the live pool eventually.
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.writeOne = function(details, result) {
|
||||
if ( this.buffer === null ) {
|
||||
return;
|
||||
}
|
||||
// Reusing log entry = less memory churning
|
||||
var entry = this.buffer[this.writePtr];
|
||||
if ( entry instanceof LogEntry === false ) {
|
||||
this.buffer[this.writePtr] = logEntryFactory(details, result);
|
||||
} else {
|
||||
entry.init(details, result);
|
||||
}
|
||||
this.writePtr += 1;
|
||||
if ( this.writePtr === this.size ) {
|
||||
this.writePtr = 0;
|
||||
}
|
||||
// Grow the buffer between 1.5x-2x the current size
|
||||
if ( this.writePtr === this.readPtr ) {
|
||||
var toMove = this.buffer.slice(0, this.writePtr);
|
||||
var minSize = Math.ceil(this.size * 1.5);
|
||||
this.size += this.writePtr;
|
||||
if ( this.size < minSize ) {
|
||||
this.buffer = this.buffer.concat(toMove, new Array(minSize - this.size));
|
||||
this.writePtr = this.size;
|
||||
} else {
|
||||
this.buffer = this.buffer.concat(toMove);
|
||||
this.writePtr = 0;
|
||||
}
|
||||
this.size = this.buffer.length;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
LogBuffer.prototype.readAll = function() {
|
||||
var out;
|
||||
if ( this.buffer === null ) {
|
||||
this.start();
|
||||
out = [];
|
||||
} else if ( this.readPtr < this.writePtr ) {
|
||||
out = this.buffer.slice(this.readPtr, this.writePtr);
|
||||
} else if ( this.writePtr < this.readPtr ) {
|
||||
out = this.buffer.slice(this.readPtr).concat(this.buffer.slice(0, this.writePtr));
|
||||
} else {
|
||||
out = [];
|
||||
}
|
||||
this.readPtr = this.writePtr;
|
||||
this.lastReadTime = Date.now();
|
||||
return out;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logBufferJanitor = function() {
|
||||
var logBuffer;
|
||||
var obsolete = Date.now() - logBufferObsoleteAfter;
|
||||
var i = liveLogBuffers.length;
|
||||
while ( i-- ) {
|
||||
logBuffer = liveLogBuffers[i];
|
||||
if ( logBuffer.lastReadTime < obsolete ) {
|
||||
logBuffer.stop();
|
||||
liveLogBuffers.splice(i, 1);
|
||||
}
|
||||
}
|
||||
setTimeout(logBufferJanitor, logBufferJanitorPeriod);
|
||||
};
|
||||
|
||||
// The janitor will look for stale log buffer every 2 minutes.
|
||||
var logBufferJanitorPeriod = 2 * 60 * 1000;
|
||||
|
||||
// After 30 seconds without being read, a buffer will be considered unused, and
|
||||
// thus removed from memory.
|
||||
var logBufferObsoleteAfter = 30 * 1000;
|
||||
|
||||
setTimeout(logBufferJanitor, logBufferJanitorPeriod);
|
||||
|
||||
/******************************************************************************/
|
||||
/******************************************************************************/
|
||||
|
||||
// To mitigate memory churning
|
||||
var netFilteringResultCacheEntryJunkyard = [];
|
||||
var netFilteringResultCacheEntryJunkyardMax = 200;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var NetFilteringResultCacheEntry = function(result, type, flags) {
|
||||
this.init(result, type, flags);
|
||||
var NetFilteringResultCacheEntry = function(result, type) {
|
||||
this.init(result, type);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
NetFilteringResultCacheEntry.prototype.init = function(result, type, flags) {
|
||||
NetFilteringResultCacheEntry.prototype.init = function(result, type) {
|
||||
this.result = result;
|
||||
this.type = type;
|
||||
this.flags = flags;
|
||||
this.time = Date.now();
|
||||
};
|
||||
|
||||
@ -76,12 +248,12 @@ NetFilteringResultCacheEntry.prototype.dispose = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
NetFilteringResultCacheEntry.factory = function(result, type, flags) {
|
||||
NetFilteringResultCacheEntry.factory = function(result, type) {
|
||||
var entry = netFilteringResultCacheEntryJunkyard.pop();
|
||||
if ( entry === undefined ) {
|
||||
entry = new NetFilteringResultCacheEntry(result, type, flags);
|
||||
entry = new NetFilteringResultCacheEntry(result, type);
|
||||
} else {
|
||||
entry.init(result, type, flags);
|
||||
entry.init(result, type);
|
||||
}
|
||||
return entry;
|
||||
};
|
||||
@ -115,10 +287,11 @@ NetFilteringResultCache.factory = function() {
|
||||
/******************************************************************************/
|
||||
|
||||
NetFilteringResultCache.prototype.init = function() {
|
||||
this.uname = 'NetFilteringResultCache:' + uidGenerator++;
|
||||
this.urls = {};
|
||||
this.count = 0;
|
||||
this.shelfLife = 60 * 1000;
|
||||
this.timer = null;
|
||||
this.boundPruneAsyncCallback = this.pruneAsyncCallback.bind(this);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -130,10 +303,12 @@ NetFilteringResultCache.prototype.dispose = function() {
|
||||
}
|
||||
this.urls[key].dispose();
|
||||
}
|
||||
µBlock.asyncJobs.remove(this.uname);
|
||||
this.uname = '';
|
||||
this.urls = {};
|
||||
this.count = 0;
|
||||
if ( this.timer !== null ) {
|
||||
clearTimeout(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
if ( netFilteringCacheJunkyard.length < netFilteringCacheJunkyardMax ) {
|
||||
netFilteringCacheJunkyard.push(this);
|
||||
}
|
||||
@ -142,16 +317,17 @@ NetFilteringResultCache.prototype.dispose = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
NetFilteringResultCache.prototype.add = function(url, result, type, flags) {
|
||||
NetFilteringResultCache.prototype.add = function(context, result) {
|
||||
var url = context.requestURL;
|
||||
var type = context.requestType;
|
||||
var entry = this.urls[url];
|
||||
if ( entry !== undefined ) {
|
||||
entry.result = result;
|
||||
entry.type = type;
|
||||
entry.flags = flags;
|
||||
entry.time = Date.now();
|
||||
return;
|
||||
}
|
||||
this.urls[url] = NetFilteringResultCacheEntry.factory(result, type, flags);
|
||||
this.urls[url] = NetFilteringResultCacheEntry.factory(result, type);
|
||||
if ( this.count === 0 ) {
|
||||
this.pruneAsync();
|
||||
}
|
||||
@ -197,13 +373,14 @@ NetFilteringResultCache.prototype.prune = function() {
|
||||
/******************************************************************************/
|
||||
|
||||
NetFilteringResultCache.prototype.pruneAsync = function() {
|
||||
µBlock.asyncJobs.add(
|
||||
this.uname,
|
||||
null,
|
||||
this.prune.bind(this),
|
||||
this.shelfLife + 120000,
|
||||
false
|
||||
);
|
||||
if ( this.timer === null ) {
|
||||
this.timer = setTimeout(this.boundPruneAsyncCallback, this.shelfLife * 2);
|
||||
}
|
||||
};
|
||||
|
||||
NetFilteringResultCache.prototype.pruneAsyncCallback = function() {
|
||||
this.timer = null;
|
||||
this.prune();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -272,12 +449,6 @@ var pageStoreJunkyardMax = 10;
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// Cache only what is worth it if logging is disabled
|
||||
// http://jsperf.com/string-indexof-vs-object
|
||||
var collapsibleRequestTypes = 'image sub_frame object';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var PageStore = function(tabId, pageURL) {
|
||||
this.init(tabId, pageURL);
|
||||
};
|
||||
@ -318,10 +489,12 @@ PageStore.prototype.init = function(tabId, pageURL) {
|
||||
this.perLoadBlockedRequestCount = 0;
|
||||
this.perLoadAllowedRequestCount = 0;
|
||||
this.skipLocalMirroring = false;
|
||||
|
||||
this.netFilteringCache = NetFilteringResultCache.factory();
|
||||
if ( µb.userSettings.logRequests ) {
|
||||
this.netFilteringCache.shelfLife = 30 * 60 * 1000;
|
||||
|
||||
// Preserve old buffer if there is one already, it may be in use, and
|
||||
// overwritting it would required another read to restart it.
|
||||
if ( this.logBuffer instanceof LogBuffer === false ) {
|
||||
this.logBuffer = logBufferFactory();
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -374,6 +547,7 @@ PageStore.prototype.dispose = function() {
|
||||
this.hostnameToCountMap = null;
|
||||
this.disposeFrameStores();
|
||||
this.netFilteringCache = this.netFilteringCache.dispose();
|
||||
this.logBuffer = this.logBuffer.dispose();
|
||||
if ( pageStoreJunkyard.length < pageStoreJunkyardMax ) {
|
||||
pageStoreJunkyard.push(this);
|
||||
}
|
||||
@ -422,27 +596,46 @@ PageStore.prototype.getNetFilteringSwitch = function() {
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.filterRequest = function(context) {
|
||||
var requestURL = context.requestURL;
|
||||
|
||||
if ( this.getNetFilteringSwitch() === false ) {
|
||||
this.recordResult(context.requestType, requestURL, '');
|
||||
this.cacheResult(context, '');
|
||||
return '';
|
||||
}
|
||||
|
||||
var entry = this.netFilteringCache.lookup(requestURL);
|
||||
var entry = this.netFilteringCache.lookup(context.requestURL);
|
||||
if ( entry !== undefined ) {
|
||||
//console.debug('cache HIT: PageStore.filterRequest("%s")', requestURL);
|
||||
//console.debug('cache HIT: PageStore.filterRequest("%s")', context.requestURL);
|
||||
return entry.result;
|
||||
}
|
||||
|
||||
var result = µb.filterRequest(context);
|
||||
var result = '';
|
||||
|
||||
//console.debug('cache MISS: PageStore.filterRequest("%s")', requestURL);
|
||||
this.recordResult(context.requestType, requestURL, result);
|
||||
// Given that:
|
||||
// - Dynamic filtering override static filtering
|
||||
// - Evaluating dynamic filtering is much faster than static filtering
|
||||
// We evaluate dynamic filtering first, and hopefully we can skip
|
||||
// evaluation of static filtering.
|
||||
var df = µb.dynamicNetFilteringEngine.clearRegisters();
|
||||
df.evaluateCellZY(context.rootHostname, context.requestHostname, context.requestType);
|
||||
if ( df.mustBlockOrAllow() ) {
|
||||
result = df.toFilterString();
|
||||
}
|
||||
|
||||
// TODO: send this to a dev-panel tool
|
||||
//console.debug('[%s, %s] = "%s"', context.requestHostname, context.requestType, result);
|
||||
// Static filtering never override dynamic filtering
|
||||
if ( result === '' ) {
|
||||
result = µb.staticNetFilteringEngine.matchString(context);
|
||||
}
|
||||
|
||||
//console.debug('cache MISS: PageStore.filterRequest("%s")', context.requestURL);
|
||||
this.cacheResult(context, result);
|
||||
|
||||
// console.debug('[%s, %s] = "%s"', context.requestHostname, context.requestType, result);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.cacheResult = function(context, result) {
|
||||
var requestHostname = context.requestHostname;
|
||||
if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) {
|
||||
this.hostnameToCountMap[requestHostname] = 0;
|
||||
@ -453,25 +646,14 @@ PageStore.prototype.filterRequest = function(context) {
|
||||
} else /* if ( c === 'b' ) */ {
|
||||
this.hostnameToCountMap[requestHostname] += 0x00000001;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.setRequestFlags = function(requestURL, targetBits, valueBits) {
|
||||
var entry = this.netFilteringCache.lookup(requestURL);
|
||||
if ( entry !== undefined ) {
|
||||
entry.flags = (entry.flags & ~targetBits) | (valueBits & targetBits);
|
||||
if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) {
|
||||
this.netFilteringCache.add(context, result);
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
PageStore.prototype.recordResult = function(requestType, requestURL, result) {
|
||||
if ( collapsibleRequestTypes.indexOf(requestType) !== -1 || µb.userSettings.logRequests ) {
|
||||
this.netFilteringCache.add(requestURL, result, requestType, 0);
|
||||
}
|
||||
};
|
||||
// Cache only what is worth it if logging is disabled
|
||||
// http://jsperf.com/string-indexof-vs-object
|
||||
var collapsibleRequestTypes = 'image sub_frame object';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
@ -493,7 +675,7 @@ PageStore.prototype.updateBadge = function() {
|
||||
var iconStr = '';
|
||||
if ( µb.userSettings.showIconBadge && netFiltering && this.perLoadBlockedRequestCount ) {
|
||||
// Safari can't show formatted strings, only integers.
|
||||
if (vAPI.safari) {
|
||||
if ( vAPI.safari ) {
|
||||
iconStr = this.perLoadBlockedRequestCount;
|
||||
}
|
||||
else {
|
||||
|
161
src/js/popup.js
161
src/js/popup.js
@ -29,8 +29,9 @@
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var stats;
|
||||
var dynaTypes = [
|
||||
var popupData;
|
||||
var dfPaneBuilt = false;
|
||||
var dfTypes = [
|
||||
'image',
|
||||
'inline-script',
|
||||
'1p-script',
|
||||
@ -48,7 +49,7 @@ var scopeToSrcHostnameMap = {
|
||||
var threePlus = '+++';
|
||||
var threeMinus = '−−−';
|
||||
var sixSpace = '\u2007\u2007\u2007\u2007\u2007\u2007';
|
||||
var dynaHotspots = null;
|
||||
var dfHotspots = null;
|
||||
var hostnameToSortableTokenMap = {};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -60,15 +61,15 @@ var messager = vAPI.messaging.channel('popup.js');
|
||||
/******************************************************************************/
|
||||
|
||||
var cachePopupData = function(data) {
|
||||
stats = {};
|
||||
popupData = {};
|
||||
scopeToSrcHostnameMap['.'] = '';
|
||||
hostnameToSortableTokenMap = {};
|
||||
if ( typeof data !== 'object' ) {
|
||||
return stats;
|
||||
return popupData;
|
||||
}
|
||||
stats = data;
|
||||
scopeToSrcHostnameMap['.'] = stats.pageHostname || '';
|
||||
var hostnameDict = stats.hostnameDict;
|
||||
popupData = data;
|
||||
scopeToSrcHostnameMap['.'] = popupData.pageHostname || '';
|
||||
var hostnameDict = popupData.hostnameDict;
|
||||
if ( typeof hostnameDict === 'object' ) {
|
||||
var domain, prefix;
|
||||
for ( var hostname in hostnameDict ) {
|
||||
@ -76,14 +77,14 @@ var cachePopupData = function(data) {
|
||||
continue;
|
||||
}
|
||||
domain = hostnameDict[hostname].domain;
|
||||
if ( domain === stats.pageDomain ) {
|
||||
if ( domain === popupData.pageDomain ) {
|
||||
domain = '\u0020';
|
||||
}
|
||||
prefix = hostname.slice(0, 0 - domain.length);
|
||||
hostnameToSortableTokenMap[hostname] = domain + prefix.split('.').reverse().join('.');
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
return popupData;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -118,12 +119,15 @@ var addDynamicFilterRow = function(des) {
|
||||
row.descendants('[data-des]').attr('data-des', des);
|
||||
row.descendants('div > span:nth-of-type(1)').text(des);
|
||||
|
||||
var hnDetails = stats.hostnameDict[des] || {};
|
||||
var hnDetails = popupData.hostnameDict[des] || {};
|
||||
var isDomain = des === hnDetails.domain;
|
||||
row.toggleClass('isDomain', isDomain);
|
||||
if ( hnDetails.allowCount !== 0 ) {
|
||||
touchedDomains[hnDetails.domain] = true;
|
||||
row.addClass('wasTouched');
|
||||
row.addClass('allowed');
|
||||
}
|
||||
if ( hnDetails.blockCount !== 0 ) {
|
||||
row.addClass('blocked');
|
||||
}
|
||||
|
||||
row.appendTo('#dynamicFilteringContainer');
|
||||
@ -134,8 +138,8 @@ var addDynamicFilterRow = function(des) {
|
||||
// of the popup, and the left pane will have a scrollbar if ever its
|
||||
// height is larger than what is available.
|
||||
if ( popupHeight === undefined ) {
|
||||
popupHeight = uDom('body > div:nth-of-type(2)').nodeAt(0).offsetHeight;
|
||||
uDom('body > div:nth-of-type(1)').css('height', popupHeight + 'px');
|
||||
popupHeight = uDom('#panes > div:nth-of-type(1)').nodeAt(0).offsetHeight;
|
||||
uDom('#panes > div:nth-of-type(2)').css('height', popupHeight + 'px');
|
||||
}
|
||||
return row;
|
||||
};
|
||||
@ -169,10 +173,10 @@ var syncDynamicFilterCell = function(scope, des, type, result) {
|
||||
if ( scope !== '.' || type !== '*' ) {
|
||||
return;
|
||||
}
|
||||
if ( stats.hostnameDict.hasOwnProperty(des) === false ) {
|
||||
if ( popupData.hostnameDict.hasOwnProperty(des) === false ) {
|
||||
return;
|
||||
}
|
||||
var hnDetails = stats.hostnameDict[des];
|
||||
var hnDetails = popupData.hostnameDict[des];
|
||||
var aCount = hnDetails.allowCount;
|
||||
var bCount = hnDetails.blockCount;
|
||||
if ( aCount === 0 && bCount === 0 ) {
|
||||
@ -192,9 +196,9 @@ var syncDynamicFilterCell = function(scope, des, type, result) {
|
||||
|
||||
var syncAllDynamicFilters = function() {
|
||||
var hasRule = false;
|
||||
var rules = stats.dynamicFilterRules;
|
||||
var rules = popupData.dynamicFilterRules;
|
||||
var type, result;
|
||||
var types = dynaTypes;
|
||||
var types = dfTypes;
|
||||
var i = types.length;
|
||||
while ( i-- ) {
|
||||
type = types[i];
|
||||
@ -220,28 +224,30 @@ var syncAllDynamicFilters = function() {
|
||||
}
|
||||
|
||||
uDom('#privacyInfo').text(vAPI.i18n('popupHitDomainCountPrompt').replace('{{count}}', Object.keys(touchedDomains).length));
|
||||
|
||||
if ( dfPaneBuilt !== true ) {
|
||||
uDom('#dynamicFilteringContainer')
|
||||
.on('click', 'span[data-src]', unsetDynamicFilterHandler)
|
||||
.on('mouseenter', '[data-src]', mouseenterCellHandler)
|
||||
.on('mouseleave', '[data-src]', mouseleaveCellHandler);
|
||||
dfHotspots = uDom('#actionSelector')
|
||||
.on('click', 'span', setDynamicFilterHandler)
|
||||
.detach();
|
||||
dfPaneBuilt = true;
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderPopup = function(details) {
|
||||
if ( !cachePopupData(details) ) {
|
||||
return;
|
||||
}
|
||||
var renderPopup = function() {
|
||||
uDom('#appname').text(popupData.appName);
|
||||
uDom('#version').text(popupData.appVersion);
|
||||
|
||||
var hdr = uDom('#version');
|
||||
hdr.nodes[0].previousSibling.textContent = details.appName;
|
||||
hdr.html(hdr.html() + 'v' + details.appVersion);
|
||||
|
||||
var isHTTP = /^https?:\/\/[0-9a-z]/.test(stats.pageURL);
|
||||
var isHTTP = /^https?:\/\/[0-9a-z]/.test(popupData.pageURL);
|
||||
|
||||
// Conditions for request log:
|
||||
// - `http` or `https` scheme
|
||||
// - logging of requests enabled
|
||||
uDom('#gotoLog').toggleClass(
|
||||
'enabled',
|
||||
isHTTP && stats.logRequests
|
||||
);
|
||||
uDom('#gotoLog').toggleClass('enabled', isHTTP);
|
||||
|
||||
// Conditions for element picker:
|
||||
// - `http` or `https` scheme
|
||||
@ -251,8 +257,8 @@ var renderPopup = function(details) {
|
||||
);
|
||||
|
||||
var or = vAPI.i18n('popupOr');
|
||||
var blocked = stats.pageBlockedRequestCount;
|
||||
var total = stats.pageAllowedRequestCount + blocked;
|
||||
var blocked = popupData.pageBlockedRequestCount;
|
||||
var total = popupData.pageAllowedRequestCount + blocked;
|
||||
var html = [];
|
||||
if ( total === 0 ) {
|
||||
html.push('0');
|
||||
@ -268,8 +274,8 @@ var renderPopup = function(details) {
|
||||
}
|
||||
uDom('#page-blocked').html(html.join(''));
|
||||
|
||||
blocked = stats.globalBlockedRequestCount;
|
||||
total = stats.globalAllowedRequestCount + blocked;
|
||||
blocked = popupData.globalBlockedRequestCount;
|
||||
total = popupData.globalAllowedRequestCount + blocked;
|
||||
html = [];
|
||||
if ( total === 0 ) {
|
||||
html.push('0');
|
||||
@ -284,27 +290,28 @@ var renderPopup = function(details) {
|
||||
);
|
||||
}
|
||||
|
||||
//if ( stats.dynamicFilteringEnabled ) {
|
||||
// Build dynamic filtering pane only if in use
|
||||
if ( popupData.dfEnabled ) {
|
||||
syncAllDynamicFilters();
|
||||
//}
|
||||
}
|
||||
|
||||
uDom('#total-blocked').html(html.join(''));
|
||||
uDom('#switch .fa').toggleClass('off', stats.pageURL === '' || !stats.netFilteringSwitch);
|
||||
uDom('body').toggleClass('dynamicFilteringEnabled', stats.dynamicFilteringEnabled);
|
||||
uDom('#switch .fa').toggleClass('off', popupData.pageURL === '' || !popupData.netFilteringSwitch);
|
||||
uDom('#panes').toggleClass('dfEnabled', popupData.dfEnabled);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleNetFilteringSwitch = function(ev) {
|
||||
if ( !stats || !stats.pageURL ) {
|
||||
if ( !popupData || !popupData.pageURL ) {
|
||||
return;
|
||||
}
|
||||
messager.send({
|
||||
what: 'toggleNetFiltering',
|
||||
url: stats.pageURL,
|
||||
url: popupData.pageURL,
|
||||
scope: ev.ctrlKey || ev.metaKey ? 'page' : '',
|
||||
state: !uDom(this).toggleClass('off').hasClass('off'),
|
||||
tabId: stats.tabId
|
||||
tabId: popupData.tabId
|
||||
});
|
||||
};
|
||||
|
||||
@ -323,11 +330,11 @@ var gotoDashboard = function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var gotoStats = function() {
|
||||
var gotoDevTools = function() {
|
||||
messager.send({
|
||||
what: 'gotoURL',
|
||||
details: {
|
||||
url: 'dashboard.html?tab=stats&which=' + stats.tabId,
|
||||
url: 'devtools.html?tabId=' + popupData.tabId,
|
||||
select: true,
|
||||
index: -1
|
||||
}
|
||||
@ -339,7 +346,7 @@ var gotoStats = function() {
|
||||
var gotoPick = function() {
|
||||
messager.send({
|
||||
what: 'gotoPick',
|
||||
tabId: stats.tabId
|
||||
tabId: popupData.tabId
|
||||
});
|
||||
window.open('','_self').close();
|
||||
};
|
||||
@ -366,42 +373,42 @@ var gotoLink = function(ev) {
|
||||
/******************************************************************************/
|
||||
|
||||
var toggleDynamicFiltering = function(ev) {
|
||||
var el = uDom('body');
|
||||
el.toggleClass('dynamicFilteringEnabled');
|
||||
var el = uDom('#panes');
|
||||
popupData.dfEnabled = !popupData.dfEnabled;
|
||||
messager.send({
|
||||
what: 'userSettings',
|
||||
name: 'dynamicFilteringEnabled',
|
||||
value: el.hasClass('dynamicFilteringEnabled')
|
||||
});
|
||||
value: popupData.dfEnabled
|
||||
}, renderPopup);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var mouseenterCellHandler = function() {
|
||||
if ( uDom(this).hasClass('ownRule') === false ) {
|
||||
dynaHotspots.appendTo(this);
|
||||
dfHotspots.appendTo(this);
|
||||
}
|
||||
};
|
||||
|
||||
var mouseleaveCellHandler = function() {
|
||||
dynaHotspots.detach();
|
||||
dfHotspots.detach();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var setDynamicFilter = function(src, des, type, action) {
|
||||
// This can happen on pages where uBlock does not work
|
||||
if ( typeof stats.pageHostname !== 'string' || stats.pageHostname === '' ) {
|
||||
if ( typeof popupData.pageHostname !== 'string' || popupData.pageHostname === '' ) {
|
||||
return;
|
||||
}
|
||||
var onDynamicFilterChanged = function(details) {
|
||||
cachePopupData(details);
|
||||
var onDynamicFilterChanged = function(response) {
|
||||
cachePopupData(response);
|
||||
syncAllDynamicFilters();
|
||||
};
|
||||
messager.send({
|
||||
what: 'toggleDynamicFilter',
|
||||
tabId: stats.tabId,
|
||||
pageHostname: stats.pageHostname,
|
||||
tabId: popupData.tabId,
|
||||
pageHostname: popupData.pageHostname,
|
||||
srcHostname: src,
|
||||
desHostname: des,
|
||||
requestType: type,
|
||||
@ -414,12 +421,12 @@ var setDynamicFilter = function(src, des, type, action) {
|
||||
var unsetDynamicFilterHandler = function() {
|
||||
var cell = uDom(this);
|
||||
setDynamicFilter(
|
||||
cell.attr('data-src') === '/' ? '*' : stats.pageHostname,
|
||||
cell.attr('data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
cell.attr('data-des'),
|
||||
cell.attr('data-type'),
|
||||
0
|
||||
);
|
||||
dynaHotspots.appendTo(cell);
|
||||
dfHotspots.appendTo(cell);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -440,30 +447,12 @@ var setDynamicFilterHandler = function() {
|
||||
action = 1;
|
||||
}
|
||||
setDynamicFilter(
|
||||
cell.attr('data-src') === '/' ? '*' : stats.pageHostname,
|
||||
cell.attr('data-src') === '/' ? '*' : popupData.pageHostname,
|
||||
cell.attr('data-des'),
|
||||
cell.attr('data-type'),
|
||||
action
|
||||
);
|
||||
dynaHotspots.detach();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var installEventHandlers = function() {
|
||||
uDom('h1,h2,h3,h4').on('click', gotoDashboard);
|
||||
uDom('#switch .fa').on('click', toggleNetFilteringSwitch);
|
||||
uDom('#gotoLog').on('click', gotoStats);
|
||||
uDom('#gotoPick').on('click', gotoPick);
|
||||
uDom('a[href^=http]').on('click', gotoLink);
|
||||
uDom('#dynamicFilteringToggler').on('click', toggleDynamicFiltering);
|
||||
|
||||
uDom('#dynamicFilteringContainer').on('click', 'span[data-src]', unsetDynamicFilterHandler);
|
||||
uDom('#dynamicFilteringContainer')
|
||||
.on('mouseenter', '[data-src]', mouseenterCellHandler)
|
||||
.on('mouseleave', '[data-src]', mouseleaveCellHandler);
|
||||
dynaHotspots = uDom('#actionSelector');
|
||||
dynaHotspots.on('click', 'span', setDynamicFilterHandler).detach();
|
||||
dfHotspots.detach();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -471,8 +460,18 @@ var installEventHandlers = function() {
|
||||
// Make menu only when popup html is fully loaded
|
||||
|
||||
uDom.onLoad(function() {
|
||||
messager.send({ what: 'activeTabStats' }, renderPopup);
|
||||
installEventHandlers();
|
||||
messager.send({ what: 'activeTabStats' }, function(response) {
|
||||
if ( !cachePopupData(response) ) {
|
||||
return;
|
||||
}
|
||||
renderPopup();
|
||||
});
|
||||
uDom('h1,h2,h3,h4').on('click', gotoDashboard);
|
||||
uDom('#switch .fa').on('click', toggleNetFilteringSwitch);
|
||||
uDom('#gotoLog').on('click', gotoDevTools);
|
||||
uDom('#gotoPick').on('click', gotoPick);
|
||||
uDom('a[href^=http]').on('click', gotoLink);
|
||||
uDom('#dfToggler').on('click', toggleDynamicFiltering);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
255
src/js/stats.js
255
src/js/stats.js
@ -1,255 +0,0 @@
|
||||
/*******************************************************************************
|
||||
|
||||
µBlock - a Chromium 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 uDom */
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
(function() {
|
||||
|
||||
'use strict';
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var messager = vAPI.messaging.channel('stats.js');
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var logSettingChanged = function() {
|
||||
messager.send({
|
||||
what: 'userSettings',
|
||||
name: 'logRequests',
|
||||
value: this.checked
|
||||
});
|
||||
uDom('#requests').toggleClass('logEnabled', this.checked);
|
||||
renderPageSelector();
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var cachedPageSelectors = {};
|
||||
var cachedPageHash = '';
|
||||
|
||||
var toPrettyTypeNames = {
|
||||
'stylesheet': 'css',
|
||||
'sub_frame': 'frame',
|
||||
'object': 'plugin',
|
||||
'xmlhttprequest': 'XHR'
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var chunkify = function(s) {
|
||||
var chunkSize = 50;
|
||||
var chunks = [];
|
||||
while ( s.length ) {
|
||||
chunks.push(s.slice(0, chunkSize));
|
||||
s = s.slice(chunkSize);
|
||||
}
|
||||
return chunks;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderURL = function(url, filter) {
|
||||
var chunkSize = 50;
|
||||
// make a regex out of the filter
|
||||
var reText = filter;
|
||||
var pos = reText.indexOf('$');
|
||||
if ( pos > 0 ) {
|
||||
reText = reText.slice(0, pos);
|
||||
}
|
||||
if ( reText.charAt(0) === 's' ) {
|
||||
reText = reText.slice(3);
|
||||
}
|
||||
if ( reText === '*' ) {
|
||||
reText = '\\*';
|
||||
} else {
|
||||
reText = reText
|
||||
.replace(/\./g, '\\.')
|
||||
.replace(/\?/g, '\\?')
|
||||
.replace('||', '')
|
||||
.replace(/\^/g, '.')
|
||||
.replace(/\*/g, '.*')
|
||||
;
|
||||
}
|
||||
var re = new RegExp(reText, 'gi');
|
||||
var matches = re.exec(url);
|
||||
var renderedURL = chunkify(url);
|
||||
|
||||
if ( matches && matches[0].length ) {
|
||||
var index = (re.lastIndex / chunkSize) | 0;
|
||||
var offset = re.lastIndex % chunkSize;
|
||||
if ( index > 0 && offset === 0 ) {
|
||||
offset = chunkSize;
|
||||
index -= 1;
|
||||
}
|
||||
var segment = renderedURL[index];
|
||||
renderedURL[index] = segment.slice(0, offset) + '</b>' + segment.slice(offset);
|
||||
|
||||
index = (matches.index / chunkSize) | 0;
|
||||
offset = matches.index % chunkSize;
|
||||
if ( index > 0 && offset === 0 ) {
|
||||
offset = chunkSize;
|
||||
index -= 1;
|
||||
}
|
||||
segment = renderedURL[index];
|
||||
renderedURL[index] = segment.slice(0, offset) + '<b>' + segment.slice(offset);
|
||||
}
|
||||
|
||||
return renderedURL.join('\n');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderPageDetails = function(tabId) {
|
||||
if ( !cachedPageSelectors[tabId] ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var onDataReceived = function(details) {
|
||||
if ( details.hash === cachedPageHash ) {
|
||||
return;
|
||||
}
|
||||
cachedPageHash = details.hash;
|
||||
var renderRequests = function(requests, className) {
|
||||
requests.sort(function(a, b) {
|
||||
var r = a.domain.localeCompare(b.domain);
|
||||
if ( r ) { return r; }
|
||||
r = a.reason.localeCompare(b.reason);
|
||||
if ( r ) { return r; }
|
||||
r = a.type.localeCompare(b.type);
|
||||
if ( r ) { return r; }
|
||||
return a.url.localeCompare(b.url);
|
||||
});
|
||||
var html = [], request;
|
||||
html.push(
|
||||
'<tr class="header ', className, '">',
|
||||
'<td colspan="4"><h3>',
|
||||
vAPI.i18n(className + (requests.length ? 'RequestsHeader' : 'RequestsEmpty')),
|
||||
'</h3>'
|
||||
);
|
||||
var currentDomain = '';
|
||||
for ( var i = 0; i < requests.length; i++ ) {
|
||||
request = requests[i];
|
||||
if ( request.domain !== currentDomain ) {
|
||||
currentDomain = request.domain;
|
||||
html.push(
|
||||
'<tr class="', className, ' domainHeader">',
|
||||
'<td colspan="4">', currentDomain
|
||||
);
|
||||
}
|
||||
html.push(
|
||||
'<tr class="', className, request.flags & 0x01 ? ' logMirrored': '', ' requestEntry">',
|
||||
'<td>',
|
||||
'<td>', toPrettyTypeNames[request.type] || request.type,
|
||||
'<td>', renderURL(request.url, request.reason),
|
||||
'<td>', chunkify(request.reason).join('\n')
|
||||
);
|
||||
}
|
||||
return html;
|
||||
};
|
||||
uDom('#requests .tableHeader ~ tr').remove();
|
||||
var htmlBlocked = renderRequests(details.blockedRequests || [], 'logBlocked');
|
||||
var htmlAllowed = renderRequests(details.allowedRequests || [], 'logAllowed');
|
||||
uDom('#requests .tableHeader').insertAfter(htmlBlocked.concat(htmlAllowed).join(''));
|
||||
};
|
||||
|
||||
messager.send({ what: 'getPageDetails', tabId: tabId }, onDataReceived);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var pageSelectorChanged = function() {
|
||||
renderPageDetails(this.value);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var renderPageSelector = function(targetTabId) {
|
||||
if ( !uDom('#logRequests').prop('checked') ) {
|
||||
return;
|
||||
}
|
||||
var selectedTabId = targetTabId || parseInt(uDom('#pageSelector').val(), 10);
|
||||
var onTabReceived = function(tab) {
|
||||
if ( !tab ) {
|
||||
return;
|
||||
}
|
||||
var html = [
|
||||
'<option value="',
|
||||
tab.id,
|
||||
'">',
|
||||
tab.title
|
||||
];
|
||||
uDom('#pageSelector').append(html.join(''));
|
||||
if ( tab.id === selectedTabId ) {
|
||||
uDom('#pageSelector').val(tab.id);
|
||||
}
|
||||
};
|
||||
var onDataReceived = function(pageSelectors) {
|
||||
uDom('#requests').toggleClass('empty', pageSelectors.length === 0);
|
||||
uDom('#pageSelector option').remove();
|
||||
cachedPageSelectors = {};
|
||||
pageSelectors.sort().map(function(tabId) {
|
||||
cachedPageSelectors[tabId] = true;
|
||||
});
|
||||
if ( !cachedPageSelectors[selectedTabId] ) {
|
||||
selectedTabId = pageSelectors[0];
|
||||
}
|
||||
for ( var i = 0; i < pageSelectors.length; i++ ) {
|
||||
messager.send({
|
||||
what: 'getTabForStats',
|
||||
tabId: parseInt(pageSelectors[i], 10)
|
||||
}, onTabReceived);
|
||||
}
|
||||
if ( pageSelectors.length > 0 ) {
|
||||
renderPageDetails(selectedTabId);
|
||||
}
|
||||
};
|
||||
messager.send({ what: 'getPageSelectors' }, onDataReceived);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
var onUserSettingsReceived = function(details) {
|
||||
uDom('#logRequests').prop('checked', details.logRequests);
|
||||
uDom('#requests').toggleClass('logEnabled', details.logRequests);
|
||||
|
||||
var matches = window.location.search.slice(1).match(/(?:^|&)which=(\d+)/);
|
||||
var tabId = matches && matches.length === 2 ? parseInt(matches[1], 10) : 0;
|
||||
renderPageSelector(tabId);
|
||||
|
||||
uDom('#logRequests').on('change', logSettingChanged);
|
||||
uDom('#refresh').on('click', function() { renderPageSelector(); });
|
||||
uDom('#pageSelector').on('change', pageSelectorChanged);
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
uDom.onLoad(function() {
|
||||
messager.send({ what: 'userSettings' }, onUserSettingsReceived);
|
||||
});
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
||||
|
@ -661,6 +661,10 @@
|
||||
µb.mirrors.toggle(settings.experimentalEnabled);
|
||||
µb.contextMenu.toggle(settings.contextMenuEnabled);
|
||||
|
||||
// Remove obsolete setting
|
||||
delete µb.userSettings.logRequests;
|
||||
µb.XAL.keyvalRemoveOne('logRequests');
|
||||
|
||||
if ( typeof settings.dynamicFilteringSelfie === 'string' ) {
|
||||
if ( settings.dynamicFilteringString === '' && settings.dynamicFilteringSelfie !== '' ) {
|
||||
µb.dynamicNetFilteringEngine.fromObsoleteSelfie(settings.dynamicFilteringSelfie);
|
||||
|
@ -83,7 +83,12 @@ vAPI.tabs.onPopup = function(details) {
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/91
|
||||
if ( result !== '' ) {
|
||||
pageStore.recordResult('popup', requestURL, result);
|
||||
var context = {
|
||||
requestURL: requestURL,
|
||||
requestHostname: µb.URI.hostnameFromURI(requestURL),
|
||||
requestType: 'popup'
|
||||
};
|
||||
pageStore.logBuffer.writeOne(context, result);
|
||||
}
|
||||
|
||||
// Not blocked
|
||||
|
@ -95,6 +95,9 @@ var onBeforeRequest = function(details) {
|
||||
|
||||
var result = pageStore.filterRequest(requestContext);
|
||||
|
||||
// Log result
|
||||
pageStore.logBuffer.writeOne(requestContext, result);
|
||||
|
||||
// Not blocked
|
||||
if ( pageStore.boolFromResult(result) === false ) {
|
||||
//console.debug('onBeforeRequest()> ALLOW "%s" (%o) because "%s"', details.url, details, result);
|
||||
@ -125,7 +128,6 @@ var onBeforeRequest = function(details) {
|
||||
// Not all redirects will succeed, until bug above is fixed.
|
||||
var redirectURL = µb.mirrors.toURL(requestURL, true);
|
||||
if ( redirectURL !== '' ) {
|
||||
pageStore.setRequestFlags(requestURL, 0x01, 0x01);
|
||||
//console.debug('"%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50));
|
||||
return { redirectUrl: redirectURL };
|
||||
}
|
||||
|
@ -321,75 +321,7 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
this.XAL.keyvalSetOne('dynamicFilteringString', this.userSettings.dynamicFilteringString);
|
||||
|
||||
// https://github.com/gorhill/uBlock/issues/420
|
||||
if ( details.requestType === '3p-frame' && !details.block ) {
|
||||
this.cosmeticFilteringEngine.removeFromSelectorCache(details.hostname, 'net');
|
||||
}
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
µBlock.isFirstParty = function(firstPartyDomain, hostname) {
|
||||
if ( hostname.slice(0 - firstPartyDomain.length) !== firstPartyDomain ) {
|
||||
return false;
|
||||
}
|
||||
// Be sure to not confuse 'example.com' with 'anotherexample.com'
|
||||
var c = hostname.charAt(hostname.length - firstPartyDomain.length - 1);
|
||||
return c === '.' || c === '';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
// The core logic to evaluate requests through dynamic/static filtering
|
||||
// is here.
|
||||
|
||||
µBlock.filterRequest = function(context) {
|
||||
// Given that:
|
||||
// - Dynamic filtering override static filtering
|
||||
// - Evaluating dynamic filtering is much faster than static filtering
|
||||
// We evaluate dynamic filtering first, and hopefully we can skip
|
||||
// evaluation of static filtering.
|
||||
// Dynamic filtering evaluation is ordered from most-specific to least-
|
||||
// specific.
|
||||
var df = this.dynamicNetFilteringEngine.clearRegisters();
|
||||
|
||||
var rootHostname = context.rootHostname;
|
||||
var requestHostname = context.requestHostname;
|
||||
var requestType = context.requestType;
|
||||
|
||||
// Dynamic filters:
|
||||
// 1. specific source, specific destination, any type, allow/block
|
||||
// 2. any source, specific destination, any type, allow/block
|
||||
df.evaluateCellZY(rootHostname, requestHostname, '*');
|
||||
if ( df.mustBlockOrAllow() ) {
|
||||
return df.toFilterString();
|
||||
}
|
||||
|
||||
// Dynamic filters:
|
||||
// 3. specific source, any destination, specific type, allow/block
|
||||
// 4. any source, any destination, specific type, allow/block
|
||||
if ( df.mustAbort() === false ) {
|
||||
if ( requestType === 'script' ) {
|
||||
df.evaluateCellZY(rootHostname, requestHostname, this.isFirstParty(rootHostname, requestHostname) ? '1p-script' : '3p-script');
|
||||
if ( df.mustBlockOrAllow() ) {
|
||||
return df.toFilterString();
|
||||
}
|
||||
} else if ( requestType === 'sub_frame' ) {
|
||||
if ( this.isFirstParty(rootHostname, requestHostname) === false ) {
|
||||
df.evaluateCellZY(rootHostname, requestHostname, '3p-frame');
|
||||
if ( df.mustBlockOrAllow() ) {
|
||||
return df.toFilterString();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
df.evaluateCellZY(rootHostname, requestHostname, requestType);
|
||||
if ( df.mustBlockOrAllow() ) {
|
||||
return df.toFilterString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Static filtering never override dynamic filtering
|
||||
return this.staticNetFilteringEngine.matchString(context);
|
||||
this.cosmeticFilteringEngine.removeFromSelectorCache(details.srcHostname, 'net');
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
@ -403,6 +335,7 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
||||
µBlock.isAllowResult = function(result) {
|
||||
return typeof result !== 'string' || result.charAt(1) !== 'b';
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
})();
|
@ -9,33 +9,36 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h4 title="popupTipDashboard">v<span id="version"></span></h4>
|
||||
<div>
|
||||
<div id="dynamicFilteringContainer">
|
||||
<div><span data-i18n="popupImageRulePrompt"></span><span data-src="/" data-des="*" data-type="image"> </span><span data-src="." data-des="*" data-type="image"></span></div>
|
||||
<div><span data-i18n="popupInlineScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="inline-script"> </span><span data-src="." data-des="*" data-type="inline-script"> </span></div>
|
||||
<div><span data-i18n="popup1pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="1p-script"> </span><span data-src="." data-des="*" data-type="1p-script"> </span></div>
|
||||
<div><span data-i18n="popup3pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-script"> </span><span data-src="." data-des="*" data-type="3p-script"> </span></div>
|
||||
<div><span data-i18n="popup3pFrameRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-frame"> </span><span data-src="." data-des="*" data-type="3p-frame"> </span></div>
|
||||
<div id="privacyInfo"></div>
|
||||
<h4 title="popupTipDashboard"><span id="appname"></span><span id="version"></span></h4>
|
||||
<div id="panes">
|
||||
<div>
|
||||
<p id="switch" data-i18n-tip="popupPowerSwitchInfo"><span class="fa"></span></p>
|
||||
<p id="switch-hint"></p>
|
||||
<p id="dfToggler" data-i18n="popupBlockedRequestPrompt"></p>
|
||||
<p class="stats">
|
||||
<span data-i18n="popupBlockedOnThisPagePrompt"></span> 
|
||||
<span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-anchor="top"></span> 
|
||||
<span id="gotoLog" class="fa tool" data-i18n-tip="popupTipLog" data-tip-anchor="top"></span>
|
||||
</p>
|
||||
<p id="page-blocked">?</p>
|
||||
<p class="stats" data-i18n="popupBlockedSinceInstallPrompt"></p>
|
||||
<p id="total-blocked">?</p>
|
||||
</div><!-- DO NOT REMOVE --><div>
|
||||
<div id="dynamicFilteringContainer">
|
||||
<div><span data-i18n="popupImageRulePrompt"></span><span data-src="/" data-des="*" data-type="image"> </span><span data-src="." data-des="*" data-type="image"></span></div>
|
||||
<div><span data-i18n="popupInlineScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="inline-script"> </span><span data-src="." data-des="*" data-type="inline-script"> </span></div>
|
||||
<div><span data-i18n="popup1pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="1p-script"> </span><span data-src="." data-des="*" data-type="1p-script"> </span></div>
|
||||
<div><span data-i18n="popup3pScriptRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-script"> </span><span data-src="." data-des="*" data-type="3p-script"> </span></div>
|
||||
<div><span data-i18n="popup3pFrameRulePrompt"></span><span data-src="/" data-des="*" data-type="3p-frame"> </span><span data-src="." data-des="*" data-type="3p-frame"> </span></div>
|
||||
<div id="privacyInfo"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- DO NOT REMOVE --><div>
|
||||
<p id="switch" data-i18n-tip="popupPowerSwitchInfo"><span class="fa"></span></p>
|
||||
<p id="switch-hint"></p>
|
||||
<p id="dynamicFilteringToggler" data-i18n="popupBlockedRequestPrompt"></p>
|
||||
<p class="stats">
|
||||
<span data-i18n="popupBlockedOnThisPagePrompt"></span> 
|
||||
<span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-anchor="top"></span> 
|
||||
<span id="gotoLog" class="fa tool" data-i18n-tip="popupTipLog" data-tip-anchor="top"></span>
|
||||
</p>
|
||||
<p id="page-blocked">?</p>
|
||||
<p class="stats" data-i18n="popupBlockedSinceInstallPrompt"></p>
|
||||
<p id="total-blocked">?</p>
|
||||
</div>
|
||||
|
||||
<div id="templates" style="display: none">
|
||||
<div><span></span><span data-src="/" data-des="" data-type="*"> </span><span data-src="." data-des="" data-type="*"> </span></div>
|
||||
<div id='actionSelector'><span id="dynaAllow"></span><span id="dynaNoop"></span><span id="dynaBlock"></span></div>
|
||||
<div id=hotspotTip></div>
|
||||
</div>
|
||||
|
||||
<script src="js/vapi-common.js"></script>
|
||||
|
@ -1,40 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>µBlock — Statistics</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/stats.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<ul>
|
||||
<li><input id="logRequests" type="checkbox" data-range="bool" /><label data-i18n="logNetRequestsPrompt" for="logRequests"></label>
|
||||
<button class="whatisthis"></button>
|
||||
<div class="whatisthis-expandable para" data-i18n="logNetRequestsHelp"></div>
|
||||
</ul>
|
||||
|
||||
<div id="requests">
|
||||
<span id="refresh" class="fa"></span><select id="pageSelector"></select>
|
||||
|
||||
<table>
|
||||
<tr class="tableHeader">
|
||||
<th>
|
||||
<th data-i18n="logRequestsHeaderType">
|
||||
<th data-i18n="logRequestsHeaderURL">
|
||||
<th data-i18n="logRequestsHeaderFilter">
|
||||
</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/dashboard-common.js"></script>
|
||||
<script src="js/stats.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user