fix #2067 (experimental) + support for hidden settings

This commit is contained in:
gorhill 2016-11-03 11:20:47 -04:00
parent 18d1c4809e
commit 8c3da95d65
20 changed files with 437 additions and 49 deletions

View file

@ -21,6 +21,8 @@
// For background page or non-background pages
/* exported objectAssign */
'use strict';
/******************************************************************************/
@ -57,6 +59,19 @@ if ( String.prototype.endsWith instanceof Function === false ) {
/******************************************************************************/
// As per MDN, Object.assign appeared first in Chromium 45.
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Browser_compatibility
var objectAssign = Object.assign || function(target, source) {
var keys = Object.keys(source);
for ( var i = 0, n = keys.length, key; i < n; i++ ) {
key = keys[i];
target[key] = source[key];
}
};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/1070
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set#Browser_compatibility
// This polyfill is designed to fulfill *only* what uBlock Origin needs -- this

View file

@ -1196,7 +1196,9 @@ vAPI.onLoadAllCompleted = function() {
µb.tabContextManager.commit(tab.id, tab.url);
µb.bindTabToPageStats(tab.id);
// https://github.com/chrisaljoudi/uBlock/issues/129
scriptStart(tab.id);
if ( /^https?:\/\//.test(tab.url) ) {
scriptStart(tab.id);
}
}
};

View file

@ -468,7 +468,7 @@ vAPI.messaging = {
if ( listeners === undefined ) {
return;
}
var pos = this.listeners.indexOf(callback);
var pos = listeners.indexOf(callback);
if ( pos === -1 ) {
console.error('Listener not found on channel "%s"', channelName);
return;

View file

@ -21,8 +21,24 @@
// For background page or non-background pages
/* exported objectAssign */
'use strict';
/******************************************************************************/
/******************************************************************************/
// As per MDN, Object.assign appeared first in Firefox 34.
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Browser_compatibility
var objectAssign = Object.assign || function(target, source) {
var keys = Object.keys(source);
for ( var i = 0, n = keys.length, key; i < n; i++ ) {
key = keys[i];
target[key] = source[key];
}
};
/******************************************************************************/
// Patching for Pale Moon which does not implement ES6 Set/Map.

View file

@ -39,6 +39,10 @@
"message":"About",
"description":"appears as tab name in dashboard"
},
"advancedSettingsPageName":{
"message":"Advanced settings",
"description":"Title for the advanced settings page"
},
"popupPowerSwitchInfo":{
"message":"Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page.",
"description":"English: Click: disable\/enable uBlock₀ for this site.\n\nCtrl+click: disable uBlock₀ only on this page."
@ -204,8 +208,12 @@
"description": ""
},
"settingsAdvancedUserPrompt":{
"message":"I am an advanced user (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Advanced-user-features'>Required reading<\/a>)",
"description":"English: "
"message":"I am an advanced user (<a href='https:\/\/github.com\/gorhill\/uBlock\/wiki\/Advanced-user-features'>required reading<\/a>)",
"description":""
},
"settingsAdvancedUserSettings":{
"message":"advanced settings",
"description":"For the tooltip of a link which gives access to advanced settings"
},
"settingsPrefetchingDisabledPrompt":{
"message":"Disable pre-fetching (to prevent any connection for blocked network requests)",
@ -671,13 +679,21 @@
"message": "This device name:",
"description": "used as a prompt for the user to provide a custom device name"
},
"advancedSettingsWarning": {
"message": "Warning! Change these advanced settings at your own risk.",
"description": "A warning to users at the top of 'Advanced settings' page"
},
"genericSubmit": {
"message": "Submit",
"description": "for generic 'submit' buttons"
"description": "for generic 'Submit' buttons"
},
"genericApplyChanges": {
"message": "Apply changes",
"description": "for generic 'Apply changes' buttons"
},
"genericRevert": {
"message": "Revert",
"description": "for generic 'revert' buttons"
"description": "for generic 'Revert' buttons"
},
"genericBytes": {
"message": "bytes",

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title data-i18n="advancedSettingsPageName"></title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/dashboard-common.css">
<link rel="stylesheet" type="text/css" href="css/advanced-settings.css">
<link rel="shortcut icon" type="image/png" href="img/icon_16.png"/>
</head>
<body>
<p><span data-i18n="advancedSettingsWarning"></span> <a class="fa info important" href="https://github.com/gorhill/uBlock/wiki/Advanced-user-features" target="_blank">&#xf05a;</a>
<p>
<button id="advancedSettingsApply" class="custom important" type="button" disabled="true" data-i18n="genericApplyChanges"></button>&ensp;
<p><textarea id="advancedSettings" dir="auto" spellcheck="false"></textarea>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
<script src="js/udom.js"></script>
<script src="js/i18n.js"></script>
<script src="js/dashboard-common.js"></script>
<script src="js/advanced-settings.js"></script>
</body>
</html>

View file

@ -0,0 +1,15 @@
div > p:first-child {
margin-top: 0;
}
div > p:last-child {
margin-bottom: 0;
}
textarea {
box-sizing: border-box;
font-size: smaller;
height: 60vh;
text-align: left;
white-space: pre;
width: 100%;
word-wrap: normal;
}

View file

@ -18,12 +18,15 @@ ul#userSettings .subgroup > span {
font-size: larger;
font-weight: bold;
}
#advanced-user-enabled ~ a.fa {
display: none;
}
body.advancedUser #advanced-user-enabled ~ a.fa {
display: inline;
}
#localData > ul > li {
margin-top: 1em;
}
#localData > ul > li > ul > li:nth-of-type(2) {
font-family: monospace;
}
#experimental-enabled {
margin-top: 1em;
}

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<a href=""></a>
<script src="js/vapi-common.js"></script>
<script src="js/vapi-client.js"></script>
<script src="js/document-suspended.js"></script>
</body>
</html>

