mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-16 23:42:39 +01:00
Merge branch 'master' of https://github.com/chrisaljoudi/uBlock into chrisaljoudi
This commit is contained in:
commit
eeb792cc8b
@ -79,7 +79,7 @@ vAPI.tabs = {};
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.isNoTabId = function(tabId) {
|
vAPI.isBehindTheSceneTabId = function(tabId) {
|
||||||
return tabId.toString() === '-1';
|
return tabId.toString() === '-1';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,8 +102,8 @@ vAPI.tabs.registerListeners = function() {
|
|||||||
var popupCandidates = Object.create(null);
|
var popupCandidates = Object.create(null);
|
||||||
|
|
||||||
var PopupCandidate = function(details) {
|
var PopupCandidate = function(details) {
|
||||||
this.targetTabId = details.tabId;
|
this.targetTabId = details.tabId.toString();
|
||||||
this.openerTabId = details.sourceTabId;
|
this.openerTabId = details.sourceTabId.toString();
|
||||||
this.targetURL = details.url;
|
this.targetURL = details.url;
|
||||||
this.selfDestructionTimer = null;
|
this.selfDestructionTimer = null;
|
||||||
};
|
};
|
||||||
@ -222,7 +222,12 @@ vAPI.tabs.get = function(tabId, callback) {
|
|||||||
if ( typeof tabId === 'string' ) {
|
if ( typeof tabId === 'string' ) {
|
||||||
tabId = parseInt(tabId, 10);
|
tabId = parseInt(tabId, 10);
|
||||||
}
|
}
|
||||||
chrome.tabs.get(tabId, onTabReady);
|
if ( typeof tabId !== 'number' || isNaN(tabId) ) {
|
||||||
|
onTabReady(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
chrome.tabs.get(tabId, onTabReady);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var onTabReceived = function(tabs) {
|
var onTabReceived = function(tabs) {
|
||||||
@ -719,7 +724,8 @@ vAPI.onLoadAllCompleted = function() {
|
|||||||
var i = tabs.length, tab;
|
var i = tabs.length, tab;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
tab = tabs[i];
|
tab = tabs[i];
|
||||||
µb.bindTabToPageStats(tab.id, tab.url);
|
µb.tabContextManager.commit(tab.id, tab.url);
|
||||||
|
µb.bindTabToPageStats(tab.id);
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/129
|
// https://github.com/chrisaljoudi/uBlock/issues/129
|
||||||
scriptStart(tab.id);
|
scriptStart(tab.id);
|
||||||
}
|
}
|
||||||
|
@ -333,7 +333,7 @@ var tabWatcher = {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.isNoTabId = function(tabId) {
|
vAPI.isBehindTheSceneTabId = function(tabId) {
|
||||||
return tabId.toString() === '-1';
|
return tabId.toString() === '-1';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1983,6 +1983,7 @@ vAPI.onLoadAllCompleted = function() {
|
|||||||
|
|
||||||
var tabId = this.tabs.getTabId(tab);
|
var tabId = this.tabs.getTabId(tab);
|
||||||
var browser = getBrowserForTab(tab);
|
var browser = getBrowserForTab(tab);
|
||||||
|
µb.tabContextManager.commit(tabId, browser.currentURI.asciiSpec);
|
||||||
µb.bindTabToPageStats(tabId, browser.currentURI.asciiSpec);
|
µb.bindTabToPageStats(tabId, browser.currentURI.asciiSpec);
|
||||||
browser.messageManager.sendAsyncMessage(
|
browser.messageManager.sendAsyncMessage(
|
||||||
location.host + '-load-completed'
|
location.host + '-load-completed'
|
||||||
|
@ -239,7 +239,7 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.isNoTabId = function(tabId) {
|
vAPI.isBehindTheSceneTabId = function(tabId) {
|
||||||
return tabId.toString() === this.noTabId;
|
return tabId.toString() === this.noTabId;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -256,13 +256,13 @@
|
|||||||
tabId = vAPI.tabs.getTabId(e.target);
|
tabId = vAPI.tabs.getTabId(e.target);
|
||||||
var details = {
|
var details = {
|
||||||
targetURL: url,
|
targetURL: url,
|
||||||
targetTabId: tabId,
|
targetTabId: tabId.toString(),
|
||||||
openerTabId: vAPI.tabs.popupCandidate
|
openerTabId: vAPI.tabs.popupCandidate
|
||||||
};
|
};
|
||||||
|
vAPI.tabs.popupCandidate = false;
|
||||||
if(vAPI.tabs.onPopup(details)) {
|
if(vAPI.tabs.onPopup(details)) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if(vAPI.tabs.stack[details.openerTabId]) {
|
if(vAPI.tabs.stack[details.openerTabId]) {
|
||||||
vAPI.tabs.popupCandidate = false;
|
|
||||||
vAPI.tabs.stack[details.openerTabId].activate();
|
vAPI.tabs.stack[details.openerTabId].activate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -731,17 +731,18 @@
|
|||||||
}
|
}
|
||||||
switch(e.message.type) {
|
switch(e.message.type) {
|
||||||
case "popup":
|
case "popup":
|
||||||
vAPI.tabs.popupCandidate = vAPI.tabs.getTabId(e.target);
|
var openerTabId = vAPI.tabs.getTabId(e.target).toString();
|
||||||
if(e.message.url === "about:blank") {
|
var shouldBlock = !!vAPI.tabs.onPopup({
|
||||||
|
targetURL: e.message.url,
|
||||||
|
targetTabId: "preempt",
|
||||||
|
openerTabId: openerTabId
|
||||||
|
});
|
||||||
|
if(shouldBlock) {
|
||||||
e.message = false;
|
e.message = false;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
e.message = !vAPI.tabs.onPopup({
|
vAPI.tabs.popupCandidate = openerTabId;
|
||||||
targetURL: e.message.url,
|
e.message = true;
|
||||||
targetTabId: 0,
|
|
||||||
openerTabId: vAPI.tabs.getTabId(e.target)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "popstate":
|
case "popstate":
|
||||||
|
@ -260,7 +260,7 @@ x.setAttribute('src', block(val, 'image') ? 'data:image/gif;base64,R0lGODlhAQABA
|
|||||||
return x;\
|
return x;\
|
||||||
};\
|
};\
|
||||||
open = function(u) {\
|
open = function(u) {\
|
||||||
return block(u, 'popup') ? null : wo.apply(this, arguments);\
|
if(block(u, 'popup')) return {}; else return wo.apply(this, arguments);\
|
||||||
};\
|
};\
|
||||||
XMLHttpRequest.prototype.open = function(m, u) {\
|
XMLHttpRequest.prototype.open = function(m, u) {\
|
||||||
if(block(u, 'xmlhttprequest')) {throw 'InvalidAccessError'; return;}\
|
if(block(u, 'xmlhttprequest')) {throw 'InvalidAccessError'; return;}\
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
<script src="js/vapi-common.js"></script>
|
<script src="js/vapi-common.js"></script>
|
||||||
<script src="js/vapi-background.js"></script>
|
<script src="js/vapi-background.js"></script>
|
||||||
<script src="js/background.js"></script>
|
<script src="js/background.js"></script>
|
||||||
<script src="js/xal.js"></script>
|
|
||||||
<script src="js/async.js"></script>
|
<script src="js/async.js"></script>
|
||||||
<script src="js/utils.js"></script>
|
<script src="js/utils.js"></script>
|
||||||
<script src="js/uritools.js"></script>
|
<script src="js/uritools.js"></script>
|
||||||
@ -19,7 +18,6 @@
|
|||||||
<script src="js/dynamic-net-filtering.js"></script>
|
<script src="js/dynamic-net-filtering.js"></script>
|
||||||
<script src="js/static-net-filtering.js"></script>
|
<script src="js/static-net-filtering.js"></script>
|
||||||
<script src="js/cosmetic-filtering.js"></script>
|
<script src="js/cosmetic-filtering.js"></script>
|
||||||
<script src="js/hnswitches.js"></script>
|
|
||||||
<script src="js/ublock.js"></script>
|
<script src="js/ublock.js"></script>
|
||||||
<script src="js/messaging.js"></script>
|
<script src="js/messaging.js"></script>
|
||||||
<script src="js/profiler.js"></script>
|
<script src="js/profiler.js"></script>
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title></title>
|
|
||||||
<link rel="stylesheet" href="css/common.css" type="text/css">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: large;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
body > div {
|
|
||||||
margin: 1.5em 0;
|
|
||||||
}
|
|
||||||
body > div > p {
|
|
||||||
margin: 4px 0;
|
|
||||||
}
|
|
||||||
body > div > p:first-child {
|
|
||||||
margin: 1.5em 0 0 0;
|
|
||||||
}
|
|
||||||
.code {
|
|
||||||
background-color: rgba(0, 0, 0, 0.1);
|
|
||||||
display: inline-block;
|
|
||||||
font-family: monospace;
|
|
||||||
padding: 2px 4px;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
cursor: pointer;
|
|
||||||
margin: 0 1em 0.25em 1em;
|
|
||||||
padding: 0.25em 0.5em;
|
|
||||||
font-size: inherit;
|
|
||||||
}
|
|
||||||
#warningSign {
|
|
||||||
margin: 1e, 0;
|
|
||||||
opacity: 1;
|
|
||||||
pointer-events: none;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
#warningSign > span {
|
|
||||||
color: #f2a500;
|
|
||||||
font-size: 180px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<!-- http://commons.wikimedia.org/wiki/File:Caution_sign_used_on_roads_pn.svg
|
|
||||||
Public domain. I removed the shadow.
|
|
||||||
--><div id="warningSign"><span class="fa"></span></div>
|
|
||||||
<div>
|
|
||||||
<p data-i18n="docblockedPrompt1"></p>
|
|
||||||
<p class="what code"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p data-i18n="docblockedPrompt2"></p>
|
|
||||||
<p id="why" class="code"></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p><button id="back" data-i18n="docblockedBack" type="button"></button>
|
|
||||||
<button id="bye" data-i18n="docblockedClose" type="button"></button></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<p id="proceed"></p>
|
|
||||||
<p><button id="proceedTemporary" data-i18n="docblockedDisableTemporary" type="button"></button>
|
|
||||||
<button id="proceedPermanent" data-i18n="docblockedDisablePermanent" type="button"></button></p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style="display: none;">
|
|
||||||
<span id="proceedTemplate"><span></span><span class="code"></span><span></span></span>
|
|
||||||
</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/document-blocked.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -84,7 +84,7 @@ AsyncJobManager.prototype.restartTimer = function() {
|
|||||||
// TODO: Maybe use chrome.alarms() API when the next job is at more than
|
// TODO: Maybe use chrome.alarms() API when the next job is at more than
|
||||||
// one minute in the future... From reading about it, chrome.alarms() is
|
// one minute in the future... From reading about it, chrome.alarms() is
|
||||||
// smarter in that it will fire the event only when the browser is not
|
// smarter in that it will fire the event only when the browser is not
|
||||||
// too busy. (through XAL to abstract API specificities)
|
// too busy.
|
||||||
if ( when < this.timerWhen ) {
|
if ( when < this.timerWhen ) {
|
||||||
clearTimeout(this.timerId);
|
clearTimeout(this.timerId);
|
||||||
this.timerWhen = when;
|
this.timerWhen = when;
|
||||||
@ -187,7 +187,7 @@ return asyncJobManager;
|
|||||||
};
|
};
|
||||||
|
|
||||||
var updateBadgeAsync = function(tabId) {
|
var updateBadgeAsync = function(tabId) {
|
||||||
if ( vAPI.isNoTabId(tabId) ) {
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
µb.asyncJobs.add('updateBadge-' + tabId, tabId, updateBadge, 250);
|
µb.asyncJobs.add('updateBadge-' + tabId, tabId, updateBadge, 250);
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
uBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2015 Raymond Hill
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/chrisaljoudi/uBlock
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global uDom */
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var messager = vAPI.messaging.channel('document-blocked.js');
|
|
||||||
var details = {};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var matches = /details=([^&]+)/.exec(window.location.search);
|
|
||||||
if ( matches === null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
details = JSON.parse(atob(matches[1]));
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var proceedToURL = function() {
|
|
||||||
window.location.replace(details.url);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var proceedTemporary = function() {
|
|
||||||
messager.send({
|
|
||||||
what: 'temporarilyWhitelistDocument',
|
|
||||||
url: details.url
|
|
||||||
}, proceedToURL);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var proceedPermanent = function() {
|
|
||||||
messager.send({
|
|
||||||
what: 'toggleHostnameSwitch',
|
|
||||||
name: 'dontBlockDoc',
|
|
||||||
hostname: details.hn,
|
|
||||||
state: true
|
|
||||||
}, proceedToURL);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var matches = /^(.*)\{\{hostname\}\}(.*)$/.exec(vAPI.i18n('docblockedProceed'));
|
|
||||||
if ( matches === null ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var proceed = uDom('#proceedTemplate').clone();
|
|
||||||
proceed.descendants('span:nth-of-type(1)').text(matches[1]);
|
|
||||||
proceed.descendants('span:nth-of-type(2)').text(details.hn);
|
|
||||||
proceed.descendants('span:nth-of-type(3)').text(matches[2]);
|
|
||||||
uDom('#proceed').append(proceed);
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
uDom('.what').text(details.url);
|
|
||||||
uDom('#why').text(details.why.slice(3));
|
|
||||||
|
|
||||||
if ( window.history.length > 1 ) {
|
|
||||||
uDom('#back').on('click', function() { window.history.back(); });
|
|
||||||
uDom('#bye').css('display', 'none');
|
|
||||||
} else {
|
|
||||||
uDom('#bye').on('click', function() { window.close(); });
|
|
||||||
uDom('#back').css('display', 'none');
|
|
||||||
}
|
|
||||||
|
|
||||||
uDom('#proceedTemporary').attr('href', details.url).on('click', proceedTemporary);
|
|
||||||
uDom('#proceedPermanent').attr('href', details.url).on('click', proceedPermanent);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
@ -1,283 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
uBlock - a Chromium browser extension to black/white list requests.
|
|
||||||
Copyright (C) 2015 Raymond Hill
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/chrisaljoudi/uBlock
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global punycode, µBlock */
|
|
||||||
/* jshint bitwise: false */
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.HnSwitches = (function() {
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var HnSwitches = function() {
|
|
||||||
this.reset();
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var switchBitOffsets = {
|
|
||||||
'dontBlockDoc': 0,
|
|
||||||
'doBlockAllPopups': 2
|
|
||||||
};
|
|
||||||
|
|
||||||
var switchStateToNameMap = {
|
|
||||||
'1': 'true',
|
|
||||||
'2': 'false'
|
|
||||||
};
|
|
||||||
|
|
||||||
var nameToSwitchStateMap = {
|
|
||||||
'true': 1,
|
|
||||||
'false': 2
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// For performance purpose, as simple tests as possible
|
|
||||||
var reHostnameVeryCoarse = /[g-z_-]/;
|
|
||||||
var reIPv4VeryCoarse = /\.\d+$/;
|
|
||||||
|
|
||||||
// http://tools.ietf.org/html/rfc5952
|
|
||||||
// 4.3: "MUST be represented in lowercase"
|
|
||||||
// Also: http://en.wikipedia.org/wiki/IPv6_address#Literal_IPv6_addresses_in_network_resource_identifiers
|
|
||||||
|
|
||||||
var isIPAddress = function(hostname) {
|
|
||||||
if ( reHostnameVeryCoarse.test(hostname) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( reIPv4VeryCoarse.test(hostname) ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return hostname.charAt(0) === '[';
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var toBroaderHostname = function(hostname) {
|
|
||||||
if ( hostname === '*' ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
if ( isIPAddress(hostname) ) {
|
|
||||||
return '*';
|
|
||||||
}
|
|
||||||
var pos = hostname.indexOf('.');
|
|
||||||
if ( pos === -1 ) {
|
|
||||||
return '*';
|
|
||||||
}
|
|
||||||
return hostname.slice(pos + 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
HnSwitches.toBroaderHostname = toBroaderHostname;
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
HnSwitches.prototype.reset = function() {
|
|
||||||
this.switches = {};
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// If value is undefined, the switch is removed
|
|
||||||
|
|
||||||
HnSwitches.prototype.toggle = function(switchName, hostname, newVal) {
|
|
||||||
var bitOffset = switchBitOffsets[switchName];
|
|
||||||
if ( bitOffset === undefined ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( newVal === this.evaluate(switchName, hostname) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var bits = this.switches[hostname] || 0;
|
|
||||||
bits &= ~(3 << bitOffset);
|
|
||||||
bits |= newVal << bitOffset;
|
|
||||||
if ( bits === 0 ) {
|
|
||||||
delete this.switches[hostname];
|
|
||||||
} else {
|
|
||||||
this.switches[hostname] = bits;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
HnSwitches.prototype.toggleZ = function(switchName, hostname, newState) {
|
|
||||||
var bitOffset = switchBitOffsets[switchName];
|
|
||||||
if ( bitOffset === undefined ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var state = this.evaluateZ(switchName, hostname);
|
|
||||||
if ( newState === state ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ( newState === undefined ) {
|
|
||||||
newState = !state;
|
|
||||||
}
|
|
||||||
var bits = this.switches[hostname] || 0;
|
|
||||||
bits &= ~(3 << bitOffset);
|
|
||||||
if ( bits === 0 ) {
|
|
||||||
delete this.switches[hostname];
|
|
||||||
} else {
|
|
||||||
this.switches[hostname] = bits;
|
|
||||||
}
|
|
||||||
state = this.evaluateZ(switchName, hostname);
|
|
||||||
if ( state === newState ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
this.switches[hostname] = bits | ((newState ? 1 : 2) << bitOffset);
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// 0 = inherit from broader scope, up to default state
|
|
||||||
// 1 = non-default state
|
|
||||||
// 2 = forced default state (to override a broader non-default state)
|
|
||||||
|
|
||||||
HnSwitches.prototype.evaluate = function(switchName, hostname) {
|
|
||||||
var bits = this.switches[hostname] || 0;
|
|
||||||
if ( bits === 0 ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
var bitOffset = switchBitOffsets[switchName];
|
|
||||||
if ( bitOffset === undefined ) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return (bits >> bitOffset) & 3;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
HnSwitches.prototype.evaluateZ = function(switchName, hostname) {
|
|
||||||
var bitOffset = switchBitOffsets[switchName];
|
|
||||||
if ( bitOffset === undefined ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var bits;
|
|
||||||
var s = hostname;
|
|
||||||
for (;;) {
|
|
||||||
bits = this.switches[s] || 0;
|
|
||||||
if ( bits !== 0 ) {
|
|
||||||
bits = bits >> bitOffset & 3;
|
|
||||||
if ( bits !== 0 ) {
|
|
||||||
return bits === 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s = toBroaderHostname(s);
|
|
||||||
if ( s === '' ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
HnSwitches.prototype.toString = function() {
|
|
||||||
var out = [];
|
|
||||||
var switchName, val;
|
|
||||||
var hostname;
|
|
||||||
for ( hostname in this.switches ) {
|
|
||||||
if ( this.switches.hasOwnProperty(hostname) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for ( switchName in switchBitOffsets ) {
|
|
||||||
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
val = this.evaluate(switchName, hostname);
|
|
||||||
if ( val === 0 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.push(switchName + ': ' + hostname + ' ' + switchStateToNameMap[val]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out.join('\n');
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
HnSwitches.prototype.fromString = function(text) {
|
|
||||||
var textEnd = text.length;
|
|
||||||
var lineBeg = 0, lineEnd;
|
|
||||||
var line, pos;
|
|
||||||
var fields;
|
|
||||||
var switchName, hostname, state;
|
|
||||||
|
|
||||||
while ( lineBeg < textEnd ) {
|
|
||||||
lineEnd = text.indexOf('\n', lineBeg);
|
|
||||||
if ( lineEnd < 0 ) {
|
|
||||||
lineEnd = text.indexOf('\r', lineBeg);
|
|
||||||
if ( lineEnd < 0 ) {
|
|
||||||
lineEnd = textEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
line = text.slice(lineBeg, lineEnd).trim();
|
|
||||||
lineBeg = lineEnd + 1;
|
|
||||||
|
|
||||||
pos = line.indexOf('# ');
|
|
||||||
if ( pos !== -1 ) {
|
|
||||||
line = line.slice(0, pos).trim();
|
|
||||||
}
|
|
||||||
if ( line === '' ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
fields = line.split(/\s+/);
|
|
||||||
if ( fields.length !== 3 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
switchName = fields[0];
|
|
||||||
pos = switchName.indexOf(':');
|
|
||||||
if ( pos === -1 ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
switchName = switchName.slice(0, pos);
|
|
||||||
if ( switchBitOffsets.hasOwnProperty(switchName) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
hostname = punycode.toASCII(fields[1]);
|
|
||||||
|
|
||||||
state = fields[2];
|
|
||||||
if ( nameToSwitchStateMap.hasOwnProperty(state) === false ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toggle(switchName, hostname, nameToSwitchStateMap[state]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
return HnSwitches;
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.hnSwitches = new µBlock.HnSwitches();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
@ -76,7 +76,7 @@ var onMessage = function(request, sender, callback) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'reloadTab':
|
case 'reloadTab':
|
||||||
if ( vAPI.isNoTabId(request.tabId) === false ) {
|
if ( vAPI.isBehindTheSceneTabId(request.tabId) === false ) {
|
||||||
vAPI.tabs.reload(request.tabId);
|
vAPI.tabs.reload(request.tabId);
|
||||||
if ( request.select && vAPI.tabs.select ) {
|
if ( request.select && vAPI.tabs.select ) {
|
||||||
vAPI.tabs.select(request.tabId);
|
vAPI.tabs.select(request.tabId);
|
||||||
@ -203,6 +203,7 @@ var getFirewallRules = function(srcHostname, desHostnames) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var getStats = function(tabId, tabTitle) {
|
var getStats = function(tabId, tabTitle) {
|
||||||
|
var tabContext = µb.tabContextManager.lookup(tabId);
|
||||||
var r = {
|
var r = {
|
||||||
advancedUserEnabled: µb.userSettings.advancedUserEnabled,
|
advancedUserEnabled: µb.userSettings.advancedUserEnabled,
|
||||||
appName: vAPI.app.name,
|
appName: vAPI.app.name,
|
||||||
@ -213,39 +214,35 @@ var getStats = function(tabId, tabTitle) {
|
|||||||
globalAllowedRequestCount: µb.localSettings.allowedRequestCount,
|
globalAllowedRequestCount: µb.localSettings.allowedRequestCount,
|
||||||
globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
|
globalBlockedRequestCount: µb.localSettings.blockedRequestCount,
|
||||||
netFilteringSwitch: false,
|
netFilteringSwitch: false,
|
||||||
pageURL: '',
|
rawURL: tabContext.rawURL,
|
||||||
|
pageURL: tabContext.normalURL,
|
||||||
|
pageHostname: tabContext.rootHostname,
|
||||||
|
pageDomain: tabContext.rootDomain,
|
||||||
pageAllowedRequestCount: 0,
|
pageAllowedRequestCount: 0,
|
||||||
pageBlockedRequestCount: 0,
|
pageBlockedRequestCount: 0,
|
||||||
tabId: tabId,
|
tabId: tabId,
|
||||||
tabTitle: tabTitle
|
tabTitle: tabTitle
|
||||||
};
|
};
|
||||||
|
|
||||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
if ( pageStore ) {
|
if ( pageStore ) {
|
||||||
r.rawURL = pageStore.rawURL;
|
|
||||||
r.pageURL = pageStore.pageURL;
|
|
||||||
r.pageDomain = pageStore.pageDomain;
|
|
||||||
r.pageHostname = pageStore.pageHostname;
|
|
||||||
r.pageBlockedRequestCount = pageStore.perLoadBlockedRequestCount;
|
r.pageBlockedRequestCount = pageStore.perLoadBlockedRequestCount;
|
||||||
r.pageAllowedRequestCount = pageStore.perLoadAllowedRequestCount;
|
r.pageAllowedRequestCount = pageStore.perLoadAllowedRequestCount;
|
||||||
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
|
r.netFilteringSwitch = pageStore.getNetFilteringSwitch();
|
||||||
r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap);
|
r.hostnameDict = getHostnameDict(pageStore.hostnameToCountMap);
|
||||||
r.contentLastModified = pageStore.contentLastModified;
|
r.contentLastModified = pageStore.contentLastModified;
|
||||||
r.firewallRules = getFirewallRules(pageStore.pageHostname, r.hostnameDict);
|
r.firewallRules = getFirewallRules(tabContext.rootHostname, r.hostnameDict);
|
||||||
r.canElementPicker = r.pageHostname.indexOf('.') !== -1;
|
r.canElementPicker = tabContext.rootHostname.indexOf('.') !== -1;
|
||||||
r.canRequestLog = canRequestLog;
|
r.canRequestLog = canRequestLog;
|
||||||
r.doBlockAllPopups = µb.hnSwitches.evaluateZ('doBlockAllPopups', r.pageHostname);
|
|
||||||
r.dontBlockDoc = µb.hnSwitches.evaluateZ('dontBlockDoc', r.pageHostname);
|
|
||||||
} else {
|
} else {
|
||||||
r.hostnameDict = {};
|
r.hostnameDict = {};
|
||||||
r.firewallRules = getFirewallRules();
|
r.firewallRules = getFirewallRules();
|
||||||
}
|
}
|
||||||
if ( r.pageHostname ) {
|
r.matrixIsDirty = !µb.sessionFirewall.hasSameRules(
|
||||||
r.matrixIsDirty = !µb.sessionFirewall.hasSameRules(
|
µb.permanentFirewall,
|
||||||
µb.permanentFirewall,
|
tabContext.rootHostname,
|
||||||
r.pageHostname,
|
r.hostnameDict
|
||||||
r.hostnameDict
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
return r;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -449,16 +446,7 @@ var filterRequests = function(pageStore, details) {
|
|||||||
var isBlockResult = µb.isBlockResult;
|
var isBlockResult = µb.isBlockResult;
|
||||||
|
|
||||||
// Create evaluation context
|
// Create evaluation context
|
||||||
var context = {
|
var context = pageStore.createContextFromFrameHostname(details.pageHostname);
|
||||||
pageHostname: vAPI.punycodeHostname(details.pageHostname),
|
|
||||||
pageDomain: µburi.domainFromHostname(details.pageHostname),
|
|
||||||
rootHostname: pageStore.rootHostname,
|
|
||||||
rootDomain: pageStore.rootDomain,
|
|
||||||
requestURL: '',
|
|
||||||
requestHostname: '',
|
|
||||||
requestType: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
var request;
|
var request;
|
||||||
var i = requests.length;
|
var i = requests.length;
|
||||||
while ( i-- ) {
|
while ( i-- ) {
|
||||||
@ -485,22 +473,18 @@ var onMessage = function(details, sender, callback) {
|
|||||||
// Sync
|
// Sync
|
||||||
var response;
|
var response;
|
||||||
|
|
||||||
var pageStore, frameStore = false;
|
var pageStore;
|
||||||
if ( sender && sender.tab ) {
|
if ( sender && sender.tab ) {
|
||||||
pageStore = µb.pageStoreFromTabId(sender.tab.id);
|
pageStore = µb.pageStoreFromTabId(sender.tab.id);
|
||||||
var frameId = sender.frameId;
|
|
||||||
if(frameId && frameId > 0) {
|
|
||||||
frameStore = pageStore.getFrame(frameId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ( details.what ) {
|
switch ( details.what ) {
|
||||||
case 'retrieveGenericCosmeticSelectors':
|
case 'retrieveGenericCosmeticSelectors':
|
||||||
response = {
|
response = {
|
||||||
shutdown: !pageStore || !pageStore.getNetFilteringSwitch() || (frameStore && !frameStore.getNetFilteringSwitch()),
|
shutdown: !pageStore || !pageStore.getNetFilteringSwitch(),
|
||||||
result: null
|
result: null
|
||||||
};
|
};
|
||||||
if(pageStore && pageStore.getGenericCosmeticFilteringSwitch()) {
|
if ( !response.shutdown && pageStore.getGenericCosmeticFilteringSwitch() ) {
|
||||||
response.result = µb.cosmeticFilteringEngine.retrieveGenericSelectors(details);
|
response.result = µb.cosmeticFilteringEngine.retrieveGenericSelectors(details);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -515,7 +499,7 @@ var onMessage = function(details, sender, callback) {
|
|||||||
shutdown: !pageStore || !pageStore.getNetFilteringSwitch(),
|
shutdown: !pageStore || !pageStore.getNetFilteringSwitch(),
|
||||||
result: null
|
result: null
|
||||||
};
|
};
|
||||||
if(pageStore) {
|
if(!response.shutdown) {
|
||||||
response.result = filterRequests(pageStore, details);
|
response.result = filterRequests(pageStore, details);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -998,7 +982,6 @@ var backupUserData = function(callback) {
|
|||||||
filterLists: µb.extractSelectedFilterLists(),
|
filterLists: µb.extractSelectedFilterLists(),
|
||||||
netWhitelist: µb.stringFromWhitelist(µb.netWhitelist),
|
netWhitelist: µb.stringFromWhitelist(µb.netWhitelist),
|
||||||
dynamicFilteringString: µb.permanentFirewall.toString(),
|
dynamicFilteringString: µb.permanentFirewall.toString(),
|
||||||
hostnameSwitchesString: µb.hnSwitches.toString(),
|
|
||||||
userFilters: details.content
|
userFilters: details.content
|
||||||
};
|
};
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
@ -1013,7 +996,7 @@ var backupUserData = function(callback) {
|
|||||||
|
|
||||||
µb.restoreBackupSettings.lastBackupFile = filename;
|
µb.restoreBackupSettings.lastBackupFile = filename;
|
||||||
µb.restoreBackupSettings.lastBackupTime = Date.now();
|
µb.restoreBackupSettings.lastBackupTime = Date.now();
|
||||||
µb.XAL.keyvalSetMany(µb.restoreBackupSettings);
|
µb.keyvalSetMany(µb.restoreBackupSettings);
|
||||||
|
|
||||||
getLocalData(callback);
|
getLocalData(callback);
|
||||||
};
|
};
|
||||||
@ -1035,20 +1018,19 @@ var restoreUserData = function(request) {
|
|||||||
|
|
||||||
var onAllRemoved = function() {
|
var onAllRemoved = function() {
|
||||||
// Be sure to adjust `countdown` if adding/removing anything below
|
// Be sure to adjust `countdown` if adding/removing anything below
|
||||||
µb.XAL.keyvalSetOne('version', userData.version);
|
µb.keyvalSetOne('version', userData.version);
|
||||||
µBlock.saveLocalSettings(true);
|
µBlock.saveLocalSettings(true);
|
||||||
µb.XAL.keyvalSetMany(userData.userSettings, onCountdown);
|
µb.keyvalSetMany(userData.userSettings, onCountdown);
|
||||||
µb.XAL.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown);
|
µb.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown);
|
||||||
µb.XAL.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown);
|
µb.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown);
|
||||||
|
|
||||||
// With versions 0.9.2.4-, dynamic rules were saved within the
|
// With versions 0.9.2.4-, dynamic rules were saved within the
|
||||||
// `userSettings` object. No longer the case.
|
// `userSettings` object. No longer the case.
|
||||||
var s = userData.dynamicFilteringString || userData.userSettings.dynamicFilteringString || '';
|
var s = userData.dynamicFilteringString || userData.userSettings.dynamicFilteringString || '';
|
||||||
µb.XAL.keyvalSetOne('dynamicFilteringString', s, onCountdown);
|
µb.keyvalSetOne('dynamicFilteringString', s, onCountdown);
|
||||||
|
|
||||||
µb.XAL.keyvalSetOne('hostnameSwitchesString', userData.hostnameSwitchesString || '', onCountdown);
|
|
||||||
µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown);
|
µb.assets.put('assets/user/filters.txt', userData.userFilters, onCountdown);
|
||||||
µb.XAL.keyvalSetMany({
|
µb.keyvalSetMany({
|
||||||
lastRestoreFile: request.file || '',
|
lastRestoreFile: request.file || '',
|
||||||
lastRestoreTime: Date.now(),
|
lastRestoreTime: Date.now(),
|
||||||
lastBackupFile: '',
|
lastBackupFile: '',
|
||||||
@ -1062,13 +1044,13 @@ var restoreUserData = function(request) {
|
|||||||
|
|
||||||
// If we are going to restore all, might as well wipe out clean local
|
// If we are going to restore all, might as well wipe out clean local
|
||||||
// storage
|
// storage
|
||||||
µb.XAL.keyvalRemoveAll(onAllRemoved);
|
vAPI.storage.clear(onAllRemoved);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var resetUserData = function() {
|
var resetUserData = function() {
|
||||||
µb.XAL.keyvalRemoveAll();
|
vAPI.storage.clear(onAllRemoved);
|
||||||
|
|
||||||
// Keep global counts, people can become quite attached to numbers
|
// Keep global counts, people can become quite attached to numbers
|
||||||
µb.saveLocalSettings(true);
|
µb.saveLocalSettings(true);
|
||||||
|
@ -391,6 +391,9 @@ NetFilteringResultCache.prototype.lookup = function(context) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// FrameStores are just for associating a
|
||||||
|
// frame ID with a URL. pageHostname is really
|
||||||
|
// frameHostname.
|
||||||
// To mitigate memory churning
|
// To mitigate memory churning
|
||||||
var frameStoreJunkyard = [];
|
var frameStoreJunkyard = [];
|
||||||
var frameStoreJunkyardMax = 50;
|
var frameStoreJunkyardMax = 50;
|
||||||
@ -420,22 +423,13 @@ FrameStore.prototype.init = function(rootHostname, frameURL) {
|
|||||||
this.pageURL = frameURL;
|
this.pageURL = frameURL;
|
||||||
this.pageHostname = µburi.hostnameFromURI(frameURL);
|
this.pageHostname = µburi.hostnameFromURI(frameURL);
|
||||||
this.pageDomain = µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
|
this.pageDomain = µburi.domainFromHostname(this.pageHostname) || this.pageHostname;
|
||||||
this.rootHostname = rootHostname;
|
|
||||||
this.rootDomain = µburi.domainFromHostname(rootHostname) || rootHostname;
|
|
||||||
// This is part of the filtering evaluation context
|
|
||||||
this.requestURL = this.requestHostname = this.requestType = '';
|
|
||||||
this.netFiltering = true;
|
|
||||||
this.netFilteringReadTime = 0;
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FrameStore.prototype.dispose = function() {
|
FrameStore.prototype.dispose = function() {
|
||||||
this.pageHostname = this.pageDomain =
|
this.pageHostname = this.pageDomain = '';
|
||||||
this.rootHostname = this.rootDomain =
|
|
||||||
this.requestURL = this.requestHostname = this.requestType = '';
|
|
||||||
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
|
if ( frameStoreJunkyard.length < frameStoreJunkyardMax ) {
|
||||||
frameStoreJunkyard.push(this);
|
frameStoreJunkyard.push(this);
|
||||||
}
|
}
|
||||||
@ -444,55 +438,35 @@ FrameStore.prototype.dispose = function() {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FrameStore.prototype.getNetFilteringSwitch = function() {
|
|
||||||
if ( this.netFilteringReadTime < µb.netWhitelistModifyTime ) {
|
|
||||||
this.netFiltering = µb.getNetFilteringSwitch(this.pageURL);
|
|
||||||
this.netFilteringReadTime = Date.now();
|
|
||||||
}
|
|
||||||
return this.netFiltering;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// To mitigate memory churning
|
// To mitigate memory churning
|
||||||
var pageStoreJunkyard = [];
|
var pageStoreJunkyard = [];
|
||||||
var pageStoreJunkyardMax = 10;
|
var pageStoreJunkyardMax = 10;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var PageStore = function(tabId, rawURL, pageURL) {
|
var PageStore = function(tabId) {
|
||||||
this.init(tabId, rawURL, pageURL);
|
this.init(tabId);
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.factory = function(tabId, rawURL, pageURL) {
|
PageStore.factory = function(tabId) {
|
||||||
var entry = pageStoreJunkyard.pop();
|
var entry = pageStoreJunkyard.pop();
|
||||||
if ( entry === undefined ) {
|
if ( entry === undefined ) {
|
||||||
entry = new PageStore(tabId, rawURL, pageURL);
|
entry = new PageStore(tabId);
|
||||||
} else {
|
} else {
|
||||||
entry.init(tabId, rawURL, pageURL);
|
entry.init(tabId);
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.init = function(tabId, rawURL, pageURL) {
|
PageStore.prototype.init = function(tabId) {
|
||||||
|
var tabContext = µb.tabContextManager.lookup(tabId);
|
||||||
this.tabId = tabId;
|
this.tabId = tabId;
|
||||||
this.rawURL = rawURL;
|
|
||||||
this.pageURL = pageURL;
|
|
||||||
this.pageHostname = µb.URI.hostnameFromURI(pageURL);
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/185
|
|
||||||
// Use hostname if no domain can be extracted
|
|
||||||
this.pageDomain = µb.URI.domainFromHostname(this.pageHostname) || this.pageHostname;
|
|
||||||
this.rootHostname = this.pageHostname;
|
|
||||||
this.rootDomain = this.pageDomain;
|
|
||||||
|
|
||||||
// This is part of the filtering evaluation context
|
|
||||||
this.requestURL = this.requestHostname = this.requestType = '';
|
|
||||||
|
|
||||||
|
this.tabHostname = tabContext.rootHostname;
|
||||||
this.hostnameToCountMap = {};
|
this.hostnameToCountMap = {};
|
||||||
this.contentLastModified = 0;
|
this.contentLastModified = 0;
|
||||||
this.frames = {};
|
this.frames = {};
|
||||||
@ -500,13 +474,13 @@ PageStore.prototype.init = function(tabId, rawURL, pageURL) {
|
|||||||
this.netFilteringReadTime = 0;
|
this.netFilteringReadTime = 0;
|
||||||
this.perLoadBlockedRequestCount = 0;
|
this.perLoadBlockedRequestCount = 0;
|
||||||
this.perLoadAllowedRequestCount = 0;
|
this.perLoadAllowedRequestCount = 0;
|
||||||
this.skipLocalMirroring = false;
|
|
||||||
this.netFilteringCache = NetFilteringResultCache.factory();
|
this.netFilteringCache = NetFilteringResultCache.factory();
|
||||||
|
|
||||||
// Support `elemhide` filter option. Called at this point so the required
|
// Support `elemhide` filter option. Called at this point so the required
|
||||||
// context is all setup at this point.
|
// context is all setup at this point.
|
||||||
|
var context = this.createContextFromPage();
|
||||||
this.skipCosmeticFiltering = µb.staticNetFilteringEngine
|
this.skipCosmeticFiltering = µb.staticNetFilteringEngine
|
||||||
.matchStringExactType(this, pageURL, 'cosmetic-filtering')
|
.matchStringExactType(context, tabContext.normalURL, 'cosmetic-filtering')
|
||||||
.charAt(1) === 'b';
|
.charAt(1) === 'b';
|
||||||
|
|
||||||
// Preserve old buffer if there is one already, it may be in use, and
|
// Preserve old buffer if there is one already, it may be in use, and
|
||||||
@ -520,7 +494,7 @@ PageStore.prototype.init = function(tabId, rawURL, pageURL) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.reuse = function(rawURL, pageURL, context) {
|
PageStore.prototype.reuse = function(context) {
|
||||||
// We can't do this: when force refreshing a page, the page store data
|
// We can't do this: when force refreshing a page, the page store data
|
||||||
// needs to be reset
|
// needs to be reset
|
||||||
//if ( pageURL === this.pageURL ) {
|
//if ( pageURL === this.pageURL ) {
|
||||||
@ -528,8 +502,8 @@ PageStore.prototype.reuse = function(rawURL, pageURL, context) {
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
// If the hostname changes, we can't merely just update the context.
|
// If the hostname changes, we can't merely just update the context.
|
||||||
var pageHostname = µb.URI.hostnameFromURI(pageURL);
|
var tabContext = µb.tabContextManager.lookup(this.tabId);
|
||||||
if ( pageHostname !== this.pageHostname ) {
|
if ( tabContext.rootHostname !== this.tabHostname ) {
|
||||||
context = '';
|
context = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -539,19 +513,16 @@ PageStore.prototype.reuse = function(rawURL, pageURL, context) {
|
|||||||
// video thumbnail would not work, because the frame hierarchy structure
|
// video thumbnail would not work, because the frame hierarchy structure
|
||||||
// was flushed from memory, while not really being flushed on the page.
|
// was flushed from memory, while not really being flushed on the page.
|
||||||
if ( context === 'tabUpdated' ) {
|
if ( context === 'tabUpdated' ) {
|
||||||
this.rawURL = rawURL;
|
|
||||||
this.pageURL = pageURL;
|
|
||||||
|
|
||||||
// As part of https://github.com/chrisaljoudi/uBlock/issues/405
|
// As part of https://github.com/chrisaljoudi/uBlock/issues/405
|
||||||
// URL changed, force a re-evaluation of filtering switch
|
// URL changed, force a re-evaluation of filtering switch
|
||||||
this.netFilteringReadTime = 0;
|
this.netFilteringReadTime = 0;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A new page is completely reloaded from scratch, reset all.
|
// A new page is completely reloaded from scratch, reset all.
|
||||||
this.disposeFrameStores();
|
this.disposeFrameStores();
|
||||||
this.netFilteringCache = this.netFilteringCache.dispose();
|
this.netFilteringCache = this.netFilteringCache.dispose();
|
||||||
this.init(this.tabId, rawURL, pageURL);
|
this.init(this.tabId);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -564,10 +535,6 @@ PageStore.prototype.dispose = function() {
|
|||||||
// need to release the memory taken by these, which can amount to
|
// need to release the memory taken by these, which can amount to
|
||||||
// sizeable enough chunks (especially requests, through the request URL
|
// sizeable enough chunks (especially requests, through the request URL
|
||||||
// used as a key).
|
// used as a key).
|
||||||
this.rawURL = this.pageURL =
|
|
||||||
this.pageHostname = this.pageDomain =
|
|
||||||
this.rootHostname = this.rootDomain =
|
|
||||||
this.requestURL = this.requestHostname = this.requestType = '';
|
|
||||||
this.hostnameToCountMap = null;
|
this.hostnameToCountMap = null;
|
||||||
this.disposeFrameStores();
|
this.disposeFrameStores();
|
||||||
this.netFilteringCache = this.netFilteringCache.dispose();
|
this.netFilteringCache = this.netFilteringCache.dispose();
|
||||||
@ -609,34 +576,74 @@ PageStore.prototype.setFrame = function(frameId, frameURL) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
PageStore.prototype.createContextFromPage = function() {
|
||||||
|
var context = new µb.tabContextManager.createContext(this.tabId);
|
||||||
|
context.pageHostname = context.rootHostname;
|
||||||
|
context.pageDomain = context.rootDomain;
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
PageStore.prototype.createContextFromFrameId = function(frameId) {
|
||||||
|
var context = new µb.tabContextManager.createContext(this.tabId);
|
||||||
|
if ( this.frames.hasOwnProperty(frameId) ) {
|
||||||
|
var frameStore = this.frames[frameId];
|
||||||
|
context.pageHostname = frameStore.pageHostname;
|
||||||
|
context.pageDomain = frameStore.pageDomain;
|
||||||
|
} else {
|
||||||
|
context.pageHostname = context.rootHostname;
|
||||||
|
context.pageDomain = context.rootDomain;
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
PageStore.prototype.createContextFromFrameHostname = function(frameHostname) {
|
||||||
|
var context = new µb.tabContextManager.createContext(this.tabId);
|
||||||
|
context.pageHostname = frameHostname;
|
||||||
|
context.pageDomain = µb.URI.domainFromHostname(frameHostname) || frameHostname;
|
||||||
|
return context;
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.getNetFilteringSwitch = function() {
|
PageStore.prototype.getNetFilteringSwitch = function() {
|
||||||
|
var tabContext = µb.tabContextManager.lookup(this.tabId);
|
||||||
|
if (
|
||||||
|
this.netFilteringReadTime > µb.netWhitelistModifyTime &&
|
||||||
|
this.netFilteringReadTime > tabContext.modifyTime
|
||||||
|
) {
|
||||||
|
return this.netFiltering;
|
||||||
|
}
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1078
|
// https://github.com/chrisaljoudi/uBlock/issues/1078
|
||||||
// Use both the raw and normalized URLs.
|
// Use both the raw and normalized URLs.
|
||||||
if ( this.netFilteringReadTime < µb.netWhitelistModifyTime ) {
|
this.netFiltering = µb.getNetFilteringSwitch(tabContext.normalURL);
|
||||||
this.netFiltering = µb.getNetFilteringSwitch(this.pageURL);
|
if ( this.netFiltering && tabContext.rawURL !== tabContext.pageURL ) {
|
||||||
if ( this.netFiltering && this.rawURL !== this.pageURL ) {
|
this.netFiltering = µb.getNetFilteringSwitch(tabContext.rawURL);
|
||||||
this.netFiltering = µb.getNetFilteringSwitch(this.rawURL);
|
|
||||||
}
|
|
||||||
this.netFilteringReadTime = Date.now();
|
|
||||||
}
|
}
|
||||||
|
this.netFilteringReadTime = Date.now();
|
||||||
return this.netFiltering;
|
return this.netFiltering;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
|
PageStore.prototype.getSpecificCosmeticFilteringSwitch = function() {
|
||||||
return this.getNetFilteringSwitch() &&
|
if ( this.getNetFilteringSwitch() === false ) {
|
||||||
(µb.userSettings.advancedUserEnabled &&
|
return false;
|
||||||
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
|
}
|
||||||
|
|
||||||
|
var tabContext = µb.tabContextManager.lookup(this.tabId);
|
||||||
|
|
||||||
|
return µb.userSettings.advancedUserEnabled === false ||
|
||||||
|
µb.sessionFirewall.mustAllowCellZY(tabContext.rootHostname, tabContext.rootHostname, '*') === false;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
|
PageStore.prototype.getGenericCosmeticFilteringSwitch = function() {
|
||||||
return this.getNetFilteringSwitch() &&
|
if ( this.skipCosmeticFiltering ) {
|
||||||
this.skipCosmeticFiltering === false &&
|
return false;
|
||||||
(µb.userSettings.advancedUserEnabled &&
|
}
|
||||||
µb.sessionFirewall.mustAllowCellZY(this.rootHostname, this.rootHostname, '*')) === false;
|
return this.getSpecificCosmeticFilteringSwitch();
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -649,15 +656,8 @@ PageStore.prototype.toggleNetFilteringSwitch = function(url, scope, state) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.filterRequest = function(context) {
|
PageStore.prototype.filterRequest = function(context) {
|
||||||
if(context.preNavigationHeader) { // sometimes we get inline-script queries before being
|
|
||||||
// informed of navigation
|
if ( this.getNetFilteringSwitch() === false ) {
|
||||||
if(µb.getNetFilteringSwitch(context.requestURL) === false) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.getNetFilteringSwitch() === false || // if we're turned off (whitelisted)
|
|
||||||
(typeof context.getNetFilteringSwitch === "function" && // or we're in a frame that's whitelisted
|
|
||||||
context.getNetFilteringSwitch() === false)) {
|
|
||||||
if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) {
|
if ( collapsibleRequestTypes.indexOf(context.requestType) !== -1 ) {
|
||||||
this.netFilteringCache.add(context, '');
|
this.netFilteringCache.add(context, '');
|
||||||
}
|
}
|
||||||
@ -678,7 +678,11 @@ PageStore.prototype.filterRequest = function(context) {
|
|||||||
// We evaluate dynamic filtering first, and hopefully we can skip
|
// We evaluate dynamic filtering first, and hopefully we can skip
|
||||||
// evaluation of static filtering.
|
// evaluation of static filtering.
|
||||||
if ( µb.userSettings.advancedUserEnabled ) {
|
if ( µb.userSettings.advancedUserEnabled ) {
|
||||||
var df = µb.sessionFirewall.evaluateCellZY(context.pageHostname, context.requestHostname, context.requestType);
|
var df = µb.sessionFirewall.evaluateCellZY(
|
||||||
|
context.rootHostname,
|
||||||
|
context.requestHostname,
|
||||||
|
context.requestType
|
||||||
|
);
|
||||||
if ( df.mustBlockOrAllow() ) {
|
if ( df.mustBlockOrAllow() ) {
|
||||||
result = df.toFilterString();
|
result = df.toFilterString();
|
||||||
}
|
}
|
||||||
@ -706,15 +710,7 @@ var collapsibleRequestTypes = 'image sub_frame object';
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.filterRequestNoCache = function(context) {
|
PageStore.prototype.filterRequestNoCache = function(context) {
|
||||||
if(context.preNavigationHeader) { // sometimes we get inline-script queries before being
|
if ( this.getNetFilteringSwitch() === false ) {
|
||||||
// informed of navigation
|
|
||||||
if(µb.getNetFilteringSwitch(context.requestURL) === false) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(this.getNetFilteringSwitch() === false || // if we're turned off (whitelisted)
|
|
||||||
(typeof context.getNetFilteringSwitch === "function" && // or we're in a frame that's whitelisted
|
|
||||||
context.getNetFilteringSwitch() === false)) {
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -726,7 +722,11 @@ PageStore.prototype.filterRequestNoCache = function(context) {
|
|||||||
// We evaluate dynamic filtering first, and hopefully we can skip
|
// We evaluate dynamic filtering first, and hopefully we can skip
|
||||||
// evaluation of static filtering.
|
// evaluation of static filtering.
|
||||||
if ( µb.userSettings.advancedUserEnabled ) {
|
if ( µb.userSettings.advancedUserEnabled ) {
|
||||||
var df = µb.sessionFirewall.evaluateCellZY(context.pageHostname, context.requestHostname, context.requestType);
|
var df = µb.sessionFirewall.evaluateCellZY(
|
||||||
|
context.rootHostname,
|
||||||
|
context.requestHostname,
|
||||||
|
context.requestType
|
||||||
|
);
|
||||||
if ( df.mustBlockOrAllow() ) {
|
if ( df.mustBlockOrAllow() ) {
|
||||||
result = df.toFilterString();
|
result = df.toFilterString();
|
||||||
}
|
}
|
||||||
@ -748,7 +748,7 @@ PageStore.prototype.logRequest = function(context, result) {
|
|||||||
// be prepared to handle invalid requestHostname, I've seen this
|
// be prepared to handle invalid requestHostname, I've seen this
|
||||||
// happen: http://./
|
// happen: http://./
|
||||||
if ( requestHostname === '' ) {
|
if ( requestHostname === '' ) {
|
||||||
requestHostname = context.pageHostname;
|
requestHostname = context.rootHostname;
|
||||||
}
|
}
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) {
|
if ( this.hostnameToCountMap.hasOwnProperty(requestHostname) === false ) {
|
||||||
@ -771,22 +771,6 @@ PageStore.prototype.logRequest = function(context, result) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
PageStore.prototype.toMirrorURL = function(requestURL) {
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/351
|
|
||||||
// Bypass experimental features when uBlock is disabled for a site
|
|
||||||
if ( µb.userSettings.experimentalEnabled === false ||
|
|
||||||
this.getNetFilteringSwitch() === false ||
|
|
||||||
this.skipLocalMirroring ) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://code.google.com/p/chromium/issues/detail?id=387198
|
|
||||||
// Not all redirects will succeed, until bug above is fixed.
|
|
||||||
return µb.mirrors.toURL(requestURL, true);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
PageStore.prototype.updateBadge = function() {
|
PageStore.prototype.updateBadge = function() {
|
||||||
var netFiltering = this.getNetFilteringSwitch();
|
var netFiltering = this.getNetFilteringSwitch();
|
||||||
var badge = '';
|
var badge = '';
|
||||||
|
@ -137,11 +137,10 @@ var onUserSettingsReady = function(fetched) {
|
|||||||
µb.contextMenu.toggle(userSettings.contextMenuEnabled);
|
µb.contextMenu.toggle(userSettings.contextMenuEnabled);
|
||||||
µb.permanentFirewall.fromString(fetched.dynamicFilteringString);
|
µb.permanentFirewall.fromString(fetched.dynamicFilteringString);
|
||||||
µb.sessionFirewall.assign(µb.permanentFirewall);
|
µb.sessionFirewall.assign(µb.permanentFirewall);
|
||||||
µb.hnSwitches.fromString(fetched.hostnameSwitchesString);
|
|
||||||
|
|
||||||
// Remove obsolete setting
|
// Remove obsolete setting
|
||||||
delete userSettings.logRequests;
|
delete userSettings.logRequests;
|
||||||
µb.XAL.keyvalRemoveOne('logRequests');
|
vAPI.storage.remove('logRequests');
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -216,7 +215,6 @@ return function() {
|
|||||||
var fetchableProps = {
|
var fetchableProps = {
|
||||||
'compiledMagic': '',
|
'compiledMagic': '',
|
||||||
'dynamicFilteringString': '',
|
'dynamicFilteringString': '',
|
||||||
'hostnameSwitchesString': '',
|
|
||||||
'lastRestoreFile': '',
|
'lastRestoreFile': '',
|
||||||
'lastRestoreTime': 0,
|
'lastRestoreTime': 0,
|
||||||
'lastBackupFile': '',
|
'lastBackupFile': '',
|
||||||
|
@ -38,6 +38,20 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
µBlock.keyvalSetOne = function(key, val, callback) {
|
||||||
|
var bin = {};
|
||||||
|
bin[key] = val;
|
||||||
|
vAPI.storage.set(bin, callback || this.noopFunc);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
µBlock.keyvalSetMany = function(dict, callback) {
|
||||||
|
vAPI.storage.set(dict, callback || this.noopFunc);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.saveLocalSettings = function(force) {
|
µBlock.saveLocalSettings = function(force) {
|
||||||
if ( force ) {
|
if ( force ) {
|
||||||
this.localSettingsModifyTime = Date.now();
|
this.localSettingsModifyTime = Date.now();
|
||||||
@ -70,13 +84,7 @@
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.savePermanentFirewallRules = function() {
|
µBlock.savePermanentFirewallRules = function() {
|
||||||
this.XAL.keyvalSetOne('dynamicFilteringString', this.permanentFirewall.toString());
|
this.keyvalSetOne('dynamicFilteringString', this.permanentFirewall.toString());
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.saveHostnameSwitches = function() {
|
|
||||||
this.XAL.keyvalSetOne('hostnameSwitchesString', this.hnSwitches.toString());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
603
src/js/tab.js
603
src/js/tab.js
@ -32,141 +32,7 @@
|
|||||||
|
|
||||||
var µb = µBlock;
|
var µb = µBlock;
|
||||||
|
|
||||||
/******************************************************************************/
|
// https://github.com/gorhill/httpswitchboard/issues/303
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// When the DOM content of root frame is loaded, this means the tab
|
|
||||||
// content has changed.
|
|
||||||
|
|
||||||
vAPI.tabs.onNavigation = function(details) {
|
|
||||||
if ( details.frameId !== 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var pageStore = µb.bindTabToPageStats(details.tabId, details.url, 'afterNavigate');
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/630
|
|
||||||
// The hostname of the bound document must always be present in the
|
|
||||||
// mini-matrix. That's the best place I could find for the fix, all other
|
|
||||||
// options had bad side-effects or complications.
|
|
||||||
// TODO: Eventually, we will have to use an API to check whether a scheme
|
|
||||||
// is supported as I suspect we are going to start to see `ws`, `wss`
|
|
||||||
// as well soon.
|
|
||||||
if ( pageStore && details.url.lastIndexOf('http', 0) === 0 ) {
|
|
||||||
pageStore.hostnameToCountMap[pageStore.pageHostname] = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// It may happen the URL in the tab changes, while the page's document
|
|
||||||
// stays the same (for instance, Google Maps). Without this listener,
|
|
||||||
// the extension icon won't be properly refreshed.
|
|
||||||
|
|
||||||
vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
|
|
||||||
if ( !tab.url || tab.url === '' ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( !changeInfo.url ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
µb.bindTabToPageStats(tabId, changeInfo.url, 'tabUpdated');
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
vAPI.tabs.onClosed = function(tabId) {
|
|
||||||
if ( tabId < 0 ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
µb.unbindTabFromPageStats(tabId);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/297
|
|
||||||
|
|
||||||
vAPI.tabs.onPopup = function(details) {
|
|
||||||
//console.debug('vAPI.tabs.onPopup: details = %o', details);
|
|
||||||
|
|
||||||
var pageStore = µb.pageStoreFromTabId(details.openerTabId);
|
|
||||||
var openerURL = details.openerURL || '';
|
|
||||||
|
|
||||||
if ( openerURL === '' && pageStore ) {
|
|
||||||
openerURL = pageStore.pageURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( openerURL === '' ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var µburi = µb.URI;
|
|
||||||
var openerHostname = µburi.hostnameFromURI(openerURL);
|
|
||||||
var openerDomain = µburi.domainFromHostname(openerHostname);
|
|
||||||
|
|
||||||
var targetURL = details.targetURL;
|
|
||||||
|
|
||||||
// If the page URL is that of our "blocked page" URL, extract the URL of
|
|
||||||
// the page which was blocked.
|
|
||||||
if ( targetURL.lastIndexOf(vAPI.getURL('document-blocked.html'), 0) === 0 ) {
|
|
||||||
var matches = /details=([^&]+)/.exec(targetURL);
|
|
||||||
if ( matches !== null ) {
|
|
||||||
targetURL = JSON.parse(atob(matches[1])).url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var context = {
|
|
||||||
pageHostname: openerHostname,
|
|
||||||
pageDomain: openerDomain,
|
|
||||||
rootHostname: openerHostname,
|
|
||||||
rootDomain: openerDomain,
|
|
||||||
requestURL: targetURL,
|
|
||||||
requestHostname: µb.URI.hostnameFromURI(targetURL),
|
|
||||||
requestType: 'popup'
|
|
||||||
};
|
|
||||||
|
|
||||||
var result = '';
|
|
||||||
|
|
||||||
// Check user switch first
|
|
||||||
if ( µb.hnSwitches.evaluateZ('doBlockAllPopups', openerHostname) ) {
|
|
||||||
result = 'ub:doBlockAllPopups true';
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/323
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1142
|
|
||||||
// If popup OR opener URL is whitelisted, do not block the popup
|
|
||||||
if (
|
|
||||||
result === '' &&
|
|
||||||
µb.getNetFilteringSwitch(openerURL) &&
|
|
||||||
µb.getNetFilteringSwitch(targetURL)
|
|
||||||
) {
|
|
||||||
result = µb.staticNetFilteringEngine.matchStringExactType(context, targetURL, 'popup');
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/91
|
|
||||||
if ( pageStore ) {
|
|
||||||
pageStore.logRequest(context, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not blocked
|
|
||||||
if ( µb.isAllowResult(result) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocked
|
|
||||||
|
|
||||||
// It is a popup, block and remove the tab.
|
|
||||||
µb.unbindTabFromPageStats(details.targetTabId);
|
|
||||||
vAPI.tabs.remove(details.targetTabId);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
vAPI.tabs.registerListeners();
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/httpswitchboard/issues/303
|
|
||||||
// Some kind of trick going on here:
|
// Some kind of trick going on here:
|
||||||
// Any scheme other than 'http' and 'https' is remapped into a fake
|
// Any scheme other than 'http' and 'https' is remapped into a fake
|
||||||
// URL which trick the rest of µBlock into being able to process an
|
// URL which trick the rest of µBlock into being able to process an
|
||||||
@ -176,8 +42,11 @@ vAPI.tabs.registerListeners();
|
|||||||
// hostname. This way, for a specific scheme you can create scope with
|
// hostname. This way, for a specific scheme you can create scope with
|
||||||
// rules which will apply only to that scheme.
|
// rules which will apply only to that scheme.
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
µb.normalizePageURL = function(tabId, pageURL) {
|
µb.normalizePageURL = function(tabId, pageURL) {
|
||||||
if ( vAPI.isNoTabId(tabId) ) {
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
return 'http://behind-the-scene/';
|
return 'http://behind-the-scene/';
|
||||||
}
|
}
|
||||||
var uri = this.URI.set(pageURL);
|
var uri = this.URI.set(pageURL);
|
||||||
@ -195,19 +64,437 @@ vAPI.tabs.registerListeners();
|
|||||||
return url;
|
return url;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************
|
||||||
|
|
||||||
|
To keep track from which context *exactly* network requests are made. This is
|
||||||
|
often tricky for various reasons, and the challenge is not specific to one
|
||||||
|
browser.
|
||||||
|
|
||||||
|
The time at which a URL is assigned to a tab and the time when a network
|
||||||
|
request for a root document is made must be assumed to be unrelated: it's all
|
||||||
|
asynchronous. There is no guaranteed order in which the two events are fired.
|
||||||
|
|
||||||
|
Also, other "anomalies" can occur:
|
||||||
|
|
||||||
|
- a network request for a root document is fired without the corresponding
|
||||||
|
tab being really assigned a new URL
|
||||||
|
<https://github.com/chrisaljoudi/uBlock/issues/516>
|
||||||
|
|
||||||
|
- a network request for a secondary resource is labeled with a tab id for
|
||||||
|
which no root document was pulled for that tab.
|
||||||
|
<https://github.com/chrisaljoudi/uBlock/issues/1001>
|
||||||
|
|
||||||
|
- a network request for a secondary resource is made without the root
|
||||||
|
document to which it belongs being formally bound yet to the proper tab id,
|
||||||
|
causing a bad scope to be used for filtering purpose.
|
||||||
|
<https://github.com/chrisaljoudi/uBlock/issues/1205>
|
||||||
|
<https://github.com/chrisaljoudi/uBlock/issues/1140>
|
||||||
|
|
||||||
|
So the solution here is to keep a lightweight data structure which only
|
||||||
|
purpose is to keep track as accurately as possible of which root document
|
||||||
|
belongs to which tab. That's the only purpose, and because of this, there are
|
||||||
|
no restrictions for when the URL of a root document can be associated to a tab.
|
||||||
|
|
||||||
|
Before, the PageStore object was trying to deal with this, but it had to
|
||||||
|
enforce some restrictions so as to not descend into one of the above issues, or
|
||||||
|
other issues. The PageStore object can only be associated with a tab for which
|
||||||
|
a definitive navigation event occurred, because it collects information about
|
||||||
|
what occurred in the tab (for example, the number of requests blocked for a
|
||||||
|
page).
|
||||||
|
|
||||||
|
The TabContext objects do not suffer this restriction, and as a result they
|
||||||
|
offer the most reliable picture of which root document URL is really associated
|
||||||
|
to which tab. Moreover, the TabObject can undo an association from a root
|
||||||
|
document, and automatically re-associate with the next most recent. This takes
|
||||||
|
care of <https://github.com/chrisaljoudi/uBlock/issues/516>.
|
||||||
|
|
||||||
|
The PageStore object no longer cache the various information about which
|
||||||
|
root document it is currently bound. When it needs to find out, it will always
|
||||||
|
defer to the TabContext object, which will provide the real answer. This takes
|
||||||
|
case of <https://github.com/chrisaljoudi/uBlock/issues/1205>. In effect, the
|
||||||
|
master switch and dynamic filtering rules can be evaluated now properly even
|
||||||
|
in the absence of a PageStore object, this was not the case before.
|
||||||
|
|
||||||
|
Also, the TabContext object will try its best to find a good candidate root
|
||||||
|
document URL for when none exists. This takes care of
|
||||||
|
<https://github.com/chrisaljoudi/uBlock/issues/1001>.
|
||||||
|
|
||||||
|
The TabContext manager is self-contained, and it takes care to properly
|
||||||
|
housekeep itself.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
µb.tabContextManager = (function() {
|
||||||
|
var tabContexts = Object.create(null);
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
||||||
|
// This is to be used as last-resort fallback in case a tab is found to not
|
||||||
|
// be bound while network requests are fired for the tab.
|
||||||
|
var mostRecentRootDocURL = '';
|
||||||
|
var mostRecentRootDocURLTimestamp = 0;
|
||||||
|
|
||||||
|
var gcPeriod = 10 * 60 * 1000;
|
||||||
|
|
||||||
|
var TabContext = function(tabId) {
|
||||||
|
this.tabId = tabId.toString();
|
||||||
|
this.stack = [];
|
||||||
|
this.rawURL =
|
||||||
|
this.normalURL =
|
||||||
|
this.rootHostname =
|
||||||
|
this.rootDomain = '';
|
||||||
|
this.timer = null;
|
||||||
|
this.onTabCallback = null;
|
||||||
|
this.onTimerCallback = null;
|
||||||
|
|
||||||
|
tabContexts[tabId] = this;
|
||||||
|
};
|
||||||
|
|
||||||
|
TabContext.prototype.destroy = function() {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( this.timer !== null ) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
|
delete tabContexts[this.tabId];
|
||||||
|
};
|
||||||
|
|
||||||
|
TabContext.prototype.onTab = function(tab) {
|
||||||
|
if ( tab ) {
|
||||||
|
this.timer = setTimeout(this.onTimerCallback, gcPeriod);
|
||||||
|
} else {
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
TabContext.prototype.onTimer = function() {
|
||||||
|
this.timer = null;
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vAPI.tabs.get(this.tabId, this.onTabCallback);
|
||||||
|
};
|
||||||
|
|
||||||
|
// This takes care of orphanized tab contexts. Can't be started for all
|
||||||
|
// contexts, as the behind-the-scene context is permanent -- so we do not
|
||||||
|
// want to slush it.
|
||||||
|
TabContext.prototype.autodestroy = function() {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onTabCallback = this.onTab.bind(this);
|
||||||
|
this.onTimerCallback = this.onTimer.bind(this);
|
||||||
|
this.timer = setTimeout(this.onTimerCallback, gcPeriod);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update just force all properties to be updated to match the most current
|
||||||
|
// root URL.
|
||||||
|
TabContext.prototype.update = function() {
|
||||||
|
if ( this.stack.length === 0 ) {
|
||||||
|
this.rawURL = this.normalURL = this.rootHostname = this.rootDomain = '';
|
||||||
|
} else {
|
||||||
|
this.rawURL = this.stack[this.stack.length - 1];
|
||||||
|
this.normalURL = µb.normalizePageURL(this.tabId, this.rawURL);
|
||||||
|
this.rootHostname = µb.URI.hostnameFromURI(this.normalURL);
|
||||||
|
this.rootDomain = µb.URI.domainFromHostname(this.rootHostname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called whenever a candidate root URL is spotted for the tab.
|
||||||
|
TabContext.prototype.push = function(url) {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stack.push(url);
|
||||||
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Called when a former push is a false positive:
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/516
|
||||||
|
TabContext.prototype.unpush = function(url) {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We are not going to unpush if there is no other candidate, the
|
||||||
|
// point of unpush is to make space for a better candidate.
|
||||||
|
if ( this.stack.length === 1 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pos = this.stack.indexOf(url);
|
||||||
|
if ( pos === -1 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stack.splice(pos, 1);
|
||||||
|
if ( this.stack.length === 0 ) {
|
||||||
|
this.destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( pos !== this.stack.length ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
// This tells that the url is definitely the one to be associated with the
|
||||||
|
// tab, there is no longer any ambiguity about which root URL is really
|
||||||
|
// sitting in which tab.
|
||||||
|
TabContext.prototype.commit = function(url) {
|
||||||
|
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.stack = [url];
|
||||||
|
this.update();
|
||||||
|
};
|
||||||
|
|
||||||
|
// These are to be used for the API of the tab context manager.
|
||||||
|
|
||||||
|
var push = function(tabId, url) {
|
||||||
|
var entry = tabContexts[tabId];
|
||||||
|
if ( entry === undefined ) {
|
||||||
|
entry = new TabContext(tabId);
|
||||||
|
entry.autodestroy();
|
||||||
|
}
|
||||||
|
entry.push(url);
|
||||||
|
mostRecentRootDocURL = url;
|
||||||
|
mostRecentRootDocURLTimestamp = Date.now();
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find a tab context for a specific tab. If none is found, attempt to
|
||||||
|
// fix this. When all fail, the behind-the-scene context is returned.
|
||||||
|
var lookup = function(tabId, url) {
|
||||||
|
var entry;
|
||||||
|
if ( url !== undefined ) {
|
||||||
|
entry = push(tabId, url);
|
||||||
|
} else {
|
||||||
|
entry = tabContexts[tabId];
|
||||||
|
}
|
||||||
|
if ( entry !== undefined ) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/1025
|
||||||
|
// Google Hangout popup opens without a root frame. So for now we will
|
||||||
|
// just discard that best-guess root frame if it is too far in the
|
||||||
|
// future, at which point it ceases to be a "best guess".
|
||||||
|
if ( mostRecentRootDocURL !== '' && mostRecentRootDocURLTimestamp + 500 < Date.now() ) {
|
||||||
|
mostRecentRootDocURL = '';
|
||||||
|
}
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
||||||
|
// Not a behind-the-scene request, yet no page store found for the
|
||||||
|
// tab id: we will thus bind the last-seen root document to the
|
||||||
|
// unbound tab. It's a guess, but better than ending up filtering
|
||||||
|
// nothing at all.
|
||||||
|
if ( mostRecentRootDocURL !== '' ) {
|
||||||
|
return push(tabId, mostRecentRootDocURL);
|
||||||
|
}
|
||||||
|
// If all else fail at finding a page store, re-categorize the
|
||||||
|
// request as behind-the-scene. At least this ensures that ultimately
|
||||||
|
// the user can still inspect/filter those net requests which were
|
||||||
|
// about to fall through the cracks.
|
||||||
|
// Example: Chromium + case #12 at
|
||||||
|
// http://raymondhill.net/ublock/popup.html
|
||||||
|
return tabContexts[vAPI.noTabId];
|
||||||
|
};
|
||||||
|
|
||||||
|
var commit = function(tabId, url) {
|
||||||
|
var entry = tabContexts[tabId];
|
||||||
|
if ( entry === undefined ) {
|
||||||
|
entry = push(tabId, url);
|
||||||
|
} else {
|
||||||
|
entry.commit(url);
|
||||||
|
}
|
||||||
|
return entry;
|
||||||
|
};
|
||||||
|
|
||||||
|
var unpush = function(tabId, url) {
|
||||||
|
var entry = tabContexts[tabId];
|
||||||
|
if ( entry !== undefined ) {
|
||||||
|
entry.unpush(url);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var destroy = function(tabId) {
|
||||||
|
var entry = tabContexts[tabId];
|
||||||
|
if ( entry !== undefined ) {
|
||||||
|
entry.destroy();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var exists = function(tabId) {
|
||||||
|
return tabContexts[tabId] !== undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Behind-the-scene tab context
|
||||||
|
(function() {
|
||||||
|
var entry = new TabContext(vAPI.noTabId);
|
||||||
|
entry.stack.push('');
|
||||||
|
entry.rawURL = '';
|
||||||
|
entry.normalURL = µb.normalizePageURL(entry.tabId);
|
||||||
|
entry.rootHostname = µb.URI.hostnameFromURI(entry.normalURL);
|
||||||
|
entry.rootDomain = µb.URI.domainFromHostname(entry.rootHostname);
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Context object, typically to be used to feed filtering engines.
|
||||||
|
var Context = function(tabId) {
|
||||||
|
var tabContext = lookup(tabId);
|
||||||
|
this.rootHostname = tabContext.rootHostname;
|
||||||
|
this.rootDomain = tabContext.rootDomain;
|
||||||
|
this.pageHostname =
|
||||||
|
this.pageDomain =
|
||||||
|
this.requestURL =
|
||||||
|
this.requestHostname =
|
||||||
|
this.requestDomain = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
var createContext = function(tabId) {
|
||||||
|
return new Context(tabId);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
push: push,
|
||||||
|
unpush: unpush,
|
||||||
|
commit: commit,
|
||||||
|
lookup: lookup,
|
||||||
|
destroy: destroy,
|
||||||
|
exists: exists,
|
||||||
|
createContext: createContext
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
// When the DOM content of root frame is loaded, this means the tab
|
||||||
|
// content has changed.
|
||||||
|
|
||||||
|
vAPI.tabs.onNavigation = function(details) {
|
||||||
|
if ( details.frameId !== 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var tabContext = µb.tabContextManager.commit(details.tabId, details.url);
|
||||||
|
var pageStore = µb.bindTabToPageStats(details.tabId, 'afterNavigate');
|
||||||
|
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/630
|
||||||
|
// The hostname of the bound document must always be present in the
|
||||||
|
// mini-matrix. That's the best place I could find for the fix, all other
|
||||||
|
// options had bad side-effects or complications.
|
||||||
|
// TODO: Eventually, we will have to use an API to check whether a scheme
|
||||||
|
// is supported as I suspect we are going to start to see `ws`, `wss`
|
||||||
|
// as well soon.
|
||||||
|
if ( pageStore && tabContext.rawURL.lastIndexOf('http', 0) === 0 ) {
|
||||||
|
pageStore.hostnameToCountMap[tabContext.rootHostname] = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// It may happen the URL in the tab changes, while the page's document
|
||||||
|
// stays the same (for instance, Google Maps). Without this listener,
|
||||||
|
// the extension icon won't be properly refreshed.
|
||||||
|
|
||||||
|
vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
|
||||||
|
if ( !tab.url || tab.url === '' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ( !changeInfo.url ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
µb.tabContextManager.commit(tabId, changeInfo.url);
|
||||||
|
µb.bindTabToPageStats(tabId, 'tabUpdated');
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
vAPI.tabs.onClosed = function(tabId) {
|
||||||
|
if ( tabId < 0 ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
µb.unbindTabFromPageStats(tabId);
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/297
|
||||||
|
|
||||||
|
vAPI.tabs.onPopup = function(details) {
|
||||||
|
// console.debug('vAPI.tabs.onPopup: details = %o', details);
|
||||||
|
|
||||||
|
var tabContext = µb.tabContextManager.lookup(details.openerTabId);
|
||||||
|
var openerURL = '';
|
||||||
|
if ( tabContext.tabId === details.openerTabId ) {
|
||||||
|
openerURL = tabContext.normalURL;
|
||||||
|
}
|
||||||
|
if ( openerURL === '' ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var µburi = µb.URI;
|
||||||
|
var openerHostname = µburi.hostnameFromURI(openerURL);
|
||||||
|
var openerDomain = µburi.domainFromHostname(openerHostname);
|
||||||
|
|
||||||
|
var targetURL = details.targetURL;
|
||||||
|
|
||||||
|
var context = {
|
||||||
|
pageHostname: openerHostname,
|
||||||
|
pageDomain: openerDomain,
|
||||||
|
rootHostname: openerHostname,
|
||||||
|
rootDomain: openerDomain,
|
||||||
|
requestURL: targetURL,
|
||||||
|
requestHostname: µb.URI.hostnameFromURI(targetURL),
|
||||||
|
requestType: 'popup'
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = '';
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/323
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/1142
|
||||||
|
// If popup OR opener URL is whitelisted, do not block the popup
|
||||||
|
if (
|
||||||
|
result === '' &&
|
||||||
|
µb.getNetFilteringSwitch(openerURL) &&
|
||||||
|
µb.getNetFilteringSwitch(targetURL)
|
||||||
|
) {
|
||||||
|
result = µb.staticNetFilteringEngine.matchStringExactType(context, targetURL, 'popup');
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/chrisaljoudi/uBlock/issues/91
|
||||||
|
var pageStore = µb.pageStoreFromTabId(details.openerTabId);
|
||||||
|
if ( pageStore ) {
|
||||||
|
pageStore.logRequest(context, result);
|
||||||
|
}
|
||||||
|
if ( µb.userSettings.showIconBadge ) {
|
||||||
|
µb.updateBadgeAsync(details.openerTabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not blocked
|
||||||
|
if ( µb.isAllowResult(result) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blocked
|
||||||
|
|
||||||
|
// It is a popup, block and remove the tab.
|
||||||
|
if(details.targetTabId !== "preempt") {
|
||||||
|
µb.unbindTabFromPageStats(details.targetTabId);
|
||||||
|
vAPI.tabs.remove(details.targetTabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
vAPI.tabs.registerListeners();
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Create an entry for the tab if it doesn't exist.
|
// Create an entry for the tab if it doesn't exist.
|
||||||
|
|
||||||
µb.bindTabToPageStats = function(tabId, pageURL, context) {
|
µb.bindTabToPageStats = function(tabId, context) {
|
||||||
this.updateBadgeAsync(tabId);
|
this.updateBadgeAsync(tabId);
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/httpswitchboard/issues/303
|
if ( µb.tabContextManager.exists(tabId) === false ) {
|
||||||
// Normalize page URL
|
|
||||||
var normalURL = this.normalizePageURL(tabId, pageURL);
|
|
||||||
|
|
||||||
// Do not create a page store for URLs which are of no interests
|
|
||||||
if ( normalURL === '' ) {
|
|
||||||
this.unbindTabFromPageStats(tabId);
|
this.unbindTabFromPageStats(tabId);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -217,7 +504,7 @@ vAPI.tabs.registerListeners();
|
|||||||
|
|
||||||
// Tab is not bound
|
// Tab is not bound
|
||||||
if ( !pageStore ) {
|
if ( !pageStore ) {
|
||||||
return this.pageStores[tabId] = this.PageStore.factory(tabId, pageURL, normalURL);
|
return this.pageStores[tabId] = this.PageStore.factory(tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/516
|
// https://github.com/chrisaljoudi/uBlock/issues/516
|
||||||
@ -229,11 +516,13 @@ vAPI.tabs.registerListeners();
|
|||||||
// Rebind according to context. We rebind even if the URL did not change,
|
// Rebind according to context. We rebind even if the URL did not change,
|
||||||
// as maybe the tab was force-reloaded, in which case the page stats must
|
// as maybe the tab was force-reloaded, in which case the page stats must
|
||||||
// be all reset.
|
// be all reset.
|
||||||
pageStore.reuse(pageURL, normalURL, context);
|
pageStore.reuse(context);
|
||||||
|
|
||||||
return pageStore;
|
return pageStore;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
µb.unbindTabFromPageStats = function(tabId) {
|
µb.unbindTabFromPageStats = function(tabId) {
|
||||||
//console.debug('µBlock> unbindTabFromPageStats(%d)', tabId);
|
//console.debug('µBlock> unbindTabFromPageStats(%d)', tabId);
|
||||||
var pageStore = this.pageStores[tabId];
|
var pageStore = this.pageStores[tabId];
|
||||||
@ -243,20 +532,6 @@ vAPI.tabs.registerListeners();
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µb.pageUrlFromTabId = function(tabId) {
|
|
||||||
var pageStore = this.pageStores[tabId];
|
|
||||||
return pageStore ? pageStore.pageURL : '';
|
|
||||||
};
|
|
||||||
|
|
||||||
µb.pageUrlFromPageStats = function(pageStats) {
|
|
||||||
if ( pageStats ) {
|
|
||||||
return pageStats.pageURL;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
};
|
|
||||||
|
|
||||||
µb.pageStoreFromTabId = function(tabId) {
|
µb.pageStoreFromTabId = function(tabId) {
|
||||||
return this.pageStores[tabId];
|
return this.pageStores[tabId];
|
||||||
};
|
};
|
||||||
@ -265,11 +540,7 @@ vAPI.tabs.registerListeners();
|
|||||||
|
|
||||||
// Permanent page store for behind-the-scene requests. Must never be removed.
|
// Permanent page store for behind-the-scene requests. Must never be removed.
|
||||||
|
|
||||||
µb.pageStores[vAPI.noTabId] = µb.PageStore.factory(
|
µb.pageStores[vAPI.noTabId] = µb.PageStore.factory(vAPI.noTabId);
|
||||||
vAPI.noTabId,
|
|
||||||
'',
|
|
||||||
µb.normalizePageURL(vAPI.noTabId)
|
|
||||||
);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -300,7 +571,7 @@ var pageStoreJanitor = function() {
|
|||||||
for ( var i = pageStoreJanitorSampleAt; i < n; i++ ) {
|
for ( var i = pageStoreJanitorSampleAt; i < n; i++ ) {
|
||||||
tabId = tabIds[i];
|
tabId = tabIds[i];
|
||||||
// Do not remove behind-the-scene page store
|
// Do not remove behind-the-scene page store
|
||||||
if ( vAPI.isNoTabId(tabId) ) {
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
checkTab(tabId);
|
checkTab(tabId);
|
||||||
|
@ -33,16 +33,6 @@
|
|||||||
|
|
||||||
var exports = {};
|
var exports = {};
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
|
||||||
// This is to be used as last-resort fallback in case a tab is found to not
|
|
||||||
// be bound while network requests are fired for the tab.
|
|
||||||
|
|
||||||
var mostRecentRootDocURLTimestamp = 0;
|
|
||||||
var mostRecentRootDocURL = '';
|
|
||||||
|
|
||||||
|
|
||||||
var documentWhitelists = Object.create(null);
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
// Intercept and filter web requests.
|
// Intercept and filter web requests.
|
||||||
@ -62,40 +52,22 @@ var onBeforeRequest = function(details) {
|
|||||||
|
|
||||||
// Special treatment: behind-the-scene requests
|
// Special treatment: behind-the-scene requests
|
||||||
var tabId = details.tabId;
|
var tabId = details.tabId;
|
||||||
if ( vAPI.isNoTabId(tabId) ) {
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
return onBeforeBehindTheSceneRequest(details);
|
return onBeforeBehindTheSceneRequest(details);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup the page store associated with this tab id.
|
// Lookup the page store associated with this tab id.
|
||||||
var µb = µBlock;
|
var µb = µBlock;
|
||||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
if ( (Date.now() - mostRecentRootDocURLTimestamp) >= 500 ) {
|
|
||||||
mostRecentRootDocURL = '';
|
|
||||||
}
|
|
||||||
if ( !pageStore ) {
|
if ( !pageStore ) {
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
var tabContext = µb.tabContextManager.lookup(tabId);
|
||||||
// Not a behind-the-scene request, yet no page store found for the
|
if ( vAPI.isBehindTheSceneTabId(tabContext.tabId) ) {
|
||||||
// tab id: we will thus bind the last-seen root document to the
|
|
||||||
// unbound tab. It's a guess, but better than ending up filtering
|
|
||||||
// nothing at all.
|
|
||||||
if ( mostRecentRootDocURL !== '' ) {
|
|
||||||
vAPI.tabs.onNavigation({ tabId: tabId, frameId: 0, url: mostRecentRootDocURL });
|
|
||||||
pageStore = µb.pageStoreFromTabId(tabId);
|
|
||||||
}
|
|
||||||
// If all else fail at finding a page store, re-categorize the
|
|
||||||
// request as behind-the-scene. At least this ensures that ultimately
|
|
||||||
// the user can still inspect/filter those net requests which were
|
|
||||||
// about to fall through the cracks.
|
|
||||||
// Example: Chromium + case #12 at
|
|
||||||
// http://raymondhill.net/ublock/popup.html
|
|
||||||
if ( !pageStore ) {
|
|
||||||
return onBeforeBehindTheSceneRequest(details);
|
return onBeforeBehindTheSceneRequest(details);
|
||||||
}
|
}
|
||||||
|
vAPI.tabs.onNavigation({ tabId: tabId, frameId: 0, url: tabContext.rawURL });
|
||||||
|
pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/114
|
|
||||||
var requestContext = pageStore;
|
|
||||||
var frameStore;
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/886
|
// https://github.com/chrisaljoudi/uBlock/issues/886
|
||||||
// For requests of type `sub_frame`, the parent frame id must be used
|
// For requests of type `sub_frame`, the parent frame id must be used
|
||||||
// to lookup the proper context:
|
// to lookup the proper context:
|
||||||
@ -105,25 +77,22 @@ var onBeforeRequest = function(details) {
|
|||||||
// > (ref: https://developer.chrome.com/extensions/webRequest)
|
// > (ref: https://developer.chrome.com/extensions/webRequest)
|
||||||
var isFrame = requestType === 'sub_frame';
|
var isFrame = requestType === 'sub_frame';
|
||||||
var frameId = isFrame ? details.parentFrameId : details.frameId;
|
var frameId = isFrame ? details.parentFrameId : details.frameId;
|
||||||
if ( frameId > 0 ) {
|
|
||||||
if ( frameStore = pageStore.getFrame(frameId) ) {
|
// https://github.com/chrisaljoudi/uBlock/issues/114
|
||||||
requestContext = frameStore;
|
var requestContext = pageStore.createContextFromFrameId(frameId);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup context and evaluate
|
// Setup context and evaluate
|
||||||
var requestURL = details.url;
|
var requestURL = details.url;
|
||||||
requestContext.requestURL = requestURL;
|
requestContext.requestURL = requestURL;
|
||||||
requestContext.requestHostname = details.hostname;
|
requestContext.requestHostname = details.hostname;
|
||||||
requestContext.requestType = requestType;
|
requestContext.requestType = requestType;
|
||||||
if(!isFrame && mostRecentRootDocURL !== '') {
|
|
||||||
requestContext.pageHostname = µb.URI.hostnameFromURI(mostRecentRootDocURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = pageStore.filterRequest(requestContext);
|
var result = pageStore.filterRequest(requestContext);
|
||||||
|
|
||||||
// Possible outcomes: blocked, allowed-passthru, allowed-mirror
|
// Possible outcomes: blocked, allowed-passthru, allowed-mirror
|
||||||
|
|
||||||
|
pageStore.logRequest(requestContext, result);
|
||||||
|
|
||||||
// Not blocked
|
// Not blocked
|
||||||
if ( µb.isAllowResult(result) ) {
|
if ( µb.isAllowResult(result) ) {
|
||||||
//console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result);
|
//console.debug('traffic.js > onBeforeRequest(): ALLOW "%s" (%o) because "%s"', details.url, details, result);
|
||||||
@ -138,27 +107,12 @@ var onBeforeRequest = function(details) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://code.google.com/p/chromium/issues/detail?id=387198
|
|
||||||
// Not all redirects will succeed, until bug above is fixed.
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/540
|
|
||||||
// Disabling local mirroring for the time being
|
|
||||||
//var redirectURL = pageStore.toMirrorURL(requestURL);
|
|
||||||
//if ( redirectURL !== '' ) {
|
|
||||||
// pageStore.logRequest(requestContext, 'ma:');
|
|
||||||
//console.debug('traffic.js > "%s" redirected to "%s..."', requestURL.slice(0, 50), redirectURL.slice(0, 50));
|
|
||||||
// return { redirectUrl: redirectURL };
|
|
||||||
//}
|
|
||||||
|
|
||||||
pageStore.logRequest(requestContext, result);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocked
|
// Blocked
|
||||||
//console.debug('traffic.js > onBeforeRequest(): BLOCK "%s" (%o) because "%s"', details.url, details, result);
|
//console.debug('traffic.js > onBeforeRequest(): BLOCK "%s" (%o) because "%s"', details.url, details, result);
|
||||||
|
|
||||||
pageStore.logRequest(requestContext, result);
|
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649
|
// https://github.com/chrisaljoudi/uBlock/issues/905#issuecomment-76543649
|
||||||
// No point updating the badge if it's not being displayed.
|
// No point updating the badge if it's not being displayed.
|
||||||
if ( µb.userSettings.showIconBadge ) {
|
if ( µb.userSettings.showIconBadge ) {
|
||||||
@ -175,16 +129,12 @@ var onBeforeRequest = function(details) {
|
|||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
var onBeforeRootFrameRequest = function(details) {
|
var onBeforeRootFrameRequest = function(details) {
|
||||||
|
var tabId = details.tabId;
|
||||||
var requestURL = details.url;
|
var requestURL = details.url;
|
||||||
|
|
||||||
mostRecentRootDocURL = requestURL;
|
|
||||||
mostRecentRootDocURLTimestamp = Date.now();
|
|
||||||
|
|
||||||
// Special handling for root document.
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
|
||||||
// This must be executed regardless of whether the request is
|
|
||||||
// behind-the-scene
|
|
||||||
var µb = µBlock;
|
var µb = µBlock;
|
||||||
|
|
||||||
|
µb.tabContextManager.push(tabId, requestURL);
|
||||||
|
|
||||||
var requestHostname = details.hostname;
|
var requestHostname = details.hostname;
|
||||||
var requestDomain = µb.URI.domainFromHostname(requestHostname);
|
var requestDomain = µb.URI.domainFromHostname(requestHostname);
|
||||||
var context = {
|
var context = {
|
||||||
@ -198,60 +148,11 @@ var onBeforeRootFrameRequest = function(details) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var result = '';
|
var result = '';
|
||||||
|
var pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
|
||||||
// If the site is whitelisted, disregard strict blocking
|
|
||||||
if ( µb.getNetFilteringSwitch(requestURL) === false ) {
|
|
||||||
result = 'ua:whitelisted';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Permanently unrestricted?
|
|
||||||
if ( result === '' && µb.hnSwitches.evaluateZ('dontBlockDoc', requestHostname) ) {
|
|
||||||
result = 'ua:dontBlockDoc true';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily whitelisted?
|
|
||||||
var obsolete = documentWhitelists[requestHostname];
|
|
||||||
if ( obsolete !== undefined ) {
|
|
||||||
if ( obsolete > Date.now() ) {
|
|
||||||
if ( result === '' ) {
|
|
||||||
result = 'ta:*' + ' ' + requestHostname + ' doc allow';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
delete documentWhitelists[requestHostname];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filtering
|
|
||||||
if ( result === '' ) {
|
|
||||||
result = µb.staticNetFilteringEngine.matchString(context);
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1128
|
|
||||||
// Do not block if the match begins after the hostname.
|
|
||||||
if ( result !== '' ) {
|
|
||||||
result = toBlockDocResult(requestURL, requestHostname, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log
|
|
||||||
var pageStore = µb.bindTabToPageStats(details.tabId, requestURL, 'beforeRequest');
|
|
||||||
if ( pageStore ) {
|
if ( pageStore ) {
|
||||||
pageStore.logRequest(context, result);
|
pageStore.logRequest(context, result);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
// Not blocked
|
|
||||||
if ( µb.isAllowResult(result) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocked
|
|
||||||
var query = btoa(JSON.stringify({
|
|
||||||
url: requestURL,
|
|
||||||
hn: requestHostname,
|
|
||||||
why: result
|
|
||||||
}));
|
|
||||||
|
|
||||||
vAPI.tabs.replace(details.tabId, vAPI.getURL('document-blocked.html?details=') + query);
|
|
||||||
|
|
||||||
return { cancel: true };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -309,9 +210,10 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
pageStore.requestURL = details.url;
|
var context = pageStore.createContextFromPage();
|
||||||
pageStore.requestHostname = details.hostname;
|
context.requestURL = details.url;
|
||||||
pageStore.requestType = details.type;
|
context.requestHostname = details.hostname;
|
||||||
|
context.requestType = details.type;
|
||||||
|
|
||||||
// Blocking behind-the-scene requests can break a lot of stuff: prevent
|
// Blocking behind-the-scene requests can break a lot of stuff: prevent
|
||||||
// browser updates, prevent extension updates, prevent extensions from
|
// browser updates, prevent extension updates, prevent extensions from
|
||||||
@ -319,10 +221,10 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||||||
// So we filter if and only if the "advanced user" mode is selected
|
// So we filter if and only if the "advanced user" mode is selected
|
||||||
var result = '';
|
var result = '';
|
||||||
if ( µb.userSettings.advancedUserEnabled ) {
|
if ( µb.userSettings.advancedUserEnabled ) {
|
||||||
result = pageStore.filterRequestNoCache(pageStore);
|
result = pageStore.filterRequestNoCache(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
pageStore.logRequest(pageStore, result);
|
pageStore.logRequest(context, result);
|
||||||
|
|
||||||
// Not blocked
|
// Not blocked
|
||||||
if ( µb.isAllowResult(result) ) {
|
if ( µb.isAllowResult(result) ) {
|
||||||
@ -343,58 +245,68 @@ var onBeforeBehindTheSceneRequest = function(details) {
|
|||||||
var onHeadersReceived = function(details) {
|
var onHeadersReceived = function(details) {
|
||||||
// Do not interfere with behind-the-scene requests.
|
// Do not interfere with behind-the-scene requests.
|
||||||
var tabId = details.tabId;
|
var tabId = details.tabId;
|
||||||
if ( vAPI.isNoTabId(tabId) ) {
|
if ( vAPI.isBehindTheSceneTabId(tabId) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestURL = details.url;
|
// Special handling for root document.
|
||||||
|
if ( details.type === 'main_frame' ) {
|
||||||
|
return onRootFrameHeadersReceived(details);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we reach this point, we are dealing with a sub_frame
|
||||||
|
|
||||||
// Lookup the page store associated with this tab id.
|
// Lookup the page store associated with this tab id.
|
||||||
var µb = µBlock;
|
var µb = µBlock;
|
||||||
var pageStore = µb.pageStoreFromTabId(tabId);
|
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
if ( !pageStore ) {
|
if ( !pageStore ) {
|
||||||
if ( details.type === 'main_frame' ) {
|
return;
|
||||||
pageStore = µb.bindTabToPageStats(tabId, requestURL, 'beforeRequest');
|
}
|
||||||
}
|
// Frame id of frame request is the their own id, while the request is made
|
||||||
if ( !pageStore ) {
|
// in the context of the parent.
|
||||||
return;
|
var context = pageStore.createContextFromFrameId(details.parentFrameId);
|
||||||
}
|
context.requestURL = details.url + '{inline-script}';
|
||||||
|
context.requestHostname = details.hostname;
|
||||||
|
context.requestType = 'inline-script';
|
||||||
|
|
||||||
|
var result = pageStore.filterRequestNoCache(context);
|
||||||
|
|
||||||
|
pageStore.logRequest(context, result);
|
||||||
|
|
||||||
|
// Don't block
|
||||||
|
if ( µb.isAllowResult(result) ) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/384
|
µb.updateBadgeAsync(tabId);
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/540
|
|
||||||
// Disabling local mirroring for the time being
|
|
||||||
//if ( details.parentFrameId === -1 ) {
|
|
||||||
// pageStore.skipLocalMirroring = headerStartsWith(details.responseHeaders, 'content-security-policy') !== '';
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
details.responseHeaders.push({
|
||||||
|
'name': 'Content-Security-Policy',
|
||||||
|
'value': "script-src 'unsafe-eval' *"
|
||||||
|
});
|
||||||
|
|
||||||
|
return { 'responseHeaders': details.responseHeaders };
|
||||||
|
};
|
||||||
|
var onRootFrameHeadersReceived = function(details) {
|
||||||
|
var tabId = details.tabId;
|
||||||
|
var requestURL = details.url;
|
||||||
var requestHostname = details.hostname;
|
var requestHostname = details.hostname;
|
||||||
|
var µb = µBlock;
|
||||||
|
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/525
|
// Check if the main_frame is a download
|
||||||
// When we are dealing with the root frame, due to fix to issue #516, it
|
// ...
|
||||||
// is likely the root frame has not been bound yet to the tab, and thus
|
if ( headerValue(details.responseHeaders, 'content-disposition').lastIndexOf('attachment', 0) === 0 ) {
|
||||||
// we could end up using the context of the previous page for filtering.
|
µb.tabContextManager.unpush(tabId, requestURL);
|
||||||
// So when the request is that of a root frame, simply create an
|
|
||||||
// artificial context, this will ensure we are properly filtering
|
|
||||||
// inline scripts.
|
|
||||||
var context;
|
|
||||||
if ( details.parentFrameId === -1 ) {
|
|
||||||
var contextDomain = µb.URI.domainFromHostname(requestHostname);
|
|
||||||
context = {
|
|
||||||
rootHostname: requestHostname,
|
|
||||||
rootDomain: contextDomain,
|
|
||||||
pageHostname: requestHostname,
|
|
||||||
pageDomain: contextDomain,
|
|
||||||
preNavigationHeader: true
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
context = pageStore;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Concatenating with '{inline-script}' so that the network request cache
|
// Lookup the page store associated with this tab id.
|
||||||
// can distinguish from the document itself
|
var pageStore = µb.pageStoreFromTabId(tabId);
|
||||||
// The cache should do whatever it takes to not confuse same
|
if ( !pageStore ) {
|
||||||
// URLs-different type
|
pageStore = µb.bindTabToPageStats(tabId, 'beforeRequest');
|
||||||
|
}
|
||||||
|
// I can't think of how pageStore could be null at this point.
|
||||||
|
|
||||||
|
var context = pageStore.createContextFromPage();
|
||||||
context.requestURL = requestURL + '{inline-script}';
|
context.requestURL = requestURL + '{inline-script}';
|
||||||
context.requestHostname = requestHostname;
|
context.requestHostname = requestHostname;
|
||||||
context.requestType = 'inline-script';
|
context.requestType = 'inline-script';
|
||||||
@ -420,6 +332,18 @@ var onHeadersReceived = function(details) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
var headerValue = function(headers, name) {
|
||||||
|
var i = headers.length;
|
||||||
|
while ( i-- ) {
|
||||||
|
if ( headers[i].name.toLowerCase() === name ) {
|
||||||
|
return headers[i].value.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
vAPI.net.onBeforeRequest = {
|
vAPI.net.onBeforeRequest = {
|
||||||
urls: [
|
urls: [
|
||||||
'http://*/*',
|
'http://*/*',
|
||||||
@ -458,18 +382,6 @@ vAPI.net.registerListeners();
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
exports.temporarilyWhitelistDocument = function(url) {
|
|
||||||
var µb = µBlock;
|
|
||||||
var hostname = µb.URI.hostnameFromURI(url);
|
|
||||||
if ( hostname === '' ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
documentWhitelists[hostname] = Date.now() + 60 * 1000;
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
return exports;
|
return exports;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
@ -318,12 +318,4 @@ var matchWhitelistDirective = function(url, hostname, directive) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.toggleHostnameSwitch = function(details) {
|
|
||||||
if ( this.hnSwitches.toggleZ(details.name, details.hostname, details.state) ) {
|
|
||||||
this.saveHostnameSwitches();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
})();
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
|
|
||||||
µBlock - a browser extension to block requests.
|
|
||||||
Copyright (C) 2014 Raymond Hill
|
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
|
||||||
|
|
||||||
Home: https://github.com/chrisaljoudi/uBlock
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* global µBlock */
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
µBlock.XAL = (function(){
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
var exports = {};
|
|
||||||
var noopFunc = function(){};
|
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
exports.keyvalSetOne = function(key, val, callback) {
|
|
||||||
var bin = {};
|
|
||||||
bin[key] = val;
|
|
||||||
vAPI.storage.set(bin, callback || noopFunc);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
exports.keyvalSetMany = function(dict, callback) {
|
|
||||||
vAPI.storage.set(dict, callback || noopFunc);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
exports.keyvalRemoveOne = function(key, callback) {
|
|
||||||
vAPI.storage.remove(key, callback || noopFunc);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
exports.keyvalRemoveAll = function(callback) {
|
|
||||||
vAPI.storage.clear(callback || noopFunc);
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
return exports;
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
})();
|
|
Loading…
Reference in New Issue
Block a user