diff options
Diffstat (limited to 'js/contentscript.js')
-rw-r--r-- | js/contentscript.js | 517 |
1 files changed, 279 insertions, 238 deletions
diff --git a/js/contentscript.js b/js/contentscript.js index 650d1be..c304fdf 100644 --- a/js/contentscript.js +++ b/js/contentscript.js @@ -21,70 +21,58 @@ uMatrix Home: https://github.com/gorhill/uMatrix */ -/* global HTMLDocument, XMLDocument */ - 'use strict'; -/******************************************************************************/ -/******************************************************************************/ - // Injected into content pages -(function() { - - /******************************************************************************/ - +(function () { // https://github.com/chrisaljoudi/uBlock/issues/464 // https://github.com/gorhill/uMatrix/issues/621 - if ( - document instanceof HTMLDocument === false && - document instanceof XMLDocument === false - ) { + if (document instanceof HTMLDocument === false + && document instanceof XMLDocument === false) { return; } - // This can also happen (for example if script injected into a `data:` URI doc) - if ( !window.location ) { + // This can also happen (for example if script injected into a + // `data:` URI doc) + if (!window.location) { return; } // This can happen - if ( typeof vAPI !== 'object' ) { + if (typeof vAPI !== 'object') { //console.debug('contentscript.js > vAPI not found'); return; } // https://github.com/chrisaljoudi/uBlock/issues/456 // Already injected? - if ( vAPI.contentscriptEndInjected ) { + if (vAPI.contentscriptEndInjected) { //console.debug('contentscript.js > content script already injected'); return; } vAPI.contentscriptEndInjected = true; - /******************************************************************************/ - /******************************************************************************/ - // Executed only once. - - (function() { - var localStorageHandler = function(mustRemove) { - if ( mustRemove ) { + (function () { + let localStorageHandler = function (mustRemove) { + if (mustRemove) { 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. + // 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 { - var hasLocalStorage = + let hasLocalStorage = window.localStorage && window.localStorage.length !== 0; - var hasSessionStorage = + let hasSessionStorage = window.sessionStorage && window.sessionStorage.length !== 0; - if ( hasLocalStorage || hasSessionStorage ) { + + if (hasLocalStorage || hasSessionStorage) { vAPI.messaging.send('contentscript.js', { what: 'contentScriptHasLocalStorage', originURL: window.location.origin @@ -104,140 +92,154 @@ // "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 - - var collapser = (function() { - var resquestIdGenerator = 1, - processTimer, - toProcess = [], - toFilter = [], - toCollapse = new Map(), - cachedBlockedMap, - cachedBlockedMapHash, - cachedBlockedMapTimer, - reURLPlaceholder = /\{\{url\}\}/g; - var src1stProps = { - 'embed': 'src', - 'iframe': 'src', - 'img': 'src', - 'object': 'data' + let collapser = (function () { + let resquestIdGenerator = 1; + let processTimer; + let toProcess = []; + let toFilter = []; + let toCollapse = new Map(); + let cachedBlockedMap; + let cachedBlockedMapHash; + let cachedBlockedMapTimer; + let reURLPlaceholder = /\{\{url\}\}/g; + let src1stProps = { + embed: 'src', + iframe: 'src', + img: 'src', + object: 'data', }; - var src2ndProps = { - 'img': 'srcset' + let src2ndProps = { + img: 'srcset', }; - var tagToTypeMap = { + let tagToTypeMap = { embed: 'media', iframe: 'frame', img: 'image', - object: 'media' + object: 'media', }; - var cachedBlockedSetClear = function() { + let cachedBlockedSetClear = function () { cachedBlockedMap = cachedBlockedMapHash = cachedBlockedMapTimer = undefined; }; // https://github.com/chrisaljoudi/uBlock/issues/174 - // Do not remove fragment from src URL - var onProcessed = function(response) { - if ( !response ) { // This happens if uBO is disabled or restarted. + // Do not remove fragment from src URL + let onProcessed = function (response) { + if (!response) { // This happens if uBO is disabled or restarted. toCollapse.clear(); return; } - var targets = toCollapse.get(response.id); - if ( targets === undefined ) { return; } + let targets = toCollapse.get(response.id); + if (targets === undefined) { + return; + } + toCollapse.delete(response.id); - if ( cachedBlockedMapHash !== response.hash ) { + + if (cachedBlockedMapHash !== response.hash) { cachedBlockedMap = new Map(response.blockedResources); cachedBlockedMapHash = response.hash; - if ( cachedBlockedMapTimer !== undefined ) { + if (cachedBlockedMapTimer !== undefined) { clearTimeout(cachedBlockedMapTimer); } - cachedBlockedMapTimer = vAPI.setTimeout(cachedBlockedSetClear, 30000); + cachedBlockedMapTimer = + vAPI.setTimeout(cachedBlockedSetClear, 30000); } - if ( cachedBlockedMap === undefined || cachedBlockedMap.size === 0 ) { + + if (cachedBlockedMap === undefined || cachedBlockedMap.size === 0) { return; } - var placeholders = response.placeholders, - tag, prop, src, collapsed, docurl, replaced; + let placeholders = response.placeholders; - for ( var target of targets ) { - tag = target.localName; - prop = src1stProps[tag]; - if ( prop === undefined ) { continue; } - src = target[prop]; - if ( typeof src !== 'string' || src.length === 0 ) { + for (let target of targets) { + let tag = target.localName; + + let prop = src1stProps[tag]; + if (prop === undefined) { + continue; + } + + let src = target[prop]; + if (typeof src !== 'string' || src.length === 0) { prop = src2ndProps[tag]; - if ( prop === undefined ) { continue; } + if (prop === undefined) { + continue; + } src = target[prop]; - if ( typeof src !== 'string' || src.length === 0 ) { continue; } + if (typeof src !== 'string' || src.length === 0) { + continue; + } } - collapsed = cachedBlockedMap.get(tagToTypeMap[tag] + ' ' + src); - if ( collapsed === undefined ) { continue; } - if ( collapsed ) { + + 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 ) { + + switch (tag) { case 'iframe': - if ( placeholders.frame !== true ) { break; } - docurl = - 'data:text/html,' + - encodeURIComponent( - placeholders.frameDocument.replace( - reURLPlaceholder, - src - ) - ); + if (placeholders.frame !== true) { + break; + } + + docurl = 'data:text/html,' + + encodeURIComponent(placeholders + .frameDocument + .replace(reURLPlaceholder, src)); replaced = false; + // Using contentWindow.location prevent tainting browser // history -- i.e. breaking back button (seen on Chromium). - if ( target.contentWindow ) { + if (target.contentWindow) { try { target.contentWindow.location.replace(docurl); replaced = true; } catch(ex) { } } - if ( !replaced ) { + + if (!replaced) { target.setAttribute('src', docurl); } break; case 'img': - if ( placeholders.image !== true ) { break; } + if (placeholders.image !== true) { + 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' - ); - target.style.setProperty( - 'background', - placeholders.imageBackground, - 'important' - ); + target.style.setProperty('border', placeholders.imageBorder, + 'important'); + target.style.setProperty('background', + placeholders.imageBackground, + 'important'); break; } } }; - var send = function() { + let send = function () { processTimer = undefined; toCollapse.set(resquestIdGenerator, toProcess); - var msg = { + let msg = { what: 'lookupBlockedCollapsibles', id: resquestIdGenerator, toFilter: toFilter, @@ -249,95 +251,108 @@ resquestIdGenerator += 1; }; - var process = function(delay) { - if ( toProcess.length === 0 ) { return; } - if ( delay === 0 ) { - if ( processTimer !== undefined ) { + let process = function (delay) { + if (toProcess.length === 0) { + return; + } + + if (delay === 0) { + if (processTimer !== undefined) { clearTimeout(processTimer); } send(); - } else if ( processTimer === undefined ) { + } else if (processTimer === undefined) { processTimer = vAPI.setTimeout(send, delay || 47); } }; - var add = function(target) { + let add = function (target) { toProcess.push(target); }; - var addMany = function(targets) { - var i = targets.length; - while ( i-- ) { + let addMany = function (targets) { + for (let i=targets.length-1; i>=0; --i) { toProcess.push(targets[i]); } }; - var iframeSourceModified = function(mutations) { - var i = mutations.length; - while ( i-- ) { + let iframeSourceModified = function (mutations) { + for (let i=mutations.length-1; i>=0; --i) { addIFrame(mutations[i].target, true); } process(); }; - var iframeSourceObserver; - var iframeSourceObserverOptions = { + + let iframeSourceObserver; + let iframeSourceObserverOptions = { attributes: true, attributeFilter: [ 'src' ] }; - var 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 ) { - iframeSourceObserver = new MutationObserver(iframeSourceModified); + if (dontObserve !== true) { + if (iframeSourceObserver === undefined) { + iframeSourceObserver = + new MutationObserver(iframeSourceModified); } - iframeSourceObserver.observe(iframe, iframeSourceObserverOptions); + iframeSourceObserver.observe(iframe, + iframeSourceObserverOptions); + } + + let src = iframe.src; + if (src === '' || typeof src !== 'string') { + return; + } + if (src.startsWith('http') === false) { + return; } - var src = iframe.src; - if ( src === '' || typeof src !== 'string' ) { return; } - if ( src.startsWith('http') === false ) { return; } - toFilter.push({ type: 'frame', url: iframe.src }); + + toFilter.push({ + type: 'frame', + url: iframe.src, + }); + add(iframe); }; - var addIFrames = function(iframes) { - var i = iframes.length; - while ( i-- ) { + let addIFrames = function (iframes) { + for (let i=iframes.length-1; i>=0; --i) { addIFrame(iframes[i]); } }; - var addNodeList = function(nodeList) { - var node, - i = nodeList.length; - while ( i-- ) { - 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')); } } }; - var onResourceFailed = function(ev) { - if ( tagToTypeMap[ev.target.localName] !== undefined ) { + let onResourceFailed = function (ev) { + if (tagToTypeMap[ev.target.localName] !== undefined) { add(ev.target); process(); } }; document.addEventListener('error', onResourceFailed, true); - vAPI.shutdown.add(function() { + vAPI.shutdown.add(function () { document.removeEventListener('error', onResourceFailed, true); - if ( iframeSourceObserver !== undefined ) { + if (iframeSourceObserver !== undefined) { iframeSourceObserver.disconnect(); iframeSourceObserver = undefined; } - if ( processTimer !== undefined ) { + if (processTimer !== undefined) { clearTimeout(processTimer); processTimer = undefined; } @@ -351,24 +366,21 @@ }; })(); - /******************************************************************************/ - /******************************************************************************/ - // Observe changes in the DOM - // Added node lists will be cumulated here before being processed - (function() { + (function () { // This fixes http://acid3.acidtests.org/ - if ( !document.body ) { return; } + if (!document.body) { + return; + } - var addedNodeLists = []; - var addedNodeListsTimer; + let addedNodeLists = []; + let addedNodeListsTimer; - var treeMutationObservedHandler = function() { + let treeMutationObservedHandler = function () { addedNodeListsTimer = undefined; - var i = addedNodeLists.length; - while ( i-- ) { + for (let i=addedNodeLists.length-1; i>=0; --i) { collapser.addNodeList(addedNodeLists[i]); } collapser.process(); @@ -377,33 +389,33 @@ // https://github.com/gorhill/uBlock/issues/205 // Do not handle added node directly from within mutation observer. - var treeMutationObservedHandlerAsync = function(mutations) { - var iMutation = mutations.length, - nodeList; - while ( iMutation-- ) { - nodeList = mutations[iMutation].addedNodes; - if ( nodeList.length !== 0 ) { + 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); + if (addedNodeListsTimer === undefined) { + addedNodeListsTimer = + vAPI.setTimeout(treeMutationObservedHandler, 47); } }; // https://github.com/gorhill/httpswitchboard/issues/176 - var treeObserver = new MutationObserver(treeMutationObservedHandlerAsync); + let treeObserver = + new MutationObserver(treeMutationObservedHandlerAsync); treeObserver.observe(document.body, { childList: true, subtree: true }); - vAPI.shutdown.add(function() { - if ( addedNodeListsTimer !== undefined ) { + vAPI.shutdown.add(function () { + if (addedNodeListsTimer !== undefined) { clearTimeout(addedNodeListsTimer); addedNodeListsTimer = undefined; } - if ( treeObserver !== null ) { + if (treeObserver !== null) { treeObserver.disconnect(); treeObserver = undefined; } @@ -411,9 +423,6 @@ }); })(); - /******************************************************************************/ - /******************************************************************************/ - // Executed only once. // // https://github.com/gorhill/httpswitchboard/issues/25 @@ -427,12 +436,41 @@ // https://github.com/gorhill/uMatrix/issues/924 // 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 - ) { + (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) { vAPI.messaging.send('contentscript.js', { what: 'securityPolicyViolation', directive: 'script-src', @@ -440,7 +478,7 @@ }); } - if ( document.querySelector('style,[style]') !== null ) { + if (document.querySelector('style,[style]') !== null) { vAPI.messaging.send('contentscript.js', { what: 'securityPolicyViolation', directive: 'style-src', @@ -453,90 +491,93 @@ collapser.process(); })(); - /******************************************************************************/ - /******************************************************************************/ - // Executed only once. - // https://github.com/gorhill/uMatrix/issues/232 - // Force `display` property, Firefox is still affected by the issue. - - (function() { - var noscripts = document.querySelectorAll('noscript'); - if ( noscripts.length === 0 ) { return; } - - var redirectTimer, - reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i, - reSafeURL = /^https?:\/\//; - - var autoRefresh = function(root) { - var meta = root.querySelector('meta[http-equiv="refresh"][content]'); - if ( meta === null ) { return; } - var match = reMetaContent.exec(meta.getAttribute('content')); - if ( match === null || match[3].trim() === '' ) { return; } - var url = new URL(match[3], document.baseURI); - if ( reSafeURL.test(url.href) === false ) { return; } - redirectTimer = setTimeout( - function() { - location.assign(url.href); - }, - parseInt(match[1], 10) * 1000 + 1 - ); + // Force `display` property, Firefox is still affected by the issue. + + (function () { + let noscripts = document.querySelectorAll('noscript'); + if (noscripts.length === 0) { + return; + } + + let redirectTimer; + let reMetaContent = /^\s*(\d+)\s*;\s*url=(['"]?)([^'"]+)\2/i; + let 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(function () { + location.assign(url.href); + }, parseInt(match[1], 10) * 1000 + 1); + meta.parentNode.removeChild(meta); }; - var morphNoscript = function(from) { - if ( /^application\/(?:xhtml\+)?xml/.test(document.contentType) ) { - var to = document.createElement('span'); - while ( from.firstChild !== null ) { + 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; } - var parser = new DOMParser(); - var doc = parser.parseFromString( - '<span>' + from.textContent + '</span>', - 'text/html' - ); + + let parser = new DOMParser(); + let doc = + parser.parseFromString('<span>' + from.textContent + '</span>', + 'text/html'); + return document.adoptNode(doc.querySelector('span')); }; - var renderNoscriptTags = function(response) { - if ( response !== true ) { return; } - var parent, span; - for ( var noscript of noscripts ) { - parent = noscript.parentNode; - if ( parent === null ) { continue; } - span = morphNoscript(noscript); + let renderNoscriptTags = function (response) { + if (response !== true) { + return; + } + + 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 ) { + + if (redirectTimer === undefined) { autoRefresh(span); } + 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?' }, - function(response) { - if ( response === true ) { - vAPI.shutdown.exec(); - } + vAPI.messaging.send('contentscript.js', { + what: 'shutdown?' + }, function (response) { + if (response === true) { + vAPI.shutdown.exec(); } - ); - - /******************************************************************************/ - /******************************************************************************/ - + }); })(); |