114
src/js/advanced-settings.js Normal file
View file

@ -0,0 +1,114 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2016 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
/* global uDom */
'use strict';
/******************************************************************************/
(function() {
/******************************************************************************/
var messaging = vAPI.messaging;
var cachedData = '';
var rawAdvancedSettings = uDom.nodeFromId('advancedSettings');
/******************************************************************************/
var hashFromAdvancedSettings = function(raw) {
return raw.trim().replace(/\s+/g, '|');
};
/******************************************************************************/
// This is to give a visual hint that the content of user blacklist has changed.
var advancedSettingsChanged = (function () {
var timer = null;
var handler = function() {
timer = null;
var changed = hashFromAdvancedSettings(rawAdvancedSettings.value) !== cachedData;
uDom.nodeFromId('advancedSettingsApply').disabled = !changed;
};
return function() {
if ( timer !== null ) {
clearTimeout(timer);
}
timer = vAPI.setTimeout(handler, 100);
};
})();
/******************************************************************************/
function renderAdvancedSettings() {
var onRead = function(raw) {
cachedData = hashFromAdvancedSettings(raw);
var pretty = [],
whitespaces = ' ',
lines = raw.split('\n'),
max = 0,
pos,
i, n = lines.length;
for ( i = 0; i < n; i++ ) {
pos = lines[i].indexOf(' ');
if ( pos > max ) {
max = pos;
}
}
for ( i = 0; i < n; i++ ) {
pos = lines[i].indexOf(' ');
pretty.push(whitespaces.slice(0, max - pos) + lines[i]);
}
rawAdvancedSettings.value = pretty.join('\n') + '\n';
advancedSettingsChanged();
rawAdvancedSettings.focus();
};
messaging.send('dashboard', { what: 'readHiddenSettings' }, onRead);
}
/******************************************************************************/
var applyChanges = function() {
messaging.send(
'dashboard',
{
what: 'writeHiddenSettings',
content: rawAdvancedSettings.value
},
renderAdvancedSettings
);
};
/******************************************************************************/
// Handle user interaction
uDom('#advancedSettings').on('input', advancedSettingsChanged);
uDom('#advancedSettingsApply').on('click', applyChanges);
renderAdvancedSettings();
/******************************************************************************/
})();

View file

@ -70,6 +70,14 @@ return {
webrtcIPAddressHidden: false
},
hiddenSettingsDefault: {
ignoreRedirectFilters: false,
ignoreScriptInjectFilters: false,
suspendTabsUntilReady: false
},
// This will be filled ASAP:
hiddenSettings: {},
// Features detection.
privacySettingsSupported: vAPI.browserSettings instanceof Object,
cloudStorageSupported: vAPI.cloud instanceof Object,

View file

