Refined Popup Blocker

Differentiates between good and bad pop-ups by monitoring user interactions and dynamically added content. Aggressively blocks unwanted pop-ups while allowing user-initiated ones. Prompts user to blacklist sites when pop-ups are blocked. Press Alt+0 to remove the site and its subdomains from both the whitelist and blacklist.

  1. // ==UserScript==
  2. // @name Refined Popup Blocker
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.7.5
  5. // @description Differentiates between good and bad pop-ups by monitoring user interactions and dynamically added content. Aggressively blocks unwanted pop-ups while allowing user-initiated ones. Prompts user to blacklist sites when pop-ups are blocked. Press Alt+0 to remove the site and its subdomains from both the whitelist and blacklist.
  6. // @match *://*/*
  7. // @grant GM_getValue
  8. // @grant GM_setValue
  9. // @run-at document-start
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. const LOG_PREFIX = '[Tampermonkey Popup Blocker]';
  17. let userInitiated = false;
  18. let blacklistedWebsites = getBlacklistedWebsites();
  19. let whitelistedWebsites = getWhitelistedWebsites();
  20. let promptedWebsites = new Set(); // Track prompted websites for the session
  21.  
  22. function log(message) {
  23. console.log(`${LOG_PREFIX} ${message}`);
  24. }
  25.  
  26. function getBlacklistedWebsites() {
  27. try {
  28. return new Set(JSON.parse(GM_getValue('blacklistedWebsites', '[]')));
  29. } catch (e) {
  30. log('Error parsing blacklist from storage: ' + e);
  31. return new Set();
  32. }
  33. }
  34.  
  35. function getWhitelistedWebsites() {
  36. try {
  37. return new Set(JSON.parse(GM_getValue('whitelistedWebsites', '[]')));
  38. } catch (e) {
  39. log('Error parsing whitelist from storage: ' + e);
  40. return new Set();
  41. }
  42. }
  43.  
  44. function saveBlacklistedWebsites(blacklistedWebsites) {
  45. GM_setValue('blacklistedWebsites', JSON.stringify([...blacklistedWebsites]));
  46. log('Blacklist saved');
  47. }
  48.  
  49. function saveWhitelistedWebsites(whitelistedWebsites) {
  50. GM_setValue('whitelistedWebsites', JSON.stringify([...whitelistedWebsites]));
  51. log('Whitelist saved');
  52. }
  53.  
  54. function addToBlacklist(hostname) {
  55. blacklistedWebsites.add(hostname);
  56. saveBlacklistedWebsites(blacklistedWebsites);
  57. log(`Added ${hostname} to blacklist`);
  58. }
  59.  
  60. function addToWhitelist(hostname) {
  61. whitelistedWebsites.add(hostname);
  62. saveWhitelistedWebsites(whitelistedWebsites);
  63. log(`Added ${hostname} to whitelist`);
  64. }
  65.  
  66. function removeFromLists(hostname) {
  67. let foundInBlacklist = false;
  68. let foundInWhitelist = false;
  69.  
  70. blacklistedWebsites.forEach(site => {
  71. if (hostname.endsWith(site) || site.endsWith(hostname)) {
  72. blacklistedWebsites.delete(site);
  73. foundInBlacklist = true;
  74. }
  75. });
  76.  
  77. whitelistedWebsites.forEach(site => {
  78. if (hostname.endsWith(site) || site.endsWith(hostname)) {
  79. whitelistedWebsites.delete(site);
  80. foundInWhitelist = true;
  81. }
  82. });
  83.  
  84. saveBlacklistedWebsites(blacklistedWebsites);
  85. saveWhitelistedWebsites(whitelistedWebsites);
  86.  
  87. return { foundInBlacklist, foundInWhitelist };
  88. }
  89.  
  90. function isBlacklisted(url) {
  91. try {
  92. const parsedUrl = new URL(url, location.origin);
  93. const hostname = parsedUrl.hostname;
  94. for (let site of blacklistedWebsites) {
  95. if (hostname.endsWith(site)) {
  96. return true;
  97. }
  98. }
  99. return false;
  100. } catch (e) {
  101. log('Error checking blacklist: ' + e);
  102. return false;
  103. }
  104. }
  105.  
  106. function isWhitelisted(url) {
  107. try {
  108. const parsedUrl = new URL(url, location.origin);
  109. const hostname = parsedUrl.hostname;
  110. for (let site of whitelistedWebsites) {
  111. if (hostname.endsWith(site)) {
  112. return true;
  113. }
  114. }
  115. return false;
  116. } catch (e) {
  117. log('Error checking whitelist: ' + e);
  118. return false;
  119. }
  120. }
  121.  
  122. function allowPopup(url) {
  123. log('Allowed popup: ' + url);
  124. }
  125.  
  126. function blockPopup(url) {
  127. try {
  128. const parsedUrl = new URL(url, location.origin);
  129. const hostname = parsedUrl.hostname;
  130. log('Blocked popup: ' + url);
  131. if (!blacklistedWebsites.has(hostname) && !whitelistedWebsites.has(hostname) && !promptedWebsites.has(hostname)) {
  132. promptedWebsites.add(hostname);
  133. setTimeout(() => {
  134. if (confirm(`A popup from ${hostname} was blocked. Do you want to block pop-ups from this site in the future?`)) {
  135. addToBlacklist(hostname);
  136. location.reload();
  137. } else {
  138. addToWhitelist(hostname);
  139. location.reload();
  140. }
  141. }, 0);
  142. }
  143. } catch (e) {
  144. log('Error blocking popup: ' + e);
  145. }
  146. }
  147.  
  148. // Intercept window.open calls
  149. const originalOpen = window.open;
  150. window.open = function (url, name, specs) {
  151. const hostname = new URL(url, location.origin).hostname;
  152. log(`window.open called with url: ${url}`);
  153. if (userInitiated || isWhitelisted(hostname)) {
  154. allowPopup(url);
  155. const newWindow = originalOpen.call(this, url, name, specs);
  156. monitorNewWindow(newWindow);
  157. return newWindow;
  158. } else {
  159. blockPopup(url);
  160. return null;
  161. }
  162. };
  163.  
  164. function monitorNewWindow(win) {
  165. const interval = setInterval(() => {
  166. try {
  167. if (win.location.href === 'about:blank') {
  168. log('Closed window that navigated to about:blank');
  169. win.close();
  170. clearInterval(interval);
  171. } else if (win.closed) {
  172. clearInterval(interval);
  173. }
  174. } catch (e) {
  175. clearInterval(interval);
  176. }
  177. }, 100);
  178. }
  179.  
  180. // Listen for user-initiated actions
  181. function setUserInitiated(event) {
  182. userInitiated = true;
  183. log(`User-initiated action detected for element: ${event.target.tagName}, class: ${event.target.className}`);
  184. setTimeout(() => {
  185. userInitiated = false;
  186. log('User-initiated action reset');
  187. }, 1000);
  188. }
  189.  
  190. document.addEventListener('click', setUserInitiated, true);
  191. document.addEventListener('submit', setUserInitiated, true);
  192. document.addEventListener('keydown', setUserInitiated, true);
  193.  
  194. // Ensure whitelisted sites are respected
  195. if (isWhitelisted(location.hostname)) {
  196. log(`Site ${location.hostname} is whitelisted. Pop-up blocker is disabled.`);
  197. } else {
  198. // Continue with blocking logic if not whitelisted
  199. const observer = new MutationObserver(function (mutations) {
  200. mutations.forEach(function (mutation) {
  201. mutation.addedNodes.forEach(function (node) {
  202. if (node.tagName === 'IFRAME' || node.tagName === 'SCRIPT') {
  203. const src = node.getAttribute('src');
  204. log(`Dynamically added element: ${node.tagName}, src: ${src}`);
  205. if (!src || src === 'about:blank' || isBlacklisted(src)) {
  206. log(`Blocked dynamically added element: ${node.tagName}, src: ${src}`);
  207. blockPopup(src || 'about:blank');
  208. node.remove();
  209. }
  210. }
  211. });
  212. });
  213. });
  214.  
  215. observer.observe(document.documentElement, { childList: true, subtree: true });
  216.  
  217. function blockInlinePopups() {
  218. const open = window.open;
  219. window.open = function (url, name, specs) {
  220. const hostname = new URL(url, location.origin).hostname;
  221. log(`Inline window.open called with url: ${url}`);
  222. if (userInitiated || isWhitelisted(hostname)) {
  223. allowPopup(url);
  224. const newWindow = open.call(this, url, name, specs);
  225. monitorNewWindow(newWindow);
  226. return newWindow;
  227. } else {
  228. blockPopup(url);
  229. return null;
  230. }
  231. };
  232. }
  233.  
  234. blockInlinePopups();
  235.  
  236. const iframeObserver = new MutationObserver((mutations) => {
  237. mutations.forEach((mutation) => {
  238. mutation.addedNodes.forEach((node) => {
  239. if (node.tagName === 'IFRAME' && node.src === 'about:blank') {
  240. log('Blocked iframe with src about:blank');
  241. blockPopup('about:blank');
  242. node.remove();
  243. }
  244. });
  245. });
  246. });
  247.  
  248. iframeObserver.observe(document.documentElement, { childList: true, subtree: true });
  249.  
  250. function overwriteSuspiciousFunctions() {
  251. const suspiciousFunctions = ['setTimeout', 'setInterval', 'open'];
  252. suspiciousFunctions.forEach((func) => {
  253. const originalFunc = window[func];
  254. window[func] = function (...args) {
  255. log(`${func} called with arguments: ${args}`);
  256. if (typeof args[0] === 'string' && args[0].includes('window.open')) {
  257. log(`Blocked ${func} containing window.open`);
  258. blockPopup('about:blank');
  259. return null;
  260. }
  261. return originalFunc.apply(this, args);
  262. };
  263. });
  264. }
  265.  
  266. overwriteSuspiciousFunctions();
  267. }
  268.  
  269. // Listen for Alt+0 keypress to remove site from both lists
  270. document.addEventListener('keydown', (event) => {
  271. if (event.altKey && event.key === '0') {
  272. const hostname = location.hostname;
  273. let { foundInBlacklist, foundInWhitelist } = removeFromLists(hostname);
  274.  
  275. if (foundInBlacklist || foundInWhitelist) {
  276. alert(`${hostname} and its subdomains have been removed from the ${foundInBlacklist ? 'blacklist' : ''} ${foundInWhitelist ? 'whitelist' : ''}.`);
  277. } else {
  278. let subdomainFound = false;
  279. blacklistedWebsites.forEach(site => {
  280. if (hostname.endsWith(site) || site.endsWith(hostname)) {
  281. blacklistedWebsites.delete(site);
  282. subdomainFound = true;
  283. }
  284. });
  285.  
  286. whitelistedWebsites.forEach(site => {
  287. if (hostname.endsWith(site) || site.endsWith(hostname)) {
  288. whitelistedWebsites.delete(site);
  289. subdomainFound = true;
  290. }
  291. });
  292.  
  293. saveBlacklistedWebsites(blacklistedWebsites);
  294. saveWhitelistedWebsites(whitelistedWebsites);
  295.  
  296. if (subdomainFound) {
  297. alert(`Subdomains of ${hostname} were found and removed.`);
  298. } else {
  299. alert(`${hostname} was not found in the blacklist or whitelist, and no subdomains were found.`);
  300. }
  301. }
  302. }
  303. });
  304.  
  305. log('Refined Popup Blocker initialized with user interaction and dynamic content monitoring.');
  306. })();