Sabotage Window/Tab Switch Visibility

Make website completely invisible to the user switching to another window or tab. May break some websites.

  1. // ==UserScript==
  2. // @name Sabotage Window/Tab Switch Visibility
  3. // @description Make website completely invisible to the user switching to another window or tab. May break some websites.
  4. // @author owowed <island@owowed.moe>
  5. // @version 0.0.3
  6. // @namespace util.owowed.moe
  7. // @license GPL-3.0-or-later
  8. // @match *://*/*
  9. // @grant unsafeWindow
  10. // @run-at document-start
  11. // @copyright All rights reserved. Licensed under GPL-3.0-or-later. View license at https://spdx.org/licenses/GPL-3.0-or-later.html
  12. // ==/UserScript==
  13.  
  14. !function () {
  15. /* Disable focus and blur event of document and window */
  16.  
  17. const windowProto = globalThis.unsafeWindow ?? window;
  18. console.log("Sabotage Window/Tab Switch Visibility is executing...");
  19. disableFocusBlurEvent(Document.prototype);
  20. disableFocusBlurEvent(windowProto);
  21. function disableFocusBlurEvent(objPrototype) {
  22. const eventBlocklist = ["focus", "blur"];
  23.  
  24. for (const event of eventBlocklist) {
  25. defineGetterSetter(objPrototype, `on${event}`, {
  26. set: () => undefined,
  27. });
  28. }
  29.  
  30. const oldEventListener = objPrototype.addEventListener;
  31. objPrototype.addEventListener = function (event, ...args) {
  32. if (eventBlocklist.includes(event)) return;
  33. oldEventListener.call(this, event, ...args);
  34. }
  35. }
  36.  
  37. /* Disable Page Visibility API */
  38.  
  39. defineGetterSetter(Document.prototype, "hidden", {
  40. get: () => false
  41. });
  42. defineGetterSetter(Document.prototype, "visibilityState", {
  43. get: () => "visible"
  44. });
  45. document.addEventListener("visibilitychange", function(e) {
  46. e.stopImmediatePropagation();
  47. }, true);
  48.  
  49. /* Partially disable blur and focus event (and other events) of element */
  50.  
  51. const safeBoxSize = 800;
  52. const elementOldEventListener = Element.prototype.addEventListener;
  53.  
  54. const eventBlocklist = [
  55. "blur",
  56. "focus",
  57. "mouseleave",
  58. "hasFocus"
  59. ];
  60.  
  61. // Element.on{event} properties, like Element.onfocus, Element.onblur etc.
  62. for (const event of eventBlocklist) {
  63. if (event == "hasFocus") continue;
  64. let registered = false;
  65. defineGetterSetter(Element.prototype, `on${event}`, {
  66. set(callback) {
  67. if (!registered) {
  68. this.addEventListener(event, callback)
  69. registered = true;
  70. }
  71. }
  72. });
  73. }
  74. // override Element.addEventListener
  75. Element.prototype.addEventListener = function (event, callback, ...args) {
  76. if (eventBlocklist.includes(event)) {
  77. const elem = this;
  78. elementOldEventListener.call(this, event, (...eventArgs) => {
  79. const clientRect = elem.getBoundingClientRect();
  80. if (clientRect.width >= safeBoxSize || clientRect.height >= safeBoxSize) {
  81. return;
  82. }
  83. callback(...eventArgs);
  84. }, ...args);
  85. }
  86. else elementOldEventListener.call(this, event, callback, ...args);
  87. };
  88. /* Disable CSS prefers-reduced-motion for JavaScript */
  89. const windowOldMatchMedia = windowProto.matchMedia;
  90. windowProto.matchMedia = function (matchMedia, ...args) {
  91. if (matchMedia.includes("prefers-reduced-motion") && matchMedia.includes("reduce")) {
  92. return false;
  93. }
  94. return windowOldMatchMedia.call(this, matchMedia, ...args);
  95. }
  96.  
  97. console.log("Sabotage Window/Tab Switch Visibility executed");
  98. }();
  99.  
  100. function defineGetterSetter(proto, property, { get, set } = {}) {
  101. Object.defineProperty(proto, property, {
  102. get, set,
  103. enumerable: true, configurable: true
  104. })
  105. }