@ -1344,6 +1344,7 @@ FilterContainer.prototype.createUserScriptRule = function(hash, hostname, select
FilterContainer.prototype.retrieveUserScripts = function(domain, hostname) {
if ( this.userScriptCount === 0 ) { return; }
if ( µb.hiddenSettings.ignoreScriptInjectFilters === true ) { return; }
var reng = µb.redirectEngine;
if ( !reng ) { return; }

View file

@ -1,7 +1,7 @@
/*******************************************************************************
µBlock - a browser extension to block requests.
Copyright (C) 2014 Raymond Hill
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2016 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
@ -21,12 +21,12 @@
/* global uDom */
'use strict';
/******************************************************************************/
(function() {
'use strict';
/******************************************************************************/
var resizeFrame = function() {

View file

@ -0,0 +1,46 @@
/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2016 Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
/******************************************************************************/
(function() {
var matches = /url=([^&]+)/.exec(window.location.search);
if ( matches === null ) { return; }
var onMessage = function(msg) {
if ( msg.what !== 'ublockOrigin-readyState-complete' ) {
return;
}
vAPI.messaging.removeChannelListener('document-suspended.js', onMessage);
window.location.replace(document.querySelector('body > a').href);
};
var link = document.querySelector('body > a'),
url = decodeURIComponent(matches[1]);
link.setAttribute('href', url);
link.appendChild(document.createTextNode(url));
vAPI.messaging.addChannelListener('document-suspended.js', onMessage);
})();
/******************************************************************************/

View file

@ -750,6 +750,7 @@ var backupUserData = function(callback) {
version: vAPI.app.version,
userSettings: µb.userSettings,
filterLists: {},
hiddenSettingsString: µb.stringFromHiddenSettings(),
netWhitelist: µb.stringFromWhitelist(µb.netWhitelist),
dynamicFilteringString: µb.permanentFirewall.toString(),
urlFilteringString: µb.permanentURLFiltering.toString(),
@ -800,13 +801,9 @@ var restoreUserData = function(request) {
µBlock.saveLocalSettings();
vAPI.storage.set(userData.userSettings, onCountdown);
µb.keyvalSetOne('remoteBlacklists', userData.filterLists, onCountdown);
µb.hiddenSettingsFromString(userData.hiddenSettingsString || '');
µb.keyvalSetOne('netWhitelist', userData.netWhitelist || '', onCountdown);
// With versions 0.9.2.4-, dynamic rules were saved within the
// `userSettings` object. No longer the case.
var s = userData.dynamicFilteringString || userData.userSettings.dynamicFilteringString || '';
µb.keyvalSetOne('dynamicFilteringString', s, onCountdown);
µb.keyvalSetOne('dynamicFilteringString', userData.dynamicFilteringString || '', onCountdown);
µb.keyvalSetOne('urlFilteringString', userData.urlFilteringString || '', onCountdown);
µb.keyvalSetOne('hostnameSwitchesString', userData.hostnameSwitchesString || '', onCountdown);
µb.assets.put(µb.userFiltersPath, userData.userFilters, onCountdown);
@ -831,6 +828,7 @@ var restoreUserData = function(request) {
var resetUserData = function() {
vAPI.cacheStorage.clear();
vAPI.storage.clear();
vAPI.localStorage.removeItem('hiddenSettings');
// Keep global counts, people can become quite attached to numbers
µb.saveLocalSettings();
@ -975,6 +973,10 @@ var onMessage = function(request, sender, callback) {
µb.assets.purgeCacheableAsset(request.path);
break;
case 'readHiddenSettings':
response = µb.stringFromHiddenSettings();
break;
case 'restoreUserData':
restoreUserData(request);
break;
@ -1005,6 +1007,10 @@ var onMessage = function(request, sender, callback) {
response = getRules();
break;
case 'writeHiddenSettings':
µb.hiddenSettingsFromString(request.content);
break;
default:
return vAPI.messaging.UNHANDLED;
}

View file

@ -165,6 +165,15 @@ var resetUserData = function() {
/******************************************************************************/
var synchronizeDOM = function() {
document.body.classList.toggle(
'advancedUser',
uDom.nodeFromId('advanced-user-enabled').checked === true
);
};
/******************************************************************************/
var changeUserSettings = function(name, value) {
messaging.send(
'dashboard',
@ -213,6 +222,7 @@ var onUserSettingsReceived = function(details) {
this.getAttribute('data-setting-name'),
this.checked
);
synchronizeDOM();
});
});
@ -230,6 +240,8 @@ var onUserSettingsReceived = function(details) {
uDom('#import').on('click', startImportFilePicker);
uDom('#reset').on('click', resetUserData);
uDom('#restoreFilePicker').on('change', handleImportFilePicker);
synchronizeDOM();
};
/******************************************************************************/

View file

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global publicSuffixList */
/* global objectAssign, publicSuffixList */
'use strict';
@ -82,6 +82,9 @@ var onAllReady = function() {
vAPI.onLoadAllCompleted();
µb.contextMenu.update(null);
µb.firstInstall = false;
vAPI.net.onBeforeReady = null;
vAPI.messaging.broadcast({ what: 'ublockOrigin-readyState-complete' });
};
/******************************************************************************/
@ -182,10 +185,6 @@ var onUserSettingsReady = function(fetched) {
if ( µb.firstInstall && vAPI.battery ) {
userSettings.ignoreGenericCosmeticFilters = true;
}
// Remove obsolete setting
delete userSettings.logRequests;
vAPI.storage.remove('logRequests');
};
/******************************************************************************/
@ -284,6 +283,23 @@ var onAdminSettingsRestored = function() {
/******************************************************************************/
µb.hiddenSettings = (function() {
var json = vAPI.localStorage.getItem('hiddenSettings');
if ( typeof json === 'string' ) {
try {
var out = JSON.parse(json);
if ( out instanceof Object ) {
return out;
}
}
catch(ex) {
}
}
return objectAssign({}, µb.hiddenSettingsDefault);
})();
/******************************************************************************/
return function() {
// https://github.com/gorhill/uBlock/issues/531
µb.restoreAdminSettings(onAdminSettingsRestored);

View file

@ -19,7 +19,7 @@
Home: https://github.com/gorhill/uBlock
*/
/* global YaMD5, punycode, publicSuffixList */
/* global YaMD5, objectAssign, punycode, publicSuffixList */
'use strict';
@ -80,6 +80,51 @@
/******************************************************************************/
// For now, only boolean type is supported.
µBlock.hiddenSettingsFromString = function(raw) {
var out = objectAssign({}, this.hiddenSettingsDefault),
lineIter = new this.LineIterator(raw),
line, matches, name, value;
while ( lineIter.eot() === false ) {
line = lineIter.next();
matches = /^\s*(\S+)\s+(.+)$/.exec(line);
if ( matches === null || matches.length !== 3 ) { continue; }
name = matches[1];
if ( out.hasOwnProperty(name) === false ) { continue; }
value = matches[2];
switch ( typeof out[name] ) {
case 'boolean':
if ( value === 'true' ) {
out[name] = true;
} else if ( value === 'false' ) {
out[name] = false;
}
break;
default:
break;
}
}
this.hiddenSettings = out;
vAPI.localStorage.setItem('hiddenSettings', JSON.stringify(out));
vAPI.storage.set({ hiddenSettingsString: this.stringFromHiddenSettings() });
};
/******************************************************************************/
µBlock.stringFromHiddenSettings = function() {
var out = [],
keys = Object.keys(this.hiddenSettings).sort(),
key;
for ( var i = 0; i < keys.length; i++ ) {
key = keys[i];
out.push(key + ' ' + this.hiddenSettings[key]);
}
return out.join('\n');
};
/******************************************************************************/
µBlock.savePermanentFirewallRules = function() {
this.keyvalSetOne('dynamicFilteringString', this.permanentFirewall.toString());
};

View file

@ -111,22 +111,24 @@ var onBeforeRequest = function(details) {
// https://github.com/gorhill/uBlock/issues/949
// Redirect blocked request?
var url = µb.redirectEngine.toURL(requestContext);
if ( url !== undefined ) {
pageStore.internalRedirectionCount += 1;
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
tabId,
'redirect',
'rr:' + µb.redirectEngine.resourceNameRegister,
requestType,
requestURL,
requestContext.rootHostname,
requestContext.pageHostname
);
if ( µb.hiddenSettings.ignoreRedirectFilters !== true ) {
var url = µb.redirectEngine.toURL(requestContext);
if ( url !== undefined ) {
pageStore.internalRedirectionCount += 1;
if ( µb.logger.isEnabled() ) {
µb.logger.writeOne(
tabId,
'redirect',
'rr:' + µb.redirectEngine.resourceNameRegister,
requestType,
requestURL,
requestContext.rootHostname,
requestContext.pageHostname
);
}
requestContext.dispose();
return { redirectUrl: url };
}
requestContext.dispose();
return { redirectUrl: url };
}
requestContext.dispose();
@ -136,9 +138,16 @@ var onBeforeRequest = function(details) {
/******************************************************************************/
var onBeforeRootFrameRequest = function(details) {
var tabId = details.tabId;
var requestURL = details.url;
var µb = µBlock;
if (
vAPI.net.onBeforeReady instanceof Function &&
vAPI.net.onBeforeReady(details) === true
) {
return { cancel: true };
}
var tabId = details.tabId,
requestURL = details.url,
µb = µBlock;
µb.tabContextManager.push(tabId, requestURL);
@ -146,9 +155,10 @@ var onBeforeRootFrameRequest = function(details) {
// https://github.com/chrisaljoudi/uBlock/issues/1001
// This must be executed regardless of whether the request is
// behind-the-scene
var µburi = µb.URI;
var requestHostname = µburi.hostnameFromURI(requestURL);
var requestDomain = µburi.domainFromHostname(requestHostname) || requestHostname;
var µburi = µb.URI,
requestHostname = µburi.hostnameFromURI(requestURL),
requestDomain = µburi.domainFromHostname(requestHostname) || requestHostname,
result = '';
var context = {
rootHostname: requestHostname,
rootDomain: requestDomain,
@ -159,8 +169,6 @@ var onBeforeRootFrameRequest = function(details) {
requestType: 'main_frame'
};
var result = '';
// If the site is whitelisted, disregard strict blocking
if ( µb.getNetFilteringSwitch(requestURL) === false ) {
result = 'ua:whitelisted';
@ -634,6 +642,33 @@ var headerIndexFromName = function(headerName, headers) {
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/2067
// Experimental: Suspend tabs until uBO is fully ready.
vAPI.net.onBeforeReady = function(details) {
if ( µBlock.hiddenSettings.suspendTabsUntilReady !== true ) {
return;
}
var pageURL = details.url;
if ( /^https?:\/\//.test(pageURL) === false ) {
return;
}
if (
details.tabId === -1 ||
details.type !== 'main_frame' ||
details.frameId !== 0
) {
return;
}
vAPI.tabs.replace(
details.tabId,
vAPI.getURL('document-suspended.html?url=') + encodeURIComponent(pageURL)
);
return true;
};
/******************************************************************************/
vAPI.net.onBeforeRequest = {
urls: [
'http://*/*',

View file

@ -17,7 +17,7 @@
<li><input id="tooltips-disabled" type="checkbox" data-setting-name="tooltipsDisabled" data-setting-type="bool"><label data-i18n="settingsTooltipsPrompt" for="tooltips-disabled"></label>
<li><input id="color-blind-friendly" type="checkbox" data-setting-name="colorBlindFriendly" data-setting-type="bool"><label data-i18n="settingsColorBlindPrompt" for="color-blind-friendly"></label>
<li><input id="cloud-storage-enabled" type="checkbox" data-setting-name="cloudStorageEnabled" data-setting-type="bool"><label data-i18n="settingsCloudStorageEnabledPrompt" for="cloud-storage-enabled"></label> <a class="fa info" href="https://github.com/gorhill/uBlock/wiki/Cloud-storage" target="_blank">&#xf05a;</a>
<li><input id="advanced-user-enabled" type="checkbox" data-setting-name="advancedUserEnabled" data-setting-type="bool"><label data-i18n="settingsAdvancedUserPrompt" for="advanced-user-enabled"></label>
<li><input id="advanced-user-enabled" type="checkbox" data-setting-name="advancedUserEnabled" data-setting-type="bool"><label data-i18n="settingsAdvancedUserPrompt" for="advanced-user-enabled"></label> <a class="fa info" href="advanced-settings.html" title="settingsAdvancedUserSettings">&#xf085;</a>
<li class="subgroup"><span data-i18n="3pGroupPrivacy"></span><ul>
<li><input id="prefetching-disabled" type="checkbox" data-setting-name="prefetchingDisabled" data-setting-type="bool"><label data-i18n="settingsPrefetchingDisabledPrompt" for="prefetching-disabled"></label> <a class="fa info" href="https://wikipedia.org/wiki/Link_prefetching#Issues_and_criticisms" target="_blank">&#xf05a;</a>
<li><input id="hyperlink-auditing-disabled" type="checkbox" data-setting-name="hyperlinkAuditingDisabled" data-setting-type="bool"><label data-i18n="settingsHyperlinkAuditingDisabledPrompt" for="hyperlink-auditing-disabled"></label> <a class="fa info important" href="https://github.com/gorhill/uBlock/wiki/Disable-hyperlink-auditing-beacon" target="_blank">&#xf05a;</a>