Web CPU Tamer

ბრაუზერის ენერგიის გავლენა შემცირდეს არაპირდაპირი დაგეგმვის შეფერხებით

  1. // ==UserScript==
  2. // @name Web CPU Tamer
  3. // @name:ja Web CPU Tamer
  4. // @name:zh-TW Web CPU Tamer
  5. // @namespace http://tampermonkey.net/
  6. // @version 2025.101.6
  7. // @license MIT License
  8. // @author CY Fung
  9. // @match https://*/*
  10. // @match http://*/*
  11. // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
  12. // @icon https://raw.githubusercontent.com/cyfung1031/userscript-supports/7b34986ad9cdf3af8766e54b0aecb394b036e970/icons/web-cpu-tamer.svg
  13. // @supportURL https://github.com/cyfung1031/userscript-supports
  14.  
  15. // @run-at document-start
  16. // @inject-into auto
  17. // @grant none
  18. // @allFrames true
  19.  
  20. // @description Reduce Browser's Energy Impact via implicit async scheduling delay
  21. // @description:en Reduce Browser's Energy Impact via implicit async scheduling delay
  22. // @description:ja 非同期スケジューリングの遅延を利用してブラウザのエネルギー影響を軽減
  23. // @description:zh-TW 透過隱性非同步排程延遲減少瀏覽器的能源影響
  24. // @description:zh-CN 通过隐式的异步调度延迟减少浏览器的能耗
  25.  
  26. // @description:ko 암묵적 지연 스케줄링을 통해 브라우저의 에너지 영향을 줄입니다
  27. // @description:ru Снижает энергопотребление браузера с помощью неявной задержки планирования
  28. // @description:af Verminder die energie-impak van die blaaier via implisiete skeduleringvertraging
  29. // @description:az Brauzerin enerji təsirini gizli cədvəl gecikməsi ilə azaldır
  30. // @description:id Kurangi Dampak Energi Browser melalui penjadwalan asinkron implisit
  31. // @description:ms Kurangkan Impak Tenaga Pelayar melalui penjadualan tak segerak tersirat
  32. // @description:bs Smanjite energetski uticaj preglednika putem implicitnog kašnjenja zakazivanja
  33. // @description:ca Redueix l'impacte energètic del navegador mitjançant un retard implícit en la planificació
  34. // @description:cs Snížit energetický dopad prohlížeče pomocí implicitního zpoždění plánování
  35. // @description:da Reducer browserens energipåvirkning via implicit forsinkelse i planlægningen
  36. // @description:de Reduzieren Sie den Energieverbrauch des Browsers durch implizite asynchrone Zeitplanung
  37. // @description:et Vähendage brauseri energiamõju vaikimisi ajastamise viivituse kaudu
  38. // @description:es Reduzca el impacto energético del navegador mediante un retraso implícito en la programación
  39. // @description:eu Nabigatzailearen energia-inpaktua murriztu inplizituko programazio atzerapenaren bidez
  40. // @description:fr Réduire l'impact énergétique du navigateur grâce à un retard implicite dans la planification
  41. // @description:gl Reduza o impacto enerxético do navegador mediante unha demora implícita na programación
  42. // @description:hr Smanjite energetski utjecaj preglednika putem implicitnog kašnjenja zakazivanja
  43. // @description:zu Yehlisa Umthelela Webrowser we-Energy nge-Implicit Scheduling Delay
  44. // @description:is Minnkaðu orkunotkun vafrans með óbeinum töfum á tímasetningu
  45. // @description:it Riduci l'impatto energetico del browser tramite ritardo implicito nella pianificazione
  46. // @description:sw Punguza athari ya nishati ya kivinjari kupitia ucheleweshaji wa ratiba usio dhahiri
  47. // @description:lv Samaziniet pārlūkprogrammas enerģijas ietekmi, izmantojot netiešu plānošanas aizkavi
  48. // @description:lt Sumažinkite naršyklės energijos poveikį naudojant netiesioginį planavimo vėlavimą
  49. // @description:hu Csökkentse a böngésző energiaterhelését implicit ütemezési késleltetéssel
  50. // @description:nl Verminder het energieverbruik van de browser via impliciete planningsvertraging
  51. // @description:uz Brauzer energiyasi ta’sirini yashirin rejalashtirish kechikishi orqali kamaytiring
  52. // @description:pl Zmniejsz zużycie energii przeglądarki przez opóźnienie w planowaniu (implicit delay)
  53. // @description:pt Reduza o impacto energético do navegador com atraso implícito na programação
  54. // @description:pt-BR Reduza o impacto energético do navegador com atraso implícito na programação
  55. // @description:ro Reduceți impactul energetic al browserului prin întârziere implicită în planificare
  56. // @description:sq Zvogëloni ndikimin energjetik të shfletuesit me vonesë të planifikimit implicit
  57. // @description:sk Znížte energetický dopad prehliadača pomocou implicitného oneskorenia plánovania
  58. // @description:sl Zmanjšajte energijski vpliv brskalnika z implicitno zakasnitvijo načrtovanja
  59. // @description:sr Smanjite energetski uticaj pregledača korišćenjem implicitnog kašnjenja rasporeda
  60. // @description:fi Vähennä selaimen energiankulutusta käyttämällä implisiittistä aikataulun viivettä
  61. // @description:sv Minska webbläsarens energipåverkan med implicit schemaläggningsfördröjning
  62. // @description:vi Giảm tác động năng lượng của trình duyệt bằng cách trì hoãn lịch trình không rõ ràng
  63. // @description:tr Tarayıcının enerji etkisini örtük zamanlama gecikmesiyle azaltın
  64. // @description:be Змяншыце энергетычны ўплыў браўзера з дапамогай неяўнай затрымкі планавання
  65. // @description:bg Намалете енергийното въздействие на браузъра чрез неявно забавяне на планирането
  66. // @description:ky Браузердеги энергия таасирин жашыруун пландоо кечиктирүүсү менен азайтыңыз
  67. // @description:kk Браузердің энергия әсерін жасырын жоспарлау кешігуі арқылы азайтыңыз
  68. // @description:mk Намалете ја енергетската потрошувачка на прелистувачот преку неексплицитно задоцнување во планирањето
  69. // @description:mn Хөтчийн эрчим хүчний нөлөөллийг далд хуваарлалтын саатаар бууруулах
  70. // @description:uk Зменште енергоспоживання браузера за допомогою неявного затримання планування
  71. // @description:el Μειώστε τον ενεργειακό αντίκτυπο του προγράμματος περιήγησης μέσω καθυστέρησης προγραμματισμού
  72. // @description:hy Բրաուզերի էներգիայի ազդեցությունը նվազեցրեք անուղղակի պլանավորման ձգձգման միջոցով
  73. // @description:ur براؤزر کے توانائی اثر کو غیر واضح شیڈولنگ کی تاخیر سے کم کریں
  74. // @description:ar تقليل استهلاك الطاقة للمتصفح من خلال تأخير جدولة ضمني
  75. // @description:fa کاهش تأثیر انرژی مرورگر از طریق تأخیر ضمنی در زمان‌بندی
  76. // @description:ne ब्राउजरको ऊर्जा प्रभावलाई निहित शेड्युलिङ ढिलाइमार्फत कम गर्नुहोस्
  77. // @description:mr ब्राउझरच्या ऊर्जेच्या प्रभावावर सूचित शेड्यूलिंग विलंबाद्वारे कमी करा
  78. // @description:hi ब्राउज़र के ऊर्जा प्रभाव को निहित शेड्यूलिंग विलंब के माध्यम से कम करें
  79. // @description:as ব্রাউজাৰৰ শক্তি প্ৰভাৱ নেপথ্যভাৱে শিডিউলিং বিঢম্বন দ্বাৰা হ্ৰাস কৰক
  80. // @description:bn ব্রাউজারের শক্তি প্রভাব নীরবভাবে নির্ধারিত বিলম্বের মাধ্যমে হ্রাস করুন
  81. // @description:pa ਬਰਾਊਜ਼ਰ ਦੀ ਊਰਜਾ ਪ੍ਰਭਾਵ ਨੂੰ ਗੁਪਤ ਸਮੇਂਬੱਧਤਾ ਦੇ ਵਿਚੰਕਾਰ ਘਟਾਓ
  82. // @description:gu બ્રાઉઝરની ઊર્જા અસરને નમ્ર શેડ્યૂલિંગ વિલંબ દ્વારા ઘટાડો
  83. // @description:or ବ୍ରାଉଜରର ଶକ୍ତି ପ୍ରଭାବକୁ ଅସ୍ପଷ୍ଟ ଅନୁସୂଚୀତ ବିଳମ୍ବ ମାଧ୍ୟମରେ କମାନ୍ତୁ
  84. // @description:ta உலாவியின் சக்தி தாக்கத்தை மறைமுக அட்டவணை தாமதத்தின் மூலம் குறைக்கவும்
  85. // @description:te బ్రౌజర్ శక్తి ప్రభావాన్ని పరోక్ష షెడ్యూలింగ్ ఆలస్యంతో తగ్గించండి
  86. // @description:kn ಬ್ರೌಸರ್ ಶಕ್ತಿ ಪರಿಣಾಮವನ್ನು ಅಸ್ಪಷ್ಟವಾದ ಶೆಡ್ಯುಲಿಂಗ್ ವಿಳಂಬದ ಮೂಲಕ ಕಡಿಮೆಮಾಡಿ
  87. // @description:ml ബ്രൗസറിന്റെ ഊർജ്ജ പ്രഭാവം പരോക്ഷ ഷെഡ്യൂളിംഗ് വൈകലിലൂടെ കുറയ്ക്കുക
  88. // @description:si බ්‍රවුසරයේ බලපෑම අනුපූර්වව වැඩිවීමේ ප්‍රමාදය මඟින් අඩු කරන්න
  89. // @description:th ลดผลกระทบทางพลังงานของเบราว์เซอร์ด้วยการหน่วงเวลาการกำหนดตารางโดยปริยาย
  90. // @description:lo ລົດຜົນກະທົບດ້ານພະລັງງານຂອງບຣາວເຊີຜ່ານການລໍຖ້າການຈັດຕາຕະລາງແບບບໍ່ຊັດເຈນ
  91. // @description:my Browser ၏စွမ်းအင်သက်ရောက်မှုကို မသိသာသောအချိန်ဇယားနောက်ကျမှုဖြင့်လျော့ချပါ
  92. // @description:ka ბრაუზერის ენერგიის გავლენა შემცირდეს არაპირდაპირი დაგეგმვის შეფერხებით
  93. // @description:am አሳይነት የሆነ የመርሃግብር መዘግየትን በመጠቀም የአሳሽ ኃይል ተፅዕኖን አሳንሱ
  94. // @description:km បន្ថយផលប៉ះពាល់ថាមពលរបស់កម្មវិធីរុករកតាមការពន្យារបែបលាក់ៗនៃការកំណត់ពេលវេលា
  95. // ==/UserScript==
  96.  
  97. /*
  98.  
  99. MIT License
  100.  
  101. Copyright 2025 CY Fung
  102.  
  103. Permission is hereby granted, free of charge, to any person obtaining a copy
  104. of this software and associated documentation files (the "Software"), to deal
  105. in the Software without restriction, including without limitation the rights
  106. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  107. copies of the Software, and to permit persons to whom the Software is
  108. furnished to do so, subject to the following conditions:
  109.  
  110. The above copyright notice and this permission notice shall be included in all
  111. copies or substantial portions of the Software.
  112.  
  113. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  114. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  115. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  116. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  117. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  118. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  119. SOFTWARE.
  120.  
  121. */
  122.  
  123. /* jshint esversion:8 */
  124.  
  125. ((o) => {
  126. 'use strict';
  127.  
  128. const HACK_TOSTRING = false;
  129. const HACK_VALUEOF = false;
  130.  
  131. const [setTimeout_, setInterval_, requestAnimationFrame_, clearTimeout_, clearInterval_, cancelAnimationFrame_] = o;
  132. const queueMicrotask_ = queueMicrotask;
  133. const win = typeof window.wrappedJSObject === 'object' ? window.wrappedJSObject : typeof unsafeWindow === 'object' ? unsafeWindow : this instanceof Window ? this : window;
  134.  
  135. // Create a unique key for the script and check if it is already running
  136. const hkey_script = 'nzsxclvflluv';
  137. if (win[hkey_script]) throw new Error('Duplicated Userscript Calling'); // avoid duplicated scripting
  138. win[hkey_script] = true;
  139.  
  140. /** @type {globalThis.PromiseConstructor} */
  141. const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
  142.  
  143. let resolvePr = () => { }, pr;
  144. const setPr = () => (pr = new Promise((resolve) => {
  145. resolvePr = resolve;
  146. }));
  147.  
  148. setPr();
  149.  
  150. const cme = document.createComment('--WebCPUTamer--');
  151. // const appendChild_ = HTMLElement.prototype.appendChild;
  152. let cmi = 0;
  153. let lastPr = null;
  154. function act() {
  155. if (lastPr !== pr) {
  156. // const b = lastPr === null;
  157. lastPr = pr;
  158. // if (b) {
  159. // appendChild_.call(document.documentElement, cme);
  160. // ro.observe(document, { childList: true });
  161. // ro.observe(document.documentElement, { childList: true });
  162. // }
  163. cmi = (cmi & 7) + 1;
  164. if (cmi & 1) {
  165. cme.data = '++WebCPUTamer++'
  166. } else {
  167. cme.data = '--WebCPUTamer--'
  168. }
  169. }
  170. }
  171.  
  172. class PseudoTimeline {
  173. constructor() {
  174. this.startTime = performance.timeOrigin || performance.now();
  175. }
  176.  
  177. get currentTime() {
  178. return performance.now() - this.startTime;
  179. }
  180. }
  181.  
  182. let tl;
  183. if (typeof DocumentTimeline === 'function') {
  184. tl = new DocumentTimeline();
  185. } else if (typeof Animation === 'function') {
  186. let AnimationConstructor = Animation, e = document.documentElement;
  187. if (e) {
  188. e = e.animate(null);
  189. if (typeof (e || 0) === 'object' && '_animation' in e && e.constructor === Object) {
  190. e = e._animation; // for YouTube
  191. }
  192. if (typeof (e || 0) === 'object' && 'timeline' in e && typeof e.constructor === 'function') {
  193. AnimationConstructor = e.constructor;
  194. }
  195. }
  196. const ant = new AnimationConstructor();
  197. tl = ant.timeline;
  198. }
  199. if (!tl || !Number.isFinite(tl.currentTime || null)) tl = new PseudoTimeline();
  200. const tl_ = tl;
  201.  
  202. const mo = new MutationObserver(() => {
  203. resolvePr();
  204. setPr();
  205. });
  206. mo.observe(cme, {
  207. characterData: true,
  208. });
  209.  
  210. // const ro = new MutationObserver(() => {
  211. // if ((cme.isConnected !== true || cme.parentNode !== document.documentElement) && lastPr !== null) {
  212. // lastPr = null;
  213. // act();
  214. // }
  215. // });
  216.  
  217. const tz = new Set();
  218. const az = new Set();
  219.  
  220. const h1 = async (r) => {
  221. tz.add(r);
  222. if (lastPr !== pr) queueMicrotask_(act);
  223. await pr;
  224. if (lastPr !== pr) queueMicrotask_(act);
  225. await pr;
  226. return tz.delete(r);
  227. };
  228.  
  229. const h2 = async (r, upr) => {
  230. az.add(r);
  231. await upr;
  232. return az.delete(r);
  233. };
  234.  
  235. const errCatch = e => {
  236. queueMicrotask_(() => { throw e });
  237. };
  238.  
  239. const dOffset = 2 ** -26; // avoid Brave/uBlock adjustSetTimeout
  240.  
  241. setTimeout = function (f, d = void 0, ...args) {
  242. let r;
  243. const g = (typeof f === 'function') ? (...args) => {
  244. h1(r).then((act) => {
  245. act && f(...args);
  246. }).catch(errCatch);
  247. } : f;
  248. if (d >= 1) d -= dOffset;
  249. r = setTimeout_(g, d, ...args);
  250. return r;
  251. };
  252.  
  253. setInterval = function (f, d = void 0, ...args) {
  254. let r;
  255. const g = (typeof f === 'function') ? (...args) => {
  256. h1(r).then((act) => {
  257. act && f(...args);
  258. }).catch(errCatch);
  259. } : f;
  260. if (d >= 1) d -= dOffset;
  261. r = setInterval_(g, d, ...args);
  262. return r;
  263. };
  264.  
  265. clearTimeout = function (cid) {
  266. tz.delete(cid);
  267. return clearTimeout_(cid);
  268. };
  269.  
  270. clearInterval = function (cid) {
  271. tz.delete(cid);
  272. return clearInterval_(cid);
  273. };
  274.  
  275. requestAnimationFrame = function (f) {
  276. let r;
  277. const upr = pr;
  278. const g = (timeRes) => {
  279. const q1 = tl_.currentTime;
  280. h2(r, upr).then((act) => {
  281. act && f(timeRes + (tl_.currentTime - q1));
  282. }).catch(errCatch);
  283. }
  284. if (lastPr !== pr) queueMicrotask_(act);
  285. r = requestAnimationFrame_(g);
  286. return r;
  287. };
  288.  
  289. cancelAnimationFrame = function (aid) {
  290. az.delete(aid);
  291. return cancelAnimationFrame_(aid);
  292. };
  293.  
  294. if (HACK_TOSTRING) {
  295. setTimeout.toString = setTimeout_.toString.bind(setTimeout_);
  296. setInterval.toString = setInterval_.toString.bind(setInterval_);
  297. clearTimeout.toString = clearTimeout_.toString.bind(clearTimeout_);
  298. clearInterval.toString = clearInterval_.toString.bind(clearInterval_);
  299. requestAnimationFrame.toString = requestAnimationFrame_.toString.bind(requestAnimationFrame_);
  300. cancelAnimationFrame.toString = cancelAnimationFrame_.toString.bind(cancelAnimationFrame_);
  301. }
  302. if (HACK_VALUEOF) {
  303. setTimeout.valueOf = setTimeout_.valueOf.bind(setTimeout_);
  304. setInterval.valueOf = setInterval_.valueOf.bind(setInterval_);
  305. clearTimeout.valueOf = clearTimeout_.valueOf.bind(clearTimeout_);
  306. clearInterval.valueOf = clearInterval_.valueOf.bind(clearInterval_);
  307. requestAnimationFrame.valueOf = requestAnimationFrame_.valueOf.bind(requestAnimationFrame_);
  308. cancelAnimationFrame.valueOf = cancelAnimationFrame_.valueOf.bind(cancelAnimationFrame_);
  309. }
  310.  
  311. if (typeof webkitRequestAnimationFrame === 'function' && typeof navigator === 'object' && typeof navigator.userAgentData === 'object') {
  312. const isYouTubePage = location?.hostname?.endsWith('youtube.com');
  313. if (isYouTubePage) {
  314. try {
  315. if (isYouTubePage && navigator?.userAgentData?.brands?.some(e => e?.brand === 'Brave')) { // fu*k you Brave!
  316. let e_;
  317. try {
  318. setTimeout_.call(1);
  319. } catch (e) { e_ = e }
  320. if (!`${e_?.stack || "Object.apply"}`.includes("Object.apply")) {
  321. let q;
  322. q = Object.getOwnPropertyDescriptor(self, 'setTimeout');
  323. Object.defineProperty(self, 'setTimeout', { ...q, writable: false });
  324. q = Object.getOwnPropertyDescriptor(self, 'setInterval');
  325. Object.defineProperty(self, 'setInterval', { ...q, writable: false });
  326. }
  327. }
  328. } catch (e) { }
  329. }
  330. }
  331.  
  332. // Firemonkey & Violentmonkey in Firefox under non-page context
  333. const isContentScript = (typeof window.wrappedJSObject === 'object' && typeof unsafeWindow === 'object' && typeof exportFunction === 'function') || (typeof GM === 'object' && ((GM || 0).info || 0).injectInto === 'content');
  334. if (isContentScript) {
  335. const exportFn = (f, name) => {
  336. typeof exportFunction === 'function' ? exportFunction(f, win, { defineAs: name, allowCrossOriginArguments: true }) : (win[name] = f);
  337. }
  338. exportFn(setTimeout, 'setTimeout');
  339. exportFn(setInterval, 'setInterval');
  340. exportFn(requestAnimationFrame, 'requestAnimationFrame');
  341. exportFn(clearTimeout, 'clearTimeout');
  342. exportFn(clearInterval, 'clearInterval');
  343. exportFn(cancelAnimationFrame, 'cancelAnimationFrame');
  344. exportFn(()=>1, `webCPUTamer_${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`);
  345. }
  346.  
  347. })([setTimeout, setInterval, requestAnimationFrame, clearTimeout, clearInterval, cancelAnimationFrame]);
  348.