diff --git a/Sources/BrowserServicesKit/ContentBlocking/UserScripts/surrogates.js b/Sources/BrowserServicesKit/ContentBlocking/UserScripts/surrogates.js index c76d0ff71..b15153721 100644 --- a/Sources/BrowserServicesKit/ContentBlocking/UserScripts/surrogates.js +++ b/Sources/BrowserServicesKit/ContentBlocking/UserScripts/surrogates.js @@ -1,5 +1,5 @@ // -// contentblocker.js +// surrogates.js // DuckDuckGo // // Copyright © 2017 DuckDuckGo. All rights reserved. @@ -466,8 +466,6 @@ } } - const loadedSurrogates = {} - // private function loadSurrogate (surrogatePattern) { trackers.surrogateList[surrogatePattern]() @@ -475,7 +473,6 @@ // public function shouldBlock (trackerUrl, type, element) { - seenUrls.add(trackerUrl) const startTime = performance.now() if (!blockingEnabled) { @@ -508,15 +505,15 @@ if (element && element.onerror) { element.onerror = () => {} } - if (!loadedSurrogates[result.matchedRule.surrogate]) { + try { loadSurrogate(result.matchedRule.surrogate) - loadedSurrogates[result.matchedRule.surrogate] = true // Trigger a load event on the original element if (element && element.onload) { element.onload(new Event('load')) } + } catch (e) { + duckduckgoDebugMessaging.log(`error loading surrogate: ${e.toString()}`) } - const pageUrl = window.location.href surrogateInjected({ url: trackerUrl, @@ -538,108 +535,28 @@ return false } - const seenUrls = new Set() - function hasNotSeen (url) { - // Ignore elements with no url - if (!url) { - return false - } - return !seenUrls.has(url) - } - - function processPage () { - [...document.scripts].filter((el) => hasNotSeen(el.src)).forEach((el) => { - if (shouldBlock(el.src, 'script', el)) { - duckduckgoDebugMessaging.log('blocking load') - } - }); - [...document.images].filter((el) => hasNotSeen(el.src)).forEach((el) => { - // If the image's natural width is zero, then it has not loaded so we - // can assume that it may have been blocked. - if (el.naturalWidth === 0) { - if (shouldBlock(el.src, 'image', el)) { - duckduckgoDebugMessaging.log('blocking load') - } - } - }); - [...document.querySelectorAll('link')].filter((el) => hasNotSeen(el.href)).forEach((el) => { - if (shouldBlock(el.href, el.rel, el)) { - duckduckgoDebugMessaging.log('blocking load') - } - }); - [...document.querySelectorAll('iframe')].filter((el) => hasNotSeen(el.src)).forEach((el) => { - if (shouldBlock(el.src, 'subdocument', el)) { - duckduckgoDebugMessaging.log('blocking load') - } - }) - } - - function debounce (func, wait) { - let timeout - return function () { - clearTimeout(timeout) - timeout = setTimeout(() => { - func.apply(this, arguments) - }, wait) - } - } - - const observer = new MutationObserver(debounce((mutations, o) => { - processPage() - }, 100)) - const rootElement = document.body || document.documentElement - observer.observe(rootElement, { childList: true, subtree: true }); - - // Init - (function () { - duckduckgoDebugMessaging.log('installing load detection') - window.addEventListener('load', function (event) { - processPage() - }, false) - - try { - duckduckgoDebugMessaging.log('installing image src detection') - - const originalImageSrc = Object.getOwnPropertyDescriptor(Image.prototype, 'src') - Object.defineProperty(Image.prototype, 'src', { - writable: true, // Needs to be writable for the content blocking rules script. Will be locked down in that script - get: function () { - return originalImageSrc.get.call(this) - }, - set: function (value) { - const instance = this - if (shouldBlock(value, 'image')) { - duckduckgoDebugMessaging.log('blocking image src: ' + value) - } else { - originalImageSrc.set.call(instance, value) + const observer = new MutationObserver((records) => { + for (const record of records) { + record.addedNodes.forEach((node) => { + if (node instanceof HTMLScriptElement) { + if (shouldBlock(node.src, 'script', node)) { + duckduckgoDebugMessaging.log('blocking load') } } }) - } catch (error) { - duckduckgoDebugMessaging.log('failed to install image src detection') - } - - try { - duckduckgoDebugMessaging.log('installing xhr detection') - - const xhr = XMLHttpRequest.prototype - const originalOpen = xhr.open - - xhr.open = function () { - const args = arguments - const url = arguments[1] - if (shouldBlock(url, 'xmlhttprequest')) { - args[1] = 'about:blank' + if (record.target instanceof HTMLScriptElement) { + if (shouldBlock(record.target.src, 'script', record.target)) { + duckduckgoDebugMessaging.log('blocking load') } - duckduckgoDebugMessaging.log('sending xhr ' + url + ' to ' + args[1]) - return originalOpen.apply(this, args) } - } catch (error) { - duckduckgoDebugMessaging.log('failed to install xhr detection') } - - duckduckgoDebugMessaging.log('content blocking initialised') - })() + }) + const rootElement = document.body || document.documentElement + observer.observe(rootElement, { + childList: true, + subtree: true, + attributeFilter: ['src'] + }); return { shouldBlock: shouldBlock