From 3c85c0319462ca331d53c350fba4bc6c1b2ef96f Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Fri, 31 Aug 2018 18:47:02 -0400 Subject: [PATCH] fix #308, #3436, https://github.com/uBlockOrigin/uBlock-issues/issues/155 : a new per-site switch has been added, no-scripting, which purpose is to wholly disable/enable javascript for a given site. This new switch has precedence over all other ways javascript can be disabled, including precedence over dynamic filtering rules. The popup panel will report the number of script resources which have been seen by uBO for the current page. There is a minor inaccuracy to be fixed regarding the count, and which fix requires to extend request journaling. : the `noscript` tags will now be respected when the new no-scripting switch is in effect on a given site. A default setting has been added to the _Settings_ pane to disable/enable globally the new no-script switch, such that one can work in default-deny mode regarding javascript execution. : a new hidden setting, `requestJournalProcessPeriod`, has been added to allow controlling the delay before uBO internally process it's network request journal queue. Default to 1000 (milliseconds). --- src/_locales/en/messages.json | 12 ++++ src/css/dashboard-common.css | 4 +- src/css/popup.css | 12 ++-- src/js/background.js | 1 + src/js/contentscript.js | 5 ++ src/js/hnswitches.js | 5 +- src/js/messaging.js | 81 +++++++++++++++++---------- src/js/pagestore.js | 47 +++++++++++++--- src/js/popup.js | 33 ++++++++--- src/js/scriptlets/cosmetic-survey.js | 57 ------------------- src/js/scriptlets/dom-survey.js | 84 ++++++++++++++++++++++++++++ src/js/scriptlets/noscript-spoof.js | 83 +++++++++++++++++++++++++++ src/js/traffic.js | 46 ++++++++++----- src/js/ublock.js | 6 ++ src/popup.html | 1 + src/settings.html | 1 + 16 files changed, 347 insertions(+), 131 deletions(-) delete mode 100644 src/js/scriptlets/cosmetic-survey.js create mode 100644 src/js/scriptlets/dom-survey.js create mode 100644 src/js/scriptlets/noscript-spoof.js diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index 6c8c2b26b..db5aa078d 100644 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -147,6 +147,14 @@ "message":"Click to no longer block remote fonts on this site", "description":"Tooltip for the no-remote-fonts per-site switch" }, + "popupTipNoScripting1":{ + "message":"Click to wholly disable javascript on this site", + "description":"Tooltip for the no-scripting per-site switch" + }, + "popupTipNoScripting2":{ + "message":"Click to no longer wholly disable javascript on this site", + "description":"Tooltip for the no-remote-fonts per-site switch" + }, "popupTipGlobalRules":{ "message":"Global rules: this column is for rules which apply to all sites.", "description":"Tooltip when hovering the top-most cell of the global-rules column." @@ -299,6 +307,10 @@ "message":"Block remote fonts", "description": "" }, + "settingsNoScriptingPrompt":{ + "message":"Disable javascript", + "description": "The default state for the per-site no-scripting switch" + }, "settingsNoCSPReportsPrompt":{ "message":"Block CSP reports", "description": "background information: https://github.com/gorhill/uBlock/issues/3150" diff --git a/src/css/dashboard-common.css b/src/css/dashboard-common.css index a011dd33e..4a8fcb5d7 100644 --- a/src/css/dashboard-common.css +++ b/src/css/dashboard-common.css @@ -49,8 +49,8 @@ input[type="checkbox"][disabled] + label { width: 40em; } .synopsis { - font-size: small; - opacity: 0.8; + display: inline-block; + padding: 0.25em 0; } .whatisthis { margin: 0 0 0 8px; diff --git a/src/css/popup.css b/src/css/popup.css index 922b249c8..853b8cc0d 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -146,28 +146,28 @@ body.off #switch .fa { #extraTools { background-color: #eee; border: 0; - color: #aaa; + color: #888; margin: 0.8em 0 0 0; - padding: 4px 0; + padding: 4px 0 4px 0.8em; text-align: center; } #extraTools > span { cursor: pointer; font-size: 1.2em; - margin: 0 0.4em; + margin: 0 0.8em 0 0; position: relative; } #extraTools > span > span.badge { color: #222; - bottom: -1px; + bottom: -2px; font: x-small sans-serif; position: absolute; } body[dir="ltr"] #extraTools > span > span.badge { - left: 100%; + /* left: 100%; */ } body[dir="rtl"] #extraTools > span > span.badge { - right: 100%; + /* right: 100%; */ } #extraTools > span > span:last-of-type { color: #e00; diff --git a/src/js/background.js b/src/js/background.js index ac7cd2394..ef06a8c97 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -48,6 +48,7 @@ var µBlock = (function() { // jshint ignore:line ignoreScriptInjectFilters: false, manualUpdateAssetFetchPeriod: 500, popupFontSize: 'unset', + requestJournalProcessPeriod: 1000, suspendTabsUntilReady: false, userResourcesLocation: 'unset' }; diff --git a/src/js/contentscript.js b/src/js/contentscript.js index 5791f81de..64c5d6866 100644 --- a/src/js/contentscript.js +++ b/src/js/contentscript.js @@ -1284,6 +1284,11 @@ vAPI.domSurveyor = (function() { return; } + vAPI.messaging.send( + 'contentscript', + { what: 'shouldRenderNoscriptTags' } + ); + if ( vAPI.domWatcher instanceof Object ) { vAPI.domWatcher.start(); } diff --git a/src/js/hnswitches.js b/src/js/hnswitches.js index 53f517937..cb04996ac 100644 --- a/src/js/hnswitches.js +++ b/src/js/hnswitches.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a Chromium browser extension to black/white list requests. - Copyright (C) 2015-2018 Raymond Hill + Copyright (C) 2015-present 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 @@ -42,7 +42,8 @@ var switchBitOffsets = { 'no-cosmetic-filtering': 4, 'no-remote-fonts': 6, 'no-large-media': 8, - 'no-csp-reports': 10 + 'no-csp-reports': 10, + 'no-scripting': 12, }; var switchStateToNameMap = { diff --git a/src/js/messaging.js b/src/js/messaging.js index e90e00149..bf4fdfb53 100644 --- a/src/js/messaging.js +++ b/src/js/messaging.js @@ -84,9 +84,6 @@ var onMessage = function(request, sender, callback) { break; } - // The concatenation with the empty string ensure that the resulting value - // is a string. This is important since tab id values are assumed to be - // of string type. var tabId = sender && sender.tab ? sender.tab.id : 0; // Sync @@ -335,6 +332,7 @@ var popupDataFromTabId = function(tabId, tabTitle) { r.largeMediaCount = pageStore.largeMediaCount; r.noRemoteFonts = µb.hnSwitches.evaluateZ('no-remote-fonts', rootHostname); r.remoteFontCount = pageStore.remoteFontCount; + r.noScripting = µb.hnSwitches.evaluateZ('no-scripting', rootHostname); } else { r.hostnameDict = {}; r.firewallRules = getFirewallRules(); @@ -374,14 +372,6 @@ var onMessage = function(request, sender, callback) { // Async switch ( request.what ) { - case 'getPopupLazyData': - pageStore = µb.pageStoreFromTabId(request.tabId); - if ( pageStore !== null ) { - pageStore.hiddenElementCount = 0; - µb.scriptlets.injectDeep(request.tabId, 'cosmetic-survey'); - } - return; - case 'getPopupData': popupDataFromRequest(request, callback); return; @@ -394,6 +384,15 @@ var onMessage = function(request, sender, callback) { var response; switch ( request.what ) { + case 'getPopupLazyData': + pageStore = µb.pageStoreFromTabId(request.tabId); + if ( pageStore !== null ) { + pageStore.hiddenElementCount = 0; + pageStore.inlineScriptCount = 0; + µb.scriptlets.injectDeep(request.tabId, 'dom-survey'); + } + break; + case 'hasPopupContentChanged': pageStore = µb.pageStoreFromTabId(request.tabId); var lastModified = pageStore ? pageStore.contentLastModified : 0; @@ -491,6 +490,22 @@ var onMessage = function(request, sender, callback) { } break; + case 'shouldRenderNoscriptTags': + if ( pageStore === null ) { break; } + let tabContext = µb.tabContextManager.lookup(tabId); + if ( tabContext === null ) { break; } + if ( pageStore.filterScripting(tabContext.rootHostname) ) { + vAPI.tabs.injectScript( + tabId, + { + file: '/js/scriptlets/noscript-spoof.js', + frameId: frameId, + runAt: 'document_end' + } + ); + } + break; + case 'retrieveContentScriptParameters': if ( pageStore === null || @@ -1233,23 +1248,22 @@ vAPI.messaging.listen('documentBlocked', onMessage); /******************************************************************************/ -var µb = µBlock; -var broadcastTimers = Object.create(null); +let µb = µBlock; +let broadcastTimers = new Map(); /******************************************************************************/ -var cosmeticallyFilteredElementCountChanged = function(tabId) { - delete broadcastTimers[tabId + '-cosmeticallyFilteredElementCountChanged']; +var domSurveyFinalReport = function(tabId) { + broadcastTimers.delete(tabId + '-domSurveyReport'); - var pageStore = µb.pageStoreFromTabId(tabId); - if ( pageStore === null ) { - return; - } + let pageStore = µb.pageStoreFromTabId(tabId); + if ( pageStore === null ) { return; } vAPI.messaging.broadcast({ - what: 'cosmeticallyFilteredElementCountChanged', + what: 'domSurveyFinalReport', tabId: tabId, - count: pageStore.hiddenElementCount + affectedElementCount: pageStore.hiddenElementCount, + scriptCount: pageStore.scriptCount + pageStore.inlineScriptCount, }); }; @@ -1280,8 +1294,8 @@ var logCosmeticFilters = function(tabId, details) { /******************************************************************************/ var onMessage = function(request, sender, callback) { - var tabId = sender && sender.tab ? sender.tab.id : 0; - var pageStore = µb.pageStoreFromTabId(tabId); + let tabId = sender && sender.tab ? sender.tab.id : 0; + let pageStore = µb.pageStoreFromTabId(tabId); // Async switch ( request.what ) { @@ -1293,15 +1307,20 @@ var onMessage = function(request, sender, callback) { var response; switch ( request.what ) { - case 'cosmeticallyFilteredElementCount': - if ( pageStore !== null && request.filteredElementCount ) { - pageStore.hiddenElementCount += request.filteredElementCount; - var broadcastKey = tabId + '-cosmeticallyFilteredElementCountChanged'; - if ( broadcastTimers[broadcastKey] === undefined ) { - broadcastTimers[broadcastKey] = vAPI.setTimeout( - cosmeticallyFilteredElementCountChanged.bind(null, tabId), + case 'domSurveyTransientReport': + if ( pageStore !== null ) { + if ( request.filteredElementCount ) { + pageStore.hiddenElementCount += request.filteredElementCount; + } + if ( request.inlineScriptCount ) { + pageStore.inlineScriptCount += request.inlineScriptCount; + } + let broadcastKey = tabId + '-domSurveyReport'; + if ( broadcastTimers.has(broadcastKey) === false ) { + broadcastTimers.set(broadcastKey, vAPI.setTimeout( + ( ) => { domSurveyFinalReport(tabId); }, 250 - ); + )); } } break; diff --git a/src/js/pagestore.js b/src/js/pagestore.js index fd0188c02..9d2141e32 100644 --- a/src/js/pagestore.js +++ b/src/js/pagestore.js @@ -290,6 +290,8 @@ PageStore.prototype.init = function(tabId, context) { this.perLoadAllowedRequestCount = 0; this.hiddenElementCount = ''; // Empty string means "unknown" this.remoteFontCount = 0; + this.scriptCount = 0; + this.inlineScriptCount = 0; this.popupBlockedCount = 0; this.largeMediaCount = 0; this.largeMediaTimer = null; @@ -517,7 +519,10 @@ PageStore.prototype.journalAddRequest = function(hostname, result) { result === 1 ? 0x00000001 : 0x00010000 ); if ( this.journalTimer === null ) { - this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000); + this.journalTimer = vAPI.setTimeout( + ( ) => { this.journalProcess(true); }, + µb.hiddenSettings.requestJournalProcessPeriod + ); } }; @@ -539,7 +544,10 @@ PageStore.prototype.journalAddRootFrame = function(type, url) { if ( this.journalTimer !== null ) { clearTimeout(this.journalTimer); } - this.journalTimer = vAPI.setTimeout(this.journalProcess.bind(this, true), 1000); + this.journalTimer = vAPI.setTimeout( + ( ) => { this.journalProcess(true); }, + µb.hiddenSettings.requestJournalProcessPeriod + ); }; PageStore.prototype.journalProcess = function(fromTimer) { @@ -549,21 +557,20 @@ PageStore.prototype.journalProcess = function(fromTimer) { this.journalTimer = null; var journal = this.journal, - i, n = journal.length, - hostname, count, hostnameCounts, + n = journal.length, aggregateCounts = 0, now = Date.now(), pivot = this.journalLastCommitted || 0; // Everything after pivot originates from current page. - for ( i = pivot; i < n; i += 2 ) { - hostname = journal[i]; - hostnameCounts = this.hostnameToCountMap.get(hostname); + for ( let i = pivot; i < n; i += 2 ) { + let hostname = journal[i]; + let hostnameCounts = this.hostnameToCountMap.get(hostname); if ( hostnameCounts === undefined ) { hostnameCounts = 0; this.contentLastModified = now; } - count = journal[i+1]; + let count = journal[i+1]; this.hostnameToCountMap.set(hostname, hostnameCounts + count); aggregateCounts += count; } @@ -579,7 +586,7 @@ PageStore.prototype.journalProcess = function(fromTimer) { // Everything before pivot does not originate from current page -- we still // need to bump global blocked/allowed counts. - for ( i = 0; i < pivot; i += 2 ) { + for ( let i = 0; i < pivot; i += 2 ) { aggregateCounts += journal[i+1]; } if ( aggregateCounts !== 0 ) { @@ -609,6 +616,13 @@ PageStore.prototype.filterRequest = function(context) { return 1; } + if ( requestType === 'script' ) { + this.scriptCount += 1; + if ( this.filterScripting(context.rootHostname) === 1 ) { + return 1; + } + } + var cacheableResult = this.cacheableResults[requestType] === true; if ( cacheableResult ) { @@ -706,6 +720,21 @@ PageStore.prototype.filterFont = function(context) { /******************************************************************************/ +PageStore.prototype.filterScripting = function(rootHostname) { + if ( + this.getNetFilteringSwitch() === false || + µb.hnSwitches.evaluateZ('no-scripting', rootHostname) === false + ) { + return 0; + } + if ( µb.logger.isEnabled() ) { + this.logData = µb.hnSwitches.toLogData(); + } + return 1; +}; + +/******************************************************************************/ + // The caller is responsible to check whether filtering is enabled or not. PageStore.prototype.filterLargeMediaElement = function(size) { diff --git a/src/js/popup.js b/src/js/popup.js index 5a5a2f61c..ca6515e51 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2018 Raymond Hill + Copyright (C) 2014-present 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 @@ -161,6 +161,7 @@ var hashFromPopupData = function(reset) { hasher.push(uDom.nodeFromId('no-large-media').classList.contains('on')); hasher.push(uDom.nodeFromId('no-cosmetic-filtering').classList.contains('on')); hasher.push(uDom.nodeFromId('no-remote-fonts').classList.contains('on')); + hasher.push(uDom.nodeFromId('no-scripting').classList.contains('on')); var hash = hasher.join(''); if ( reset ) { @@ -438,6 +439,7 @@ var renderPopup = function() { uDom.nodeFromId('no-large-media').classList.toggle('on', popupData.noLargeMedia === true); uDom.nodeFromId('no-cosmetic-filtering').classList.toggle('on', popupData.noCosmeticFiltering === true); uDom.nodeFromId('no-remote-fonts').classList.toggle('on', popupData.noRemoteFonts === true); + uDom.nodeFromId('no-scripting').classList.toggle('on', popupData.noScripting === true); // Report blocked popup count on badge total = popupData.popupBlockedCount; @@ -487,14 +489,13 @@ var renderPopup = function() { // Use tooltip for ARIA purpose. var renderTooltips = function(selector) { - var elem, text; - for ( var entry of tooltipTargetSelectors ) { + for ( let entry of tooltipTargetSelectors ) { if ( selector !== undefined && entry[0] !== selector ) { continue; } - text = vAPI.i18n( + let text = vAPI.i18n( entry[1].i18n + (uDom.nodeFromSelector(entry[1].state) === null ? '1' : '2') ); - elem = uDom.nodeFromSelector(entry[0]); + let elem = uDom.nodeFromSelector(entry[0]); elem.setAttribute('aria-label', text); elem.setAttribute('data-tip', text); if ( selector !== undefined ) { @@ -539,7 +540,14 @@ var tooltipTargetSelectors = new Map([ state: '#no-remote-fonts.on', i18n: 'popupTipNoRemoteFonts' } - ] + ], + [ + '#no-scripting', + { + state: '#no-scripting.on', + i18n: 'popupTipNoScripting' + } + ], ]); /******************************************************************************/ @@ -627,10 +635,17 @@ var onPopupMessage = function(data) { if ( data.tabId !== popupData.tabId ) { return; } switch ( data.what ) { - case 'cosmeticallyFilteredElementCountChanged': - var v = data.count || ''; + case 'domSurveyFinalReport': + let count = data.affectedElementCount || ''; uDom.nodeFromSelector('#no-cosmetic-filtering > span.badge') - .textContent = typeof v === 'number' ? v.toLocaleString() : v; + .textContent = typeof count === 'number' ? + count.toLocaleString() : + count; + count = data.scriptCount || ''; + uDom.nodeFromSelector('#no-scripting > span.badge') + .textContent = typeof count === 'number' ? + count.toLocaleString() : + count; break; } }; diff --git a/src/js/scriptlets/cosmetic-survey.js b/src/js/scriptlets/cosmetic-survey.js deleted file mode 100644 index efe1faccb..000000000 --- a/src/js/scriptlets/cosmetic-survey.js +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - - uBlock Origin - a browser extension to block requests. - Copyright (C) 2015-2018 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() { - if ( typeof vAPI !== 'object' || !vAPI.domFilterer ) { return; } - - vAPI.messaging.send( - 'scriptlets', - { - what: 'cosmeticallyFilteredElementCount', - pageURL: window.location.href, - filteredElementCount: vAPI.domFilterer.getFilteredElementCount() - } - ); -})(); - - - - - - - - -/******************************************************************************* - - DO NOT: - - Remove the following code - - Add code beyond the following code - Reason: - - https://github.com/gorhill/uBlock/pull/3721 - - uBO never uses the return value from injected content scripts - -**/ - -void 0; diff --git a/src/js/scriptlets/dom-survey.js b/src/js/scriptlets/dom-survey.js new file mode 100644 index 000000000..1bb5454e0 --- /dev/null +++ b/src/js/scriptlets/dom-survey.js @@ -0,0 +1,84 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2015-2018 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() { + if ( typeof vAPI !== 'object' ) { return; } + + // https://github.com/gorhill/httpswitchboard/issues/25 + // + // https://github.com/gorhill/httpswitchboard/issues/131 + // Looks for inline javascript also in at least one a[href] element. + // + // https://github.com/gorhill/uMatrix/issues/485 + // Mind "on..." attributes. + // + // https://github.com/gorhill/uMatrix/issues/924 + // Report inline styles. + let inlineScriptCount = 0; + if ( + document.querySelector('script:not([src])') !== null || + document.querySelector('script[src^="data:"]') !== null || + document.querySelector('script[src^="blob:"]') !== null || + document.querySelector('a[href^="javascript:"]') !== null || + document.querySelector('[onabort],[onblur],[oncancel],[oncanplay],[oncanplaythrough],[onchange],[onclick],[onclose],[oncontextmenu],[oncuechange],[ondblclick],[ondrag],[ondragend],[ondragenter],[ondragexit],[ondragleave],[ondragover],[ondragstart],[ondrop],[ondurationchange],[onemptied],[onended],[onerror],[onfocus],[oninput],[oninvalid],[onkeydown],[onkeypress],[onkeyup],[onload],[onloadeddata],[onloadedmetadata],[onloadstart],[onmousedown],[onmouseenter],[onmouseleave],[onmousemove],[onmouseout],[onmouseover],[onmouseup],[onwheel],[onpause],[onplay],[onplaying],[onprogress],[onratechange],[onreset],[onresize],[onscroll],[onseeked],[onseeking],[onselect],[onshow],[onstalled],[onsubmit],[onsuspend],[ontimeupdate],[ontoggle],[onvolumechange],[onwaiting],[onafterprint],[onbeforeprint],[onbeforeunload],[onhashchange],[onlanguagechange],[onmessage],[onoffline],[ononline],[onpagehide],[onpageshow],[onrejectionhandled],[onpopstate],[onstorage],[onunhandledrejection],[onunload],[oncopy],[oncut],[onpaste]') !== null + ) { + inlineScriptCount = 1; + } + + let filteredElementCount = 0; + if ( vAPI.domFilterer ) { + filteredElementCount = vAPI.domFilterer.getFilteredElementCount(); + } + + vAPI.messaging.send( + 'scriptlets', + { + what: 'domSurveyTransientReport', + pageURL: window.location.href, + filteredElementCount: filteredElementCount, + inlineScriptCount: inlineScriptCount, + } + ); +})(); + + + + + + + + +/******************************************************************************* + + DO NOT: + - Remove the following code + - Add code beyond the following code + Reason: + - https://github.com/gorhill/uBlock/pull/3721 + - uBO never uses the return value from injected content scripts + +**/ + +void 0; diff --git a/src/js/scriptlets/noscript-spoof.js b/src/js/scriptlets/noscript-spoof.js new file mode 100644 index 000000000..76f7896b8 --- /dev/null +++ b/src/js/scriptlets/noscript-spoof.js @@ -0,0 +1,83 @@ +/******************************************************************************* + + uBlock Origin - a browser extension to block requests. + Copyright (C) 2014-present 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 +*/ + +// Code below has been imported from uMatrix and modified to fit uBO: +// https://github.com/gorhill/uMatrix/blob/3f8794dd899a05e066c24066c6c0a2515d5c60d2/src/js/contentscript.js#L464-L531 + +'use strict'; + +/******************************************************************************/ + +// https://github.com/gorhill/uMatrix/issues/232 +// Force `display` property, Firefox is still affected by the issue. + +(function() { + let noscripts = document.querySelectorAll('noscript'); + if ( noscripts.length === 0 ) { return; } + + let redirectTimer, + reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i, + reSafeURL = /^https?:\/\//; + + let autoRefresh = function(root) { + let meta = root.querySelector('meta[http-equiv="refresh"][content]'); + if ( meta === null ) { return; } + let match = reMetaContent.exec(meta.getAttribute('content')); + if ( match === null || match[3].trim() === '' ) { return; } + let url = new URL(match[3], document.baseURI); + if ( reSafeURL.test(url.href) === false ) { return; } + redirectTimer = setTimeout(( ) => { + location.assign(url.href); + }, + parseInt(match[1], 10) * 1000 + 1 + ); + meta.parentNode.removeChild(meta); + }; + + let morphNoscript = function(from) { + if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) { + let to = document.createElement('span'); + while ( from.firstChild !== null ) { + to.appendChild(from.firstChild); + } + return to; + } + let parser = new DOMParser(); + let doc = parser.parseFromString( + '' + from.textContent + '', + 'text/html' + ); + return document.adoptNode(doc.querySelector('span')); + }; + + for ( let noscript of noscripts ) { + let parent = noscript.parentNode; + if ( parent === null ) { continue; } + let span = morphNoscript(noscript); + span.style.setProperty('display', 'inline', 'important'); + if ( redirectTimer === undefined ) { + autoRefresh(span); + } + parent.replaceChild(span, noscript); + } +})(); + +/******************************************************************************/ diff --git a/src/js/traffic.js b/src/js/traffic.js index fd28d1762..8984fd68e 100644 --- a/src/js/traffic.js +++ b/src/js/traffic.js @@ -1,7 +1,7 @@ /******************************************************************************* uBlock Origin - a browser extension to block requests. - Copyright (C) 2014-2018 Raymond Hill + Copyright (C) 2014-present 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 @@ -912,20 +912,36 @@ var injectCSP = function(pageStore, details) { let builtinDirectives = []; - context.requestType = 'inline-script'; - if ( pageStore.filterRequest(context) === 1 ) { - builtinDirectives.push("script-src 'unsafe-eval' * blob: data:"); - } - if ( loggerEnabled === true ) { - logger.writeOne( - tabId, - 'net', - pageStore.logData, - 'inline-script', - requestURL, - context.rootHostname, - context.pageHostname - ); + context.requestType = 'script'; + if ( pageStore.filterScripting(context.rootHostname) === 1 ) { + builtinDirectives.push("script-src *"); + if ( loggerEnabled === true ) { + logger.writeOne( + tabId, + 'net', + pageStore.logData, + 'no-scripting', + requestURL, + context.rootHostname, + context.pageHostname + ); + } + } else { + context.requestType = 'inline-script'; + if ( pageStore.filterRequest(context) === 1 ) { + builtinDirectives.push("script-src 'unsafe-eval' * blob: data:"); + } + if ( loggerEnabled === true ) { + logger.writeOne( + tabId, + 'net', + pageStore.logData, + 'inline-script', + requestURL, + context.rootHostname, + context.pageHostname + ); + } } // https://github.com/gorhill/uBlock/issues/1539 diff --git a/src/js/ublock.js b/src/js/ublock.js index 728d5c668..13ee21818 100644 --- a/src/js/ublock.js +++ b/src/js/ublock.js @@ -286,6 +286,7 @@ var matchBucket = function(url, hostname, bucket, start) { us.noCosmeticFiltering = this.hnSwitches.evaluate('no-cosmetic-filtering', '*') === 1; us.noLargeMedia = this.hnSwitches.evaluate('no-large-media', '*') === 1; us.noRemoteFonts = this.hnSwitches.evaluate('no-remote-fonts', '*') === 1; + us.noScripting = this.hnSwitches.evaluate('no-scripting', '*') === 1; us.noCSPReports = this.hnSwitches.evaluate('no-csp-reports', '*') === 1; return us; } @@ -354,6 +355,11 @@ var matchBucket = function(url, hostname, bucket, start) { this.saveHostnameSwitches(); } break; + case 'noScripting': + if ( this.hnSwitches.toggle('no-scripting', '*', value ? 1 : 0) ) { + this.saveHostnameSwitches(); + } + break; case 'noCSPReports': if ( this.hnSwitches.toggle('no-csp-reports', '*', value ? 1 : 0) ) { this.saveHostnameSwitches(); diff --git a/src/popup.html b/src/popup.html index 872bf1d3a..0fd128f08 100644 --- a/src/popup.html +++ b/src/popup.html @@ -38,6 +38,7 @@ +
diff --git a/src/settings.html b/src/settings.html index f5ffdb192..bfd4aa3e2 100644 --- a/src/settings.html +++ b/src/settings.html @@ -30,6 +30,7 @@
  • +