From f7bbc807176fa93680fdaf8b713593a43a3df2a5 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 18 May 2019 14:20:05 -0400 Subject: [PATCH] Improve "Whitelist pane"; remove now useless built-in switch rule Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/214 Built-in whitelist directives are now rendered differently than user-defined whitelist directives. Also, removing a built-in whitelist directive will only cause that directive to be commented out, so that users do not have to remember built-in directives should they want to bring them back. Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/494 The built-in per-site switch rule `no-scripting: behind-the-scene false` has been removed, it should not ever be needed since there will always be a valid root context for main- and sub-frames. --- src/css/whitelist.css | 6 ++ src/js/background.js | 20 +++--- src/js/filtering-context.js | 2 +- src/js/messaging.js | 26 +++----- src/js/start.js | 11 ++-- src/js/ublock.js | 20 +++--- src/js/whitelist.js | 121 ++++++++++++++++++++++++------------ 7 files changed, 125 insertions(+), 81 deletions(-) diff --git a/src/css/whitelist.css b/src/css/whitelist.css index fe116381b..a5ca328f2 100644 --- a/src/css/whitelist.css +++ b/src/css/whitelist.css @@ -10,3 +10,9 @@ body { text-align: left; width: 100%; } +.codeMirrorContainer .cm-builtin { + color: #00F; + } +.codeMirrorContainer .cm-comment.cm-builtin { + color: #88F; + } diff --git a/src/js/background.js b/src/js/background.js index b89959e24..5afed6d49 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -57,16 +57,6 @@ const µBlock = (function() { // jshint ignore:line userResourcesLocation: 'unset' }; - const whitelistDefault = [ - 'about-scheme', - 'chrome-extension-scheme', - 'chrome-scheme', - 'moz-extension-scheme', - 'opera-scheme', - 'vivaldi-scheme', - 'wyciwyg-scheme', // Firefox's "What-You-Cache-Is-What-You-Get" - ]; - return { firstInstall: false, @@ -126,7 +116,15 @@ const µBlock = (function() { // jshint ignore:line // Whitelist directives need to be loaded once the PSL is available netWhitelist: {}, netWhitelistModifyTime: 0, - netWhitelistDefault: whitelistDefault.join('\n'), + netWhitelistDefault: [ + 'about-scheme', + 'chrome-extension-scheme', + 'chrome-scheme', + 'moz-extension-scheme', + 'opera-scheme', + 'vivaldi-scheme', + 'wyciwyg-scheme', // Firefox's "What-You-Cache-Is-What-You-Get" + ], localSettings: { blockedRequestCount: 0, diff --git a/src/js/filtering-context.js b/src/js/filtering-context.js index 4b0ec5b30..7a09bf7a8 100644 --- a/src/js/filtering-context.js +++ b/src/js/filtering-context.js @@ -91,7 +91,7 @@ µBlock.normalizePageURL(0, details.documentUrl) ); this.setDocOrigin(origin).setTabOrigin(origin); - } else if ( this.type === 'sub_frame' ) { + } else if ( this.type.endsWith('_frame') ) { const origin = this.originFromURI(this.url); this.setDocOrigin(origin).setTabOrigin(origin); } else { diff --git a/src/js/messaging.js b/src/js/messaging.js index 643906f05..b37ae8dea 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -30,24 +30,17 @@ /******************************************************************************/ -var µb = µBlock; +const µb = µBlock; /******************************************************************************/ -var getDomainNames = function(targets) { - var out = []; - var µburi = µb.URI; - var target, domain; - for ( var i = 0; i < targets.length; i++ ) { - target = targets[i]; - if ( target.indexOf('/') !== -1 ) { - domain = µburi.domainFromURI(target) || ''; - } else { - domain = µburi.domainFromHostname(target) || target; - } - out.push(domain); - } - return out; +const getDomainNames = function(targets) { + const µburi = µb.URI; + return targets.map(target => + target.indexOf('/') !== -1 + ? µburi.domainFromURI(target) || '' + : µburi.domainFromHostname(target) || target + ); }; /******************************************************************************/ @@ -128,7 +121,8 @@ const onMessage = function(request, sender, callback) { case 'getWhitelist': response = { - whitelist: µb.stringFromWhitelist(µb.netWhitelist), + whitelist: µb.arrayFromWhitelist(µb.netWhitelist), + whitelistDefault: µb.netWhitelistDefault, reBadHostname: µb.reWhitelistBadHostname.source, reHostnameExtractor: µb.reWhitelistHostnameExtractor.source }; diff --git a/src/js/start.js b/src/js/start.js index 37adb7af2..f9abf5565 100644 --- a/src/js/start.js +++ b/src/js/start.js @@ -208,9 +208,11 @@ const onVersionReady = function(lastVersion) { const lastVersionInt = intFromVersion(lastVersion); - if ( lastVersionInt <= 1016021007 ) { - µb.sessionSwitches.toggle('no-scripting', 'behind-the-scene', 2); - µb.permanentSwitches.toggle('no-scripting', 'behind-the-scene', 2); + // https://github.com/uBlockOrigin/uBlock-issues/issues/494 + // Remove useless per-site switches. + if ( lastVersionInt <= 1019003007 ) { + µb.sessionSwitches.toggle('no-scripting', 'behind-the-scene', 0); + µb.permanentSwitches.toggle('no-scripting', 'behind-the-scene', 0); µb.saveHostnameSwitches(); } @@ -347,13 +349,12 @@ const createDefaultProps = function() { 'urlFilteringString': '', 'hostnameSwitchesString': [ 'no-large-media: behind-the-scene false', - 'no-scripting: behind-the-scene false' ].join('\n'), 'lastRestoreFile': '', 'lastRestoreTime': 0, 'lastBackupFile': '', 'lastBackupTime': 0, - 'netWhitelist': µb.netWhitelistDefault, + 'netWhitelist': µb.netWhitelistDefault.join('\n'), 'selfieMagic': 0, 'version': '0.0.0.0' }; diff --git a/src/js/ublock.js b/src/js/ublock.js index 1ecc34fbb..4afad4980 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -177,17 +177,19 @@ var matchBucket = function(url, hostname, bucket, start) { /******************************************************************************/ -µBlock.stringFromWhitelist = function(whitelist) { - var r = {}; - var i, bucket; - for ( var key in whitelist ) { - bucket = whitelist[key]; - i = bucket.length; - while ( i-- ) { - r[bucket[i]] = true; +µBlock.arrayFromWhitelist = function(whitelist) { + const out = new Set(); + for ( const key in whitelist ) { + const bucket = whitelist[key]; + for ( const directive of bucket ) { + out.add(directive); } } - return Object.keys(r).sort(function(a,b){return a.localeCompare(b);}).join('\n'); + return Array.from(out).sort((a, b) => a.localeCompare(b)); +}; + +µBlock.stringFromWhitelist = function(whitelist) { + return this.arrayFromWhitelist(whitelist).join('\n'); }; /******************************************************************************/ diff --git a/src/js/whitelist.js b/src/js/whitelist.js index 13456f010..fac008542 100644 --- a/src/js/whitelist.js +++ b/src/js/whitelist.js @@ -25,26 +25,42 @@ /******************************************************************************/ -(function() { +(( ) => { + +/******************************************************************************/ + +const reComment = /^\s*#\s*/; + +const directiveFromLine = function(line) { + const match = reComment.exec(line); + return match === null + ? line.trim() + : line.slice(match.index + match[0].length).trim(); +}; /******************************************************************************/ CodeMirror.defineMode("ubo-whitelist-directives", function() { - var reComment = /^\s*#/, - reRegex = /^\/.+\/$/; + const reRegex = /^\/.+\/$/; return { token: function(stream) { - var line = stream.string.trim(); + const line = stream.string.trim(); stream.skipToEnd(); if ( reBadHostname === undefined ) { return null; } if ( reComment.test(line) ) { - return 'comment'; + return whitelistDefaultSet.has(directiveFromLine(line)) + ? 'builtin comment' + : 'comment'; } if ( line.indexOf('/') === -1 ) { - return reBadHostname.test(line) ? 'error' : null; + if ( reBadHostname.test(line) ) { return 'error'; } + if ( whitelistDefaultSet.has(line.trim()) ) { + return 'builtin'; + } + return null; } if ( reRegex.test(line) ) { try { @@ -59,16 +75,18 @@ CodeMirror.defineMode("ubo-whitelist-directives", function() { }; }); -var reBadHostname, - reHostnameExtractor; +let reBadHostname; +let reHostnameExtractor; +let whitelistDefaultSet = new Set(); /******************************************************************************/ -var messaging = vAPI.messaging, - cachedWhitelist = '', - noopFunc = function(){}; +const messaging = vAPI.messaging; +const noopFunc = function(){}; -var cmEditor = new CodeMirror( +let cachedWhitelist = ''; + +const cmEditor = new CodeMirror( document.getElementById('whitelist'), { autofocus: true, @@ -82,11 +100,11 @@ uBlockDashboard.patchCodeMirrorEditor(cmEditor); /******************************************************************************/ -var whitelistChanged = function() { - var whitelistElem = uDom.nodeFromId('whitelist'); - var bad = whitelistElem.querySelector('.cm-error') !== null; - var changedWhitelist = cmEditor.getValue().trim(); - var changed = changedWhitelist !== cachedWhitelist; +const whitelistChanged = function() { + const whitelistElem = uDom.nodeFromId('whitelist'); + const bad = whitelistElem.querySelector('.cm-error') !== null; + const changedWhitelist = cmEditor.getValue().trim(); + const changed = changedWhitelist !== cachedWhitelist; uDom.nodeFromId('whitelistApply').disabled = !changed || bad; uDom.nodeFromId('whitelistRevert').disabled = !changed; CodeMirror.commands.save = changed && !bad ? applyChanges : noopFunc; @@ -96,15 +114,39 @@ cmEditor.on('changes', whitelistChanged); /******************************************************************************/ -var renderWhitelist = function() { - var onRead = function(details) { - var first = reBadHostname === undefined; +const renderWhitelist = function() { + const onRead = details => { + const first = reBadHostname === undefined; if ( first ) { reBadHostname = new RegExp(details.reBadHostname); reHostnameExtractor = new RegExp(details.reHostnameExtractor); + whitelistDefaultSet = new Set(details.whitelistDefault); } - cachedWhitelist = details.whitelist.trim(); - cmEditor.setValue(cachedWhitelist + '\n'); + const toAdd = new Set(whitelistDefaultSet); + for ( const line of details.whitelist ) { + const directive = directiveFromLine(line); + if ( whitelistDefaultSet.has(directive) === false ) { continue; } + toAdd.delete(directive); + if ( toAdd.size === 0 ) { break; } + } + if ( toAdd.size !== 0 ) { + details.whitelist.push(...Array.from(toAdd).map(a => `# ${a}`)); + } + details.whitelist.sort((a, b) => { + const ad = directiveFromLine(a); + const bd = directiveFromLine(b); + const abuiltin = whitelistDefaultSet.has(ad); + if ( abuiltin !== whitelistDefaultSet.has(bd) ) { + return abuiltin ? -1 : 1; + } + return ad.localeCompare(bd); + }); + let whitelistStr = details.whitelist.join('\n').trim(); + cachedWhitelist = whitelistStr; + if ( whitelistStr !== '' ) { + whitelistStr += '\n'; + } + cmEditor.setValue(whitelistStr); if ( first ) { cmEditor.clearHistory(); } @@ -114,8 +156,8 @@ var renderWhitelist = function() { /******************************************************************************/ -var handleImportFilePicker = function() { - var fileReaderOnLoadHandler = function() { +const handleImportFilePicker = function() { + const fileReaderOnLoadHandler = ( ) => { cmEditor.setValue( [ cmEditor.getValue().trim(), @@ -123,18 +165,18 @@ var handleImportFilePicker = function() { ].join('\n').trim() ); }; - var file = this.files[0]; + const file = this.files[0]; if ( file === undefined || file.name === '' ) { return; } if ( file.type.indexOf('text') !== 0 ) { return; } - var fr = new FileReader(); + const fr = new FileReader(); fr.onload = fileReaderOnLoadHandler; fr.readAsText(file); }; /******************************************************************************/ -var startImportFilePicker = function() { - var input = document.getElementById('importFilePicker'); +const startImportFilePicker = function() { + const input = document.getElementById('importFilePicker'); // Reset to empty string, this will ensure an change event is properly // triggered if the user pick a file, even if it is the same as the last // one picked. @@ -144,21 +186,22 @@ var startImportFilePicker = function() { /******************************************************************************/ -var exportWhitelistToFile = function() { - var val = cmEditor.getValue().trim(); +const exportWhitelistToFile = function() { + const val = cmEditor.getValue().trim(); if ( val === '' ) { return; } - var filename = vAPI.i18n('whitelistExportFilename') - .replace('{{datetime}}', uBlockDashboard.dateNowToSensibleString()) - .replace(/ +/g, '_'); + const filename = + vAPI.i18n('whitelistExportFilename') + .replace('{{datetime}}', uBlockDashboard.dateNowToSensibleString()) + .replace(/ +/g, '_'); vAPI.download({ - 'url': 'data:text/plain;charset=utf-8,' + encodeURIComponent(val + '\n'), + 'url': `data:text/plain;charset=utf-8,${encodeURIComponent(val + '\n')}`, 'filename': filename }); }; /******************************************************************************/ -var applyChanges = function() { +const applyChanges = function() { cachedWhitelist = cmEditor.getValue().trim(); messaging.send( 'dashboard', @@ -170,19 +213,19 @@ var applyChanges = function() { ); }; -var revertChanges = function() { - var content = cachedWhitelist; +const revertChanges = function() { + let content = cachedWhitelist; if ( content !== '' ) { content += '\n'; } cmEditor.setValue(content); }; /******************************************************************************/ -var getCloudData = function() { +const getCloudData = function() { return cmEditor.getValue(); }; -var setCloudData = function(data, append) { +const setCloudData = function(data, append) { if ( typeof data !== 'string' ) { return; } if ( append ) { data = uBlockDashboard.mergeNewLines(cmEditor.getValue().trim(), data);