From dfd42ebf5ffa1e70c8ecd6f7cc0e8fb680e0d934 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 12 Apr 2025 15:20:18 -0400 Subject: [PATCH] Improve `noscript` spoofing Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/2642 --- src/js/scriptlets/noscript-spoof.js | 40 +++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/js/scriptlets/noscript-spoof.js b/src/js/scriptlets/noscript-spoof.js index a897df3c5..1abf8e069 100644 --- a/src/js/scriptlets/noscript-spoof.js +++ b/src/js/scriptlets/noscript-spoof.js @@ -33,6 +33,8 @@ const reMetaContent = /^\s*(\d+)\s*;\s*url=(?:"([^"]+)"|'([^']+)'|(.+))/i; const reSafeURL = /^https?:\/\//; + const reNoscript = /(^|[ >+~])noscript([ >+~]|$)/g; + const className = vAPI.sessionId; let redirectTimer; const autoRefresh = function(root) { @@ -55,30 +57,46 @@ }; const morphNoscript = function(from) { + const fragment = new DocumentFragment(); + const inHead = from.closest('head'); if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) { - const to = document.createElement('span'); while ( from.firstChild !== null ) { - to.appendChild(from.firstChild); + fragment.append(from.firstChild); } - return to; + return fragment; } const parser = new DOMParser(); const doc = parser.parseFromString( - '' + from.textContent + '', + ``, 'text/html' ); - return document.adoptNode(doc.querySelector('span')); + for ( const elem of doc.querySelectorAll(inHead ? 'span > *' : 'span') ) { + fragment.append(document.adoptNode(elem)); + } + return fragment; }; for ( const noscript of noscripts ) { - const parent = noscript.parentNode; - if ( parent === null ) { continue; } - const span = morphNoscript(noscript); - span.style.setProperty('display', 'inline', 'important'); + const fragment = morphNoscript(noscript); if ( redirectTimer === undefined ) { - autoRefresh(span); + autoRefresh(fragment); } - parent.replaceChild(span, noscript); + noscript.replaceWith(fragment); + } + + for ( const sheet of document.styleSheets ) { + try { + for ( const rule of sheet.cssRules ) { + if ( reNoscript.test(rule.selectorText) === false ) { continue; } + rule.selectorText = + rule.selectorText.replace(reNoscript, `$1span.${className}$2`); + } + } catch { + } + } + + for ( const span of document.querySelectorAll(`.${className}`) ) { + span.style.setProperty('display', 'inline', 'important'); } })();