diff options
Diffstat (limited to 'js/contentscript.js')
-rw-r--r-- | js/contentscript.js | 558 |
1 files changed, 279 insertions, 279 deletions
diff --git a/js/contentscript.js b/js/contentscript.js index 21fbcf1..9fb15f8 100644 --- a/js/contentscript.js +++ b/js/contentscript.js @@ -2,7 +2,7 @@ ηMatrix - a browser extension to black/white list requests. Copyright (C) 2014-2019 Raymond Hill - Copyright (C) 2019-2020 Alessio Vanni + Copyright (C) 2019-2022 Alessio Vanni 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 @@ -17,7 +17,7 @@ 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://libregit.spks.xyz/heckyel/ematrix + Home: https://gitlab.com/vannilla/ematrix uMatrix Home: https://github.com/gorhill/uMatrix */ @@ -29,54 +29,54 @@ // https://github.com/chrisaljoudi/uBlock/issues/464 // https://github.com/gorhill/uMatrix/issues/621 if (document instanceof HTMLDocument === false - && document instanceof XMLDocument === false) { - return; + && document instanceof XMLDocument === false) { + return; } // This can also happen (for example if script injected into a // `data:` URI doc) if (!window.location) { - return; + return; } // This can happen if (typeof vAPI !== 'object') { - //console.debug('contentscript.js > vAPI not found'); - return; + //console.debug('contentscript.js > vAPI not found'); + return; } // https://github.com/chrisaljoudi/uBlock/issues/456 // Already injected? if (vAPI.contentscriptEndInjected) { - //console.debug('contentscript.js > content script already injected'); - return; + //console.debug('contentscript.js > content script already injected'); + return; } vAPI.contentscriptEndInjected = true; // Executed only once. (function () { - let localStorageHandler = function (mustRemove) { + let localStorageHandler = function (mustRemove) { if (mustRemove) { - window.localStorage.clear(); - window.sessionStorage.clear(); + window.localStorage.clear(); + window.sessionStorage.clear(); } - }; + }; - // Check with extension whether local storage must be emptied - // rhill 2014-03-28: we need an exception handler in case - // 3rd-party access to site data is disabled. - // https://github.com/gorhill/httpswitchboard/issues/215 - try { + // Check with extension whether local storage must be emptied + // rhill 2014-03-28: we need an exception handler in case + // 3rd-party access to site data is disabled. + // https://github.com/gorhill/httpswitchboard/issues/215 + try { let hasLocalStorage = - window.localStorage && window.localStorage.length !== 0; + window.localStorage && window.localStorage.length !== 0; let hasSessionStorage = - window.sessionStorage && window.sessionStorage.length !== 0; + window.sessionStorage && window.sessionStorage.length !== 0; if (hasLocalStorage || hasSessionStorage) { - vAPI.messaging.send('contentscript.js', { + vAPI.messaging.send('contentscript.js', { what: 'contentScriptHasLocalStorage', originURL: window.location.origin - }, localStorageHandler); + }, localStorageHandler); } // TODO: indexedDB @@ -92,13 +92,13 @@ // "There is no way to enumerate or delete the databases available for an origin from this API." // Ref.: http://www.w3.org/TR/webdatabase/#databases // } - } catch (e) { - } + } catch (e) { + } })(); // https://github.com/gorhill/uMatrix/issues/45 let collapser = (function () { - let resquestIdGenerator = 1; + let resquestIdGenerator = 1; let processTimer; let toProcess = []; let toFilter = []; @@ -107,320 +107,320 @@ let cachedBlockedMapHash; let cachedBlockedMapTimer; let reURLPlaceholder = /\{\{url\}\}/g; - let src1stProps = { + let src1stProps = { embed: 'src', iframe: 'src', img: 'src', object: 'data', - }; - let src2ndProps = { + }; + let src2ndProps = { img: 'srcset', - }; - let tagToTypeMap = { + }; + let tagToTypeMap = { embed: 'media', iframe: 'frame', img: 'image', object: 'media', - }; - let cachedBlockedSetClear = function () { + }; + let cachedBlockedSetClear = function () { cachedBlockedMap = - cachedBlockedMapHash = - cachedBlockedMapTimer = undefined; - }; + cachedBlockedMapHash = + cachedBlockedMapTimer = undefined; + }; - // https://github.com/chrisaljoudi/uBlock/issues/174 - // Do not remove fragment from src URL - let onProcessed = function (response) { + // https://github.com/chrisaljoudi/uBlock/issues/174 + // Do not remove fragment from src URL + let onProcessed = function (response) { if (!response) { // This happens if uBO is disabled or restarted. - toCollapse.clear(); - return; + toCollapse.clear(); + return; } let targets = toCollapse.get(response.id); if (targets === undefined) { - return; - } + return; + } toCollapse.delete(response.id); if (cachedBlockedMapHash !== response.hash) { - cachedBlockedMap = new Map(response.blockedResources); - cachedBlockedMapHash = response.hash; - if (cachedBlockedMapTimer !== undefined) { + cachedBlockedMap = new Map(response.blockedResources); + cachedBlockedMapHash = response.hash; + if (cachedBlockedMapTimer !== undefined) { clearTimeout(cachedBlockedMapTimer); - } - cachedBlockedMapTimer = - vAPI.setTimeout(cachedBlockedSetClear, 30000); + } + cachedBlockedMapTimer = + vAPI.setTimeout(cachedBlockedSetClear, 30000); } if (cachedBlockedMap === undefined || cachedBlockedMap.size === 0) { - return; + return; } let placeholders = response.placeholders; for (let target of targets) { - let tag = target.localName; + let tag = target.localName; - let prop = src1stProps[tag]; - if (prop === undefined) { - continue; - } + let prop = src1stProps[tag]; + if (prop === undefined) { + continue; + } - let src = target[prop]; - if (typeof src !== 'string' || src.length === 0) { + let src = target[prop]; + if (typeof src !== 'string' || src.length === 0) { prop = src2ndProps[tag]; if (prop === undefined) { - continue; - } + continue; + } src = target[prop]; if (typeof src !== 'string' || src.length === 0) { - continue; - } - } - - let collapsed = cachedBlockedMap.get(tagToTypeMap[tag] - + ' ' - + src); - if (collapsed === undefined) { - continue; - } - - if (collapsed) { + continue; + } + } + + let collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + + ' ' + + src); + if (collapsed === undefined) { + continue; + } + + if (collapsed) { target.style.setProperty('display', 'none', 'important'); target.hidden = true; continue; - } + } - switch (tag) { - case 'iframe': + switch (tag) { + case 'iframe': if (placeholders.frame !== true) { - break; - } + break; + } let docurl = 'data:text/html,' - + encodeURIComponent(placeholders - .frameDocument - .replace(reURLPlaceholder, src)); + + encodeURIComponent(placeholders + .frameDocument + .replace(reURLPlaceholder, src)); let replaced = false; // Using contentWindow.location prevent tainting browser // history -- i.e. breaking back button (seen on Chromium). if (target.contentWindow) { - try { + try { target.contentWindow.location.replace(docurl); replaced = true; - } catch(ex) { - } + } catch(ex) { + } } if (!replaced) { - target.setAttribute('src', docurl); + target.setAttribute('src', docurl); } break; - case 'img': + case 'img': if (placeholders.image !== true) { - break; - } + break; + } target.style.setProperty('display', 'inline-block'); target.style.setProperty('min-width', '20px', 'important'); target.style.setProperty('min-height', '20px', 'important'); target.style.setProperty('border', placeholders.imageBorder, - 'important'); + 'important'); target.style.setProperty('background', - placeholders.imageBackground, - 'important'); + placeholders.imageBackground, + 'important'); break; - } + } } - }; + }; - let send = function () { + let send = function () { processTimer = undefined; toCollapse.set(resquestIdGenerator, toProcess); let msg = { - what: 'lookupBlockedCollapsibles', - id: resquestIdGenerator, - toFilter: toFilter, - hash: cachedBlockedMapHash + what: 'lookupBlockedCollapsibles', + id: resquestIdGenerator, + toFilter: toFilter, + hash: cachedBlockedMapHash }; vAPI.messaging.send('contentscript.js', msg, onProcessed); toProcess = []; toFilter = []; resquestIdGenerator += 1; - }; + }; - let process = function (delay) { + let process = function (delay) { if (toProcess.length === 0) { - return; - } + return; + } if (delay === 0) { - if (processTimer !== undefined) { + if (processTimer !== undefined) { clearTimeout(processTimer); - } - send(); + } + send(); } else if (processTimer === undefined) { - processTimer = vAPI.setTimeout(send, delay || 47); + processTimer = vAPI.setTimeout(send, delay || 47); } - }; + }; - let add = function (target) { + let add = function (target) { toProcess.push(target); - }; + }; - let addMany = function (targets) { - for (let i=targets.length-1; i>=0; --i) { - toProcess.push(targets[i]); + let addMany = function (targets) { + for (let i=targets.length-1; i>=0; --i) { + toProcess.push(targets[i]); } - }; + }; - let iframeSourceModified = function (mutations) { - for (let i=mutations.length-1; i>=0; --i) { - addIFrame(mutations[i].target, true); + let iframeSourceModified = function (mutations) { + for (let i=mutations.length-1; i>=0; --i) { + addIFrame(mutations[i].target, true); } process(); - }; + }; - let iframeSourceObserver; - let iframeSourceObserverOptions = { + let iframeSourceObserver; + let iframeSourceObserverOptions = { attributes: true, attributeFilter: [ 'src' ] - }; + }; - let addIFrame = function (iframe, dontObserve) { + let addIFrame = function (iframe, dontObserve) { // https://github.com/gorhill/uBlock/issues/162 // Be prepared to deal with possible change of src attribute. if (dontObserve !== true) { - if (iframeSourceObserver === undefined) { + if (iframeSourceObserver === undefined) { iframeSourceObserver = - new MutationObserver(iframeSourceModified); - } - iframeSourceObserver.observe(iframe, - iframeSourceObserverOptions); + new MutationObserver(iframeSourceModified); + } + iframeSourceObserver.observe(iframe, + iframeSourceObserverOptions); } let src = iframe.src; if (src === '' || typeof src !== 'string') { - return; - } + return; + } if (src.startsWith('http') === false) { - return; - } + return; + } toFilter.push({ - type: 'frame', - url: iframe.src, - }); + type: 'frame', + url: iframe.src, + }); add(iframe); - }; + }; - let addIFrames = function (iframes) { - for (let i=iframes.length-1; i>=0; --i) { - addIFrame(iframes[i]); + let addIFrames = function (iframes) { + for (let i=iframes.length-1; i>=0; --i) { + addIFrame(iframes[i]); } - }; - - let addNodeList = function (nodeList) { - for (let i=nodeList.length-1; i>=0; --i) { - let node = nodeList[i]; - if (node.nodeType !== 1) { - continue; - } - if (node.localName === 'iframe') { + }; + + let addNodeList = function (nodeList) { + for (let i=nodeList.length-1; i>=0; --i) { + let node = nodeList[i]; + if (node.nodeType !== 1) { + continue; + } + if (node.localName === 'iframe') { addIFrame(node); - } - if (node.childElementCount !== 0) { + } + if (node.childElementCount !== 0) { addIFrames(node.querySelectorAll('iframe')); - } + } } - }; + }; - let onResourceFailed = function (ev) { + let onResourceFailed = function (ev) { if (tagToTypeMap[ev.target.localName] !== undefined) { - add(ev.target); - process(); + add(ev.target); + process(); } - }; - document.addEventListener('error', onResourceFailed, true); + }; + document.addEventListener('error', onResourceFailed, true); - vAPI.shutdown.add(function () { + vAPI.shutdown.add(function () { document.removeEventListener('error', onResourceFailed, true); if (iframeSourceObserver !== undefined) { - iframeSourceObserver.disconnect(); - iframeSourceObserver = undefined; + iframeSourceObserver.disconnect(); + iframeSourceObserver = undefined; } if (processTimer !== undefined) { - clearTimeout(processTimer); - processTimer = undefined; + clearTimeout(processTimer); + processTimer = undefined; } - }); + }); - return { + return { addMany: addMany, addIFrames: addIFrames, addNodeList: addNodeList, process: process - }; + }; })(); // Observe changes in the DOM // Added node lists will be cumulated here before being processed (function () { - // This fixes http://acid3.acidtests.org/ - if (!document.body) { - return; - } + // This fixes http://acid3.acidtests.org/ + if (!document.body) { + return; + } - let addedNodeLists = []; - let addedNodeListsTimer; + let addedNodeLists = []; + let addedNodeListsTimer; - let treeMutationObservedHandler = function () { + let treeMutationObservedHandler = function () { addedNodeListsTimer = undefined; - for (let i=addedNodeLists.length-1; i>=0; --i) { - collapser.addNodeList(addedNodeLists[i]); + for (let i=addedNodeLists.length-1; i>=0; --i) { + collapser.addNodeList(addedNodeLists[i]); } collapser.process(); addedNodeLists = []; - }; - - // https://github.com/gorhill/uBlock/issues/205 - // Do not handle added node directly from within mutation observer. - let treeMutationObservedHandlerAsync = function (mutations) { - for (let i=mutations.length-1; i>=0; --i) { - let nodeList = mutations[i].addedNodes; - if (nodeList.length !== 0) { + }; + + // https://github.com/gorhill/uBlock/issues/205 + // Do not handle added node directly from within mutation observer. + let treeMutationObservedHandlerAsync = function (mutations) { + for (let i=mutations.length-1; i>=0; --i) { + let nodeList = mutations[i].addedNodes; + if (nodeList.length !== 0) { addedNodeLists.push(nodeList); - } + } } if (addedNodeListsTimer === undefined) { - addedNodeListsTimer = - vAPI.setTimeout(treeMutationObservedHandler, 47); + addedNodeListsTimer = + vAPI.setTimeout(treeMutationObservedHandler, 47); } - }; + }; - // https://github.com/gorhill/httpswitchboard/issues/176 - let treeObserver = - new MutationObserver(treeMutationObservedHandlerAsync); - treeObserver.observe(document.body, { + // https://github.com/gorhill/httpswitchboard/issues/176 + let treeObserver = + new MutationObserver(treeMutationObservedHandlerAsync); + treeObserver.observe(document.body, { childList: true, subtree: true - }); + }); - vAPI.shutdown.add(function () { + vAPI.shutdown.add(function () { if (addedNodeListsTimer !== undefined) { - clearTimeout(addedNodeListsTimer); - addedNodeListsTimer = undefined; + clearTimeout(addedNodeListsTimer); + addedNodeListsTimer = undefined; } if (treeObserver !== null) { - treeObserver.disconnect(); - treeObserver = undefined; + treeObserver.disconnect(); + treeObserver = undefined; } addedNodeLists = []; - }); + }); })(); // Executed only once. @@ -437,58 +437,58 @@ // Report inline styles. (function () { - if (document.querySelector('script:not([src])') !== 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) { + if (document.querySelector('script:not([src])') !== 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) { vAPI.messaging.send('contentscript.js', { - what: 'securityPolicyViolation', - directive: 'script-src', - documentURI: window.location.href + what: 'securityPolicyViolation', + directive: 'script-src', + documentURI: window.location.href }); - } + } - if (document.querySelector('style,[style]') !== null) { + if (document.querySelector('style,[style]') !== null) { vAPI.messaging.send('contentscript.js', { - what: 'securityPolicyViolation', - directive: 'style-src', - documentURI: window.location.href + what: 'securityPolicyViolation', + directive: 'style-src', + documentURI: window.location.href }); - } + } - collapser.addMany(document.querySelectorAll('img')); - collapser.addIFrames(document.querySelectorAll('iframe')); - collapser.process(); + collapser.addMany(document.querySelectorAll('img')); + collapser.addIFrames(document.querySelectorAll('iframe')); + collapser.process(); })(); // Executed only once. @@ -496,88 +496,88 @@ // Force `display` property, Firefox is still affected by the issue. (function () { - let noscripts = document.querySelectorAll('noscript'); - if (noscripts.length === 0) { - return; - } + let noscripts = document.querySelectorAll('noscript'); + if (noscripts.length === 0) { + return; + } - let redirectTimer; + let redirectTimer; let reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i; let reSafeURL = /^https?:\/\//; - let autoRefresh = function (root) { + let autoRefresh = function (root) { let meta = - root.querySelector('meta[http-equiv="refresh"][content]'); + root.querySelector('meta[http-equiv="refresh"][content]'); if (meta === null) { - return; - } + return; + } let match = reMetaContent.exec(meta.getAttribute('content')); if (match === null || match[3].trim() === '') { - return; - } + return; + } let url = new URL(match[3], document.baseURI); if (reSafeURL.test(url.href) === false) { - return; - } + return; + } redirectTimer = setTimeout(function () { location.assign(url.href); - }, parseInt(match[1], 10) * 1000 + 1); + }, parseInt(match[1], 10) * 1000 + 1); meta.parentNode.removeChild(meta); - }; + }; - let morphNoscript = function (from) { + let morphNoscript = function (from) { if (/^application\/(?:xhtml\+)?xml/.test(document.contentType)) { - let to = document.createElement('span'); - while (from.firstChild !== null) { + let to = document.createElement('span'); + while (from.firstChild !== null) { to.appendChild(from.firstChild); - } - return to; + } + return to; } let parser = new DOMParser(); let doc = - parser.parseFromString('<span>' + from.textContent + '</span>', - 'text/html'); + parser.parseFromString('<span>' + from.textContent + '</span>', + 'text/html'); return document.adoptNode(doc.querySelector('span')); - }; + }; - let renderNoscriptTags = function (response) { + let renderNoscriptTags = function (response) { if (response !== true) { - return; - } + return; + } for (let noscript of noscripts) { - let parent = noscript.parentNode; - if (parent === null) { - continue; - } + let parent = noscript.parentNode; + if (parent === null) { + continue; + } - let span = morphNoscript(noscript); - span.style.setProperty('display', 'inline', 'important'); + let span = morphNoscript(noscript); + span.style.setProperty('display', 'inline', 'important'); - if (redirectTimer === undefined) { + if (redirectTimer === undefined) { autoRefresh(span); - } + } - parent.replaceChild(span, noscript); + parent.replaceChild(span, noscript); } - }; + }; - vAPI.messaging.send('contentscript.js', { - what: 'mustRenderNoscriptTags?' - }, renderNoscriptTags); + vAPI.messaging.send('contentscript.js', { + what: 'mustRenderNoscriptTags?' + }, renderNoscriptTags); })(); vAPI.messaging.send('contentscript.js', { - what: 'shutdown?' + what: 'shutdown?' }, function (response) { if (response === true) { - vAPI.shutdown.exec(); + vAPI.shutdown.exec(); } }); })(); |