aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorgorhill <rhill@raymondhill.net>2015-01-06 08:01:15 -0500
committergorhill <rhill@raymondhill.net>2015-01-06 08:01:15 -0500
commit1597ce7fd91880b92f25b361167b3455d891a29a (patch)
tree63d907bb0673a1b2e3e3cb7f859b3f0781f16cb6
parent81e035589b3be6f03fa87dd47f5cd2b6ed0d9bf1 (diff)
downloaduBlock-1597ce7fd91880b92f25b361167b3455d891a29a.zip
uBlock-1597ce7fd91880b92f25b361167b3455d891a29a.tar.gz
uBlock-1597ce7fd91880b92f25b361167b3455d891a29a.tar.bz2
lot of work related to dynamic filtering + new net requests logger
-rw-r--r--platform/chromium/vapi-background.js9
-rw-r--r--src/css/devtool-log.css68
-rw-r--r--src/css/devtools.css49
-rw-r--r--src/css/popup.css105
-rw-r--r--src/css/stats.css86
-rw-r--r--src/dashboard.html1
-rw-r--r--src/devtool-log.html20
-rw-r--r--src/devtools.html25
-rw-r--r--src/js/background.js1
-rw-r--r--src/js/cosmetic-filtering.js10
-rw-r--r--src/js/devtool-log.js178
-rw-r--r--src/js/devtools.js98
-rw-r--r--src/js/dynamic-net-filtering.js53
-rw-r--r--src/js/messaging.js125
-rw-r--r--src/js/pagestore.js294
-rw-r--r--src/js/popup.js159
-rw-r--r--src/js/stats.js255
-rw-r--r--src/js/storage.js4
-rw-r--r--src/js/tab.js7
-rw-r--r--src/js/traffic.js4
-rw-r--r--src/js/ublock.js71
-rw-r--r--src/popup.html45
-rw-r--r--src/stats.html40
23 files changed, 990 insertions, 717 deletions
diff --git a/platform/chromium/vapi-background.js b/platform/chromium/vapi-background.js
index 6d4ff02..76b25bf 100644
--- a/platform/chromium/vapi-background.js
+++ b/platform/chromium/vapi-background.js
@@ -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
diff --git a/src/css/devtool-log.css b/src/css/devtool-log.css
new file mode 100644
index 0000000..d4a21e0
--- /dev/null
+++ b/src/css/devtool-log.css
@@ -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;
+ } \ No newline at end of file
diff --git a/src/css/devtools.css b/src/css/devtools.css
new file mode 100644
index 0000000..f3022e7
--- /dev/null
+++ b/src/css/devtools.css
@@ -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%;
+ }
diff --git a/src/css/popup.css b/src/css/popup.css
index 1dc2251..cb0ac5e 100644
--- a/src/css/popup.css
+++ b/src/css/popup.css
@@ -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[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 */
}
-body.dynamicFilteringEnabled > div:nth-of-type(1) {
+#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;
+ } \ No newline at end of file
diff --git a/src/css/stats.css b/src/css/stats.css
deleted file mode 100644
index 23ae170..0000000
--- a/src/css/stats.css
+++ /dev/null
@@ -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;
- } \ No newline at end of file
diff --git a/src/dashboard.html b/src/dashboard.html
index 9a172dd..4c36f62 100644
--- a/src/dashboard.html
+++ b/src/dashboard.html
@@ -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>
diff --git a/src/devtool-log.html b/src/devtool-log.html
new file mode 100644
index 0000000..c8ff572
--- /dev/null
+++ b/src/devtool-log.html
@@ -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">&#xf021;</span>
+ <span id="clear" class="button fa">&#xf12d;</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>
diff --git a/src/devtools.html b/src/devtools.html
new file mode 100644
index 0000000..09721d1
--- /dev/null
+++ b/src/devtools.html
@@ -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">&#xf021;</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>
diff --git a/src/js/background.js b/src/js/background.js
index c181616..4a98ab1 100644
--- a/src/js/background.js
+++ b/src/js/background.js
@@ -60,7 +60,6 @@ return {
dynamicFilteringEnabled: false,
experimentalEnabled: false,
externalLists: defaultExternalLists,
- logRequests: false,
parseAllABPHideFilters: true,
showIconBadge: true
},
diff --git a/src/js/cosmetic-filtering.js b/src/js/cosmetic-filtering.js
index 0a95b0f..e8452f2 100644
--- a/src/js/cosmetic-filtering.js
+++ b/src/js/cosmetic-filtering.js
@@ -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);
}
diff --git a/src/js/devtool-log.js b/src/js/devtool-log.js
new file mode 100644
index 0000000..38bd8a3
--- /dev/null
+++ b/src/js/devtool-log.js
@@ -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);
+});
+
+/******************************************************************************/
+
+})();
diff --git a/src/js/devtools.js b/src/js/devtools.js
new file mode 100644
index 0000000..809eae9
--- /dev/null
+++ b/src/js/devtools.js
@@ -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(); } );
+});
+
+/******************************************************************************/
+
+})();
+
diff --git a/src/js/dynamic-net-filtering.js b/src/js/dynamic-net-filtering.js
index 4f31696..635b91e 100644
--- a/src/js/dynamic-net-filtering.js
+++ b/src/js/dynamic-net-filtering.js
@@ -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();
/******************************************************************************/
diff --git a/src/js/messaging.js b/src/js/messaging.js
index 53c1ab9..aa95a7e 100644
--- a/src/js/messaging.js
+++ b/src/js/messaging.js
@@ -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
/******************************************************************************/
diff --git a/src/js/pagestore.js b/src/js/pagestore.js
index ad70f42..ec887cd 100644
--- a/src/js/pagestore.js
+++ b/src/js/pagestore.js
@@ -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 = '';
+
+ // 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();
+ }
+
+ // 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('cache MISS: PageStore.filterRequest("%s")', requestURL);
- this.recordResult(context.requestType, requestURL, result);
+ // console.debug('[%s, %s] = "%s"', context.requestHostname, context.requestType, result);
- // TODO: send this to a dev-panel tool
- //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 {
diff --git a/src/js/popup.js b/src/js/popup.js
index 91dab68..0d706bb 100644
--- a/src/js/popup.js
+++ b/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 hdr = uDom('#version');
- hdr.nodes[0].previousSibling.textContent = details.appName;
- hdr.html(hdr.html() + 'v' + details.appVersion);
+var renderPopup = function() {
+ uDom('#appname').text(popupData.appName);
+ uDom('#version').text(popupData.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,39 +447,31 @@ 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();
+ dfHotspots.detach();
};
/******************************************************************************/
-var installEventHandlers = function() {
+// Make menu only when popup html is fully loaded
+
+uDom.onLoad(function() {
+ 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', gotoStats);
+ uDom('#gotoLog').on('click', gotoDevTools);
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();
-};
-
-/******************************************************************************/
-
-// Make menu only when popup html is fully loaded
-
-uDom.onLoad(function() {
- messager.send({ what: 'activeTabStats' }, renderPopup);
- installEventHandlers();
+ uDom('#dfToggler').on('click', toggleDynamicFiltering);
});
/******************************************************************************/
diff --git a/src/js/stats.js b/src/js/stats.js
deleted file mode 100644
index df5a924..0000000
--- a/src/js/stats.js
+++ /dev/null
@@ -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);
-});
-
-/******************************************************************************/
-
-})();
-
diff --git a/src/js/storage.js b/src/js/storage.js
index d90a1d1..044ec86 100644
--- a/src/js/storage.js
+++ b/src/js/storage.js
@@ -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);
diff --git a/src/js/tab.js b/src/js/tab.js
index 9584a1e..fd035f2 100644
--- a/src/js/tab.js
+++ b/src/js/tab.js
@@ -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
diff --git a/src/js/traffic.js b/src/js/traffic.js
index 7b50dcf..785becd 100644
--- a/src/js/traffic.js
+++ b/src/js/traffic.js
@@ -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 };
}
diff --git a/src/js/ublock.js b/src/js/ublock.js
index 26ab7f8..a0be4c0 100644
--- a/src/js/ublock.js
+++ b/src/js/ublock.js
@@ -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';
};
+
/******************************************************************************/
})(); \ No newline at end of file
diff --git a/src/popup.html b/src/popup.html
index 1fb6725..312f2b2 100644
--- a/src/popup.html
+++ b/src/popup.html
@@ -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">&#xf011;</span></p>
+ <p id="switch-hint"></p>
+ <p id="dfToggler" data-i18n="popupBlockedRequestPrompt"></p>
+ <p class="stats">
+ <span data-i18n="popupBlockedOnThisPagePrompt"></span>&ensp;
+ <span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-anchor="top">&#xf1fb;</span>&ensp;
+ <span id="gotoLog" class="fa tool" data-i18n-tip="popupTipLog" data-tip-anchor="top">&#xf06e;</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">&#xf011;</span></p>
- <p id="switch-hint"></p>
- <p id="dynamicFilteringToggler" data-i18n="popupBlockedRequestPrompt"></p>
- <p class="stats">
- <span data-i18n="popupBlockedOnThisPagePrompt"></span>&ensp;
- <span id="gotoPick" class="fa tool" data-i18n-tip="popupTipPicker" data-tip-anchor="top">&#xf1fb;</span>&ensp;
- <span id="gotoLog" class="fa tool" data-i18n-tip="popupTipLog" data-tip-anchor="top">&#xf06e;</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>
diff --git a/src/stats.html b/src/stats.html
deleted file mode 100644
index ab3187e..0000000
--- a/src/stats.html
+++ /dev/null
@@ -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">&#xf021;</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>