YouTube RM3 - Reduce Memory Usage by Reusing Components

一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。

目前为 2024-12-22 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube RM3 - Reduce Memory Usage by Reusing Components
  3. // @namespace Violentmonkey Scripts
  4. // @version 0.1.0015
  5. // @license MIT
  6. // @match https://www.youtube.com/*
  7. // @match https://studio.youtube.com/live_chat*
  8. //
  9. //
  10. // @author CY Fung
  11. // @run-at document-start
  12. // @grant none
  13. // @unwrap
  14. // @allFrames true
  15. // @inject-into page
  16. //
  17. // @compatible firefox Violentmonkey
  18. // @compatible firefox Tampermonkey
  19. // @compatible firefox FireMonkey
  20. // @compatible chrome Violentmonkey
  21. // @compatible chrome Tampermonkey
  22. // @compatible opera Violentmonkey
  23. // @compatible opera Tampermonkey
  24. // @compatible safari Stay
  25. // @compatible edge Violentmonkey
  26. // @compatible edge Tampermonkey
  27. // @compatible brave Violentmonkey
  28. // @compatible brave Tampermonkey
  29. //
  30. // @description A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
  31. // @description:en A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
  32. // @description:ja YouTubeコンポーネントを再利用することで、長期的なメモリ使用量の削減を目指す、バックグラウンドで実行されるシンプルなツールです。
  33. // @description:zh-TW 一個在背景執行的簡易工具,可重複利用 YouTube 元件,從而在長期減少記憶體使用量。
  34. // @description:zh-CN 一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。
  35. //
  36. // ==/UserScript==
  37.  
  38. const rm3 = window.rm3 = {};
  39. // console.log(3001);
  40.  
  41. (() => {
  42.  
  43. const DEBUG_OPT = false;
  44. const CONFIRM_TIME = 4000;
  45. const CHECK_INTERVAL = 400;
  46.  
  47.  
  48. /** @type {globalThis.PromiseConstructor} */
  49. const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.
  50.  
  51. // https://qiita.com/piroor/items/02885998c9f76f45bfa0
  52. // https://gist.github.com/piroor/829ecb32a52c2a42e5393bbeebe5e63f
  53. function uniq(array) {
  54. return [...new Set(array)];
  55. };
  56.  
  57.  
  58. rm3.uniq = uniq; // [[debug]]
  59.  
  60.  
  61. rm3.inspect = () => {
  62. return uniq([...document.getElementsByTagName('*')].filter(e => e?.polymerController?.createComponent_).map(e => e.nodeName)); // [[debug]]
  63. }
  64.  
  65.  
  66. const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
  67. const indr = o => insp(o).$ || o.$ || 0;
  68.  
  69. const getProto = (element) => {
  70. if (element) {
  71. const cnt = insp(element);
  72. return cnt.constructor.prototype || null;
  73. }
  74. return null;
  75. }
  76.  
  77.  
  78. const LinkedArray = (() => {
  79.  
  80.  
  81. class Node {
  82. constructor(value) {
  83. this.value = value;
  84. this.next = null;
  85. this.prev = null;
  86. }
  87. }
  88.  
  89. class LinkedArray {
  90. constructor() {
  91. this.head = null;
  92. this.tail = null;
  93. this.length = 0;
  94. }
  95.  
  96. push(value) {
  97. const newNode = new Node(value);
  98. if (this.length === 0) {
  99. this.head = newNode;
  100. this.tail = newNode;
  101. } else {
  102. this.tail.next = newNode;
  103. newNode.prev = this.tail;
  104. this.tail = newNode;
  105. }
  106. this.length++;
  107. return this.length;
  108. }
  109.  
  110. pop() {
  111. if (this.length === 0) return undefined;
  112. const removedNode = this.tail;
  113. if (this.length === 1) {
  114. this.head = null;
  115. this.tail = null;
  116. } else {
  117. this.tail = removedNode.prev;
  118. this.tail.next = null;
  119. removedNode.prev = null;
  120. }
  121. this.length--;
  122. return removedNode.value;
  123. }
  124.  
  125. unshift(value) {
  126. const newNode = new Node(value);
  127. if (this.length === 0) {
  128. this.head = newNode;
  129. this.tail = newNode;
  130. } else {
  131. newNode.next = this.head;
  132. this.head.prev = newNode;
  133. this.head = newNode;
  134. }
  135. this.length++;
  136. return this.length;
  137. }
  138.  
  139. shift() {
  140. if (this.length === 0) return undefined;
  141. const removedNode = this.head;
  142. if (this.length === 1) {
  143. this.head = null;
  144. this.tail = null;
  145. } else {
  146. this.head = removedNode.next;
  147. this.head.prev = null;
  148. removedNode.next = null;
  149. }
  150. this.length--;
  151. return removedNode.value;
  152. }
  153.  
  154. size() {
  155. return this.length;
  156. }
  157.  
  158. // Get a node by index (0-based)
  159. getNode(index) {
  160. if (index < 0 || index >= this.length) return null;
  161.  
  162. let current;
  163. let counter;
  164.  
  165. // Optimization: start from closest end
  166. if (index < this.length / 2) {
  167. current = this.head;
  168. counter = 0;
  169. while (counter !== index) {
  170. current = current.next;
  171. counter++;
  172. }
  173. } else {
  174. current = this.tail;
  175. counter = this.length - 1;
  176. while (counter !== index) {
  177. current = current.prev;
  178. counter--;
  179. }
  180. }
  181. return current;
  182. }
  183.  
  184. // Get value by index
  185. get(index) {
  186. const node = this.getNode(index);
  187. return node ? node.value : undefined;
  188. }
  189.  
  190. // Find the first node with the given value and return both node and index
  191. findNode(value) {
  192. let current = this.head;
  193. let idx = 0;
  194. while (current) {
  195. if (current.value === value) {
  196. return { node: current, index: idx };
  197. }
  198. current = current.next;
  199. idx++;
  200. }
  201. return { node: null, index: -1 };
  202. }
  203.  
  204. toArray() {
  205. const arr = [];
  206. let current = this.head;
  207. while (current) {
  208. arr.push(current.value);
  209. current = current.next;
  210. }
  211. return arr;
  212. }
  213.  
  214. // Insert a new value before a given node (provided you already have the node reference)
  215. insertBeforeNode(node, newValue) {
  216. if (!node) {
  217. this.unshift(newValue);
  218. return true;
  219. }
  220.  
  221. if (node === this.head) {
  222. // If the target is the head, just unshift
  223. this.unshift(newValue);
  224. } else {
  225. const newNode = new Node(newValue);
  226. const prevNode = node.prev;
  227.  
  228. prevNode.next = newNode;
  229. newNode.prev = prevNode;
  230. newNode.next = node;
  231. node.prev = newNode;
  232.  
  233. this.length++;
  234. }
  235. return true;
  236. }
  237.  
  238. // Insert a new value after a given node (provided you already have the node reference)
  239. insertAfterNode(node, newValue) {
  240.  
  241. if (!node) {
  242. this.push(newValue);
  243. return true;
  244. }
  245.  
  246. if (node === this.tail) {
  247. // If the target is the tail, just push
  248. this.push(newValue);
  249. } else {
  250. const newNode = new Node(newValue);
  251. const nextNode = node.next;
  252.  
  253. node.next = newNode;
  254. newNode.prev = node;
  255. newNode.next = nextNode;
  256. nextNode.prev = newNode;
  257.  
  258. this.length++;
  259. }
  260. return true;
  261. }
  262.  
  263. // Insert a new value before the first occurrence of an existing value (search by value)
  264. insertBefore(existingValue, newValue) {
  265. const { node } = this.findNode(existingValue);
  266. if (!node) return false; // Not found
  267. return this.insertBeforeNode(node, newValue);
  268. }
  269.  
  270. // Insert a new value after the first occurrence of an existing value (search by value)
  271. insertAfter(existingValue, newValue) {
  272. const { node } = this.findNode(existingValue);
  273. if (!node) return false; // Not found
  274. return this.insertAfterNode(node, newValue);
  275. }
  276.  
  277.  
  278.  
  279. // Delete a given node from the list
  280. deleteNode(node) {
  281. if (!node) return false;
  282.  
  283. if (this.length === 1 && node === this.head && node === this.tail) {
  284. // Only one element in the list
  285. this.head = null;
  286. this.tail = null;
  287. } else if (node === this.head) {
  288. // Node is the head
  289. this.head = node.next;
  290. this.head.prev = null;
  291. node.next = null;
  292. } else if (node === this.tail) {
  293. // Node is the tail
  294. this.tail = node.prev;
  295. this.tail.next = null;
  296. node.prev = null;
  297. } else {
  298. // Node is in the middle
  299. const prevNode = node.prev;
  300. const nextNode = node.next;
  301. prevNode.next = nextNode;
  302. nextNode.prev = prevNode;
  303. node.prev = null;
  304. node.next = null;
  305. }
  306.  
  307. this.length--;
  308. return true;
  309. }
  310.  
  311. }
  312.  
  313.  
  314. LinkedArray.Node = Node;
  315. return LinkedArray;
  316. })();
  317.  
  318.  
  319.  
  320. class LimitedSizeSet extends Set {
  321. constructor(n) {
  322. super();
  323. this.limit = n;
  324. }
  325.  
  326. add(key) {
  327. if (!super.has(key)) {
  328. super.add(key);
  329. let n = super.size - this.limit;
  330. if (n > 0) {
  331. const iterator = super.values();
  332. do {
  333. const firstKey = iterator.next().value; // Get the first (oldest) key
  334. super.delete(firstKey); // Delete the oldest key
  335. } while (--n > 0)
  336. }
  337. }
  338. }
  339.  
  340. removeAdd(key) {
  341. super.delete(key);
  342. this.add(key);
  343. }
  344.  
  345. }
  346.  
  347.  
  348.  
  349. if (!document.createElement9512 && typeof document.createElement === 'function' && document.createElement.length === 1) {
  350.  
  351. // sizing of Map / Set. Shall limit ?
  352.  
  353. const hookTos = new Set(); // [[debug]]
  354. DEBUG_OPT && (rm3.hookTos = hookTos);
  355.  
  356. // const reusePool = new Map(); // xx858
  357. const entryRecords = new WeakMap(); // a weak link between element and record
  358.  
  359. // rm3.list = [];
  360.  
  361. const operations = rm3.operations = new Set(); // to find out the "oldest elements"
  362.  
  363. const availablePools = rm3.availablePools = new Map(); // those "old elements" can be used
  364. let lastTimeCheck = 0;
  365.  
  366. const reuseRecord_ = new LimitedSizeSet(256); // [[debug]]
  367. const reuseCount_ = new Map();
  368.  
  369. let noTimeCheck = false;
  370.  
  371. // const defaultValues = new Map();
  372. // const noValues = new Map();
  373.  
  374. const timeCheck = () => {
  375. // regularly check elements are old enough to put into the available pools
  376. // note: the characterists of YouTube components are non-volatile. So don't need to waste time to check weakRef.deref() is null or not for removing in operations.
  377.  
  378. const ct = Date.now();
  379. if (ct - lastTimeCheck < CHECK_INTERVAL || noTimeCheck) return;
  380. lastTimeCheck = ct;
  381. noTimeCheck = true;
  382.  
  383. // 16,777,216
  384. if (hookTos.size > 777216) hookTos.clear(); // just debug usage, dont concern
  385. if (operations.size > 7777216) {
  386. // extremely old elements in operations mean they have no attach/detach action. so no reuse as well. they are just trash in memory.
  387. // as no checking of the weakRef.deref() being null or not, those trash could be already cleaned. However we don't concern this.
  388. // (not to count whether they are actual memory trash or not)
  389. const half = operations.size >>> 1;
  390. let i = 0;
  391. for (const value of operations) {
  392. if (i++ > half) break;
  393. operations.delete(value);
  394. }
  395. }
  396.  
  397. // {
  398. // // smallest to largest
  399. // // past to recent
  400.  
  401. // const iterator = operations[Symbol.iterator]();
  402. // console.log(1831, '------------------------')
  403. // while (true) {
  404. // const iteratorResult = iterator.next(); // 順番に値を取りだす
  405. // if (iteratorResult.done) break; // 取り出し終えたなら、break
  406. // console.log(1835, iteratorResult.value[3])
  407. // }
  408.  
  409. // console.log(1839, '------------------------')
  410. // }
  411.  
  412. // Set iterator
  413. // s.add(2) s.add(6) s.add(1) s.add(3)
  414. // next: 2 -> 6 -> 1 -> 3
  415. // op1 (oldest) -> op2 -> op3 -> op4 (latest)
  416. const iterator = operations[Symbol.iterator]();
  417.  
  418. const targetTime = ct - CONFIRM_TIME;
  419.  
  420. const pivotNodes = new WeakMap();
  421.  
  422. while (true) {
  423. const iteratorResult = iterator.next(); // 順番に値を取りだす
  424. if (iteratorResult.done) break; // 取り出し終えたなら、break
  425. const entryRecord = iteratorResult.value;
  426. if (entryRecord[3] > targetTime) break;
  427.  
  428. if (!entryRecord[4] && entryRecord[1] < 0 && entryRecord[2] > 0) {
  429. const element = entryRecord[0].deref();
  430. const eKey = (element || 0).__rm3Tag003__;
  431. if (!eKey) {
  432. operations.delete(entryRecord);
  433. } else if (element.isConnected === false && insp(element).isAttached === false) {
  434. entryRecord[4] = true;
  435.  
  436. let availablePool = availablePools.get(eKey);
  437. if (!availablePool) availablePools.set(eKey, availablePool = new LinkedArray());
  438. if (!(availablePool instanceof LinkedArray)) throw new Error();
  439. let pivotNode = pivotNodes.get(availablePool);
  440. if (!pivotNode) pivotNodes.set(availablePool, pivotNode = availablePool.head) // cached the previous newest node (head) as pivotNode
  441.  
  442. availablePool.insertBeforeNode(pivotNode, entryRecord); // head = newest, tail = oldest
  443.  
  444. }
  445. }
  446.  
  447. }
  448. noTimeCheck = false;
  449.  
  450. }
  451.  
  452. const attachedDefine = function () {
  453.  
  454. Promise.resolve().then(timeCheck);
  455. try {
  456. const hostElement = this?.hostElement;
  457. if (hostElement instanceof HTMLElement) {
  458. const entryRecord = entryRecords.get(hostElement);
  459. if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === true && this?.isAttached === true) {
  460. noTimeCheck = true;
  461. const ct = Date.now();
  462. entryRecord[1] = ct;
  463. entryRecord[2] = -1;
  464. entryRecord[3] = ct;
  465. operations.delete(entryRecord);
  466. operations.add(entryRecord);
  467. noTimeCheck = false;
  468. // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
  469. // entryRecord[4] is not required to be updated here.
  470. }
  471. }
  472. } catch (e) { }
  473. return this.attached9512();
  474. }
  475. const detachedDefine = function () {
  476.  
  477. Promise.resolve().then(timeCheck);
  478. try {
  479. const hostElement = this?.hostElement;
  480. if (hostElement instanceof HTMLElement) {
  481. const entryRecord = entryRecords.get(hostElement);
  482. if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === false && this?.isAttached === false) {
  483. noTimeCheck= true;
  484. const ct = Date.now();
  485. entryRecord[2] = ct;
  486. entryRecord[1] = -1;
  487. entryRecord[3] = ct;
  488. operations.delete(entryRecord);
  489. operations.add(entryRecord);
  490. noTimeCheck= false;
  491. // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
  492. // entryRecord[4] is not required to be updated here.
  493. }
  494. }
  495. } catch (e) { }
  496.  
  497. return this.detached9512();
  498. }
  499.  
  500.  
  501. // function cpy(x) {
  502. // if (!x) return x;
  503. // try {
  504. // if (typeof x === 'object' && typeof x.length ==='number' && typeof x.slice === 'function') {
  505. // x = x.slice(0)
  506. // } else if (typeof x === 'object' && !x.length) {
  507. // x = JSON.parse(JSON.stringify(x));
  508. // } else {
  509. // return Object.assign({}, x);
  510. // }
  511. // } catch (e) { }
  512. // return x;
  513. // }
  514.  
  515.  
  516. const createComponentDefine_ = function (a, b, c) {
  517.  
  518. Promise.resolve().then(timeCheck);
  519.  
  520.  
  521. const creatorTag = this?.is || this?.nodeName?.toLowerCase() || '';
  522.  
  523. const componentTag = typeof a === 'string' ? a : ((a || 0).component || '');
  524.  
  525.  
  526.  
  527. const eKey = creatorTag && componentTag ? `${creatorTag}.${componentTag}` : '*'; // '*' for play-safe
  528. const availablePool = availablePools.get(eKey);
  529.  
  530. try {
  531.  
  532. if (availablePool instanceof LinkedArray) {
  533. noTimeCheck = true;
  534.  
  535. let node = availablePool.tail; // oldest
  536.  
  537. while (node instanceof LinkedArray.Node) {
  538. const entryRecord = node.value;
  539. const prevNode = node.prev;
  540.  
  541. let ok = false;
  542. let elm = null;
  543. if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
  544. elm = entryRecord[0].deref();
  545. if (elm && elm instanceof HTMLElement && elm.isConnected === false && insp(elm).isAttached === false && elm.parentNode === null) {
  546. ok = true;
  547. }
  548. }
  549.  
  550. if (ok) {
  551.  
  552. // useEntryRecord = entryRecord;
  553. entryRecord[4] = false;
  554. availablePool.deleteNode(node);
  555. // break;
  556.  
  557.  
  558. const cnt = insp(elm);
  559.  
  560. // cnt.__dataReady = false;
  561. cnt.__dataInvalid = true;
  562. cnt.__dataEnabled = false; // tbc
  563. // if('__dataEnabled' in cnt) cnt.__dataEnabled = false;
  564. // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
  565. // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;
  566.  
  567. // try {
  568. // if ('data' in cnt) cnt.data = null;
  569. // if ('__data' in cnt) cnt.__data = null;
  570. // } catch (e) {
  571. // console.warn(e)
  572. // }
  573.  
  574. // try {
  575. // if ('data' in cnt) cnt.data = {};
  576. // if ('__data' in cnt) cnt.__data = {};
  577. // } catch (e) {
  578. // console.warn(e)
  579. // }
  580.  
  581.  
  582.  
  583.  
  584.  
  585.  
  586.  
  587.  
  588.  
  589.  
  590.  
  591. // const noValue = noValues.get(eKey);
  592. // if(noValue){
  593. // if(!noValue.data) cnt.data = noValue.data;
  594. // if(!noValue.__data) cnt.data = noValue.__data;
  595. // }
  596.  
  597. // const defaultValue = defaultValues.get(eKey);
  598. // if (defaultValue) {
  599.  
  600. // try {
  601. // if ('data' in defaultValue) cnt.data = cpy(cnt.data);
  602. // if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
  603. // } catch (e) {
  604. // console.warn(e)
  605. // }
  606. // }
  607.  
  608. // const flg001 = elm.__rm3Flg001__;
  609. // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  610.  
  611.  
  612.  
  613.  
  614.  
  615.  
  616.  
  617. // const flg001 = elm.__rm3Flg001__;
  618. // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;
  619.  
  620.  
  621. if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
  622. if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;
  623.  
  624. // cnt.__dataInstanceProps = null;
  625. if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
  626. // cnt.__serializing = !1;
  627.  
  628.  
  629.  
  630. if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {
  631.  
  632. if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {
  633.  
  634. console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');
  635.  
  636. }
  637.  
  638. cnt.__dataClientsReady = !1;
  639. cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
  640. cnt.__dataHasPaths = !1;
  641. cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
  642. cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
  643. if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
  644. cnt.__dataClientsInitialized = !1;
  645.  
  646. }
  647.  
  648. if (entryRecord[5] < 1e9) entryRecord[5] += 1;
  649. DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
  650. DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
  651. DEBUG_OPT && reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1)
  652. if (rm3.reuseCount < 1e9) rm3.reuseCount++;
  653.  
  654. return elm;
  655.  
  656.  
  657. }
  658.  
  659. entryRecord[4] = false;
  660. availablePool.deleteNode(node);
  661. node = prevNode;
  662.  
  663. }
  664. // for(const ) availablePool
  665. // noTimeCheck = false;
  666. }
  667.  
  668. } catch (e) {
  669. console.warn(e)
  670. }
  671. noTimeCheck = false;
  672.  
  673.  
  674. // console.log('createComponentDefine_', a, b, c)
  675.  
  676. // if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858
  677.  
  678. // const pool = reusePool.get(componentTag); // xx858
  679. // if (!(pool instanceof LinkedArray)) throw new Error(); // xx858
  680.  
  681.  
  682. const newElement = this.createComponent9512_(a, b, c);
  683. // if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)
  684.  
  685. try {
  686.  
  687. const cntE = insp(newElement);
  688. if (!cntE.attached9512 && cntE.attached) {
  689.  
  690. const cProtoE = getProto(newElement);
  691.  
  692.  
  693. if (cProtoE.attached === cntE.attached) {
  694.  
  695. if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {
  696.  
  697. cProtoE.attached9512 = cProtoE.attached;
  698.  
  699. cProtoE.attached = attachedDefine;
  700. // hookTos.add(a);
  701. }
  702. } else {
  703.  
  704. if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
  705. cntE.attached9512 = cntE.attached;
  706.  
  707. cntE.attached = attachedDefine;
  708. // hookTos.add(a);
  709. }
  710. }
  711.  
  712.  
  713. }
  714.  
  715. if (!cntE.detached9512 && cntE.detached) {
  716.  
  717. const cProtoE = getProto(newElement);
  718.  
  719.  
  720. if (cProtoE.detached === cntE.detached) {
  721.  
  722. if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {
  723.  
  724. cProtoE.detached9512 = cProtoE.detached;
  725.  
  726. cProtoE.detached = detachedDefine;
  727. // hookTos.add(a);
  728. }
  729. } else {
  730.  
  731. if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
  732. cntE.detached9512 = cntE.detached;
  733.  
  734. cntE.detached = detachedDefine;
  735. // hookTos.add(a);
  736. }
  737. }
  738.  
  739.  
  740. }
  741.  
  742.  
  743. const acceptance = true;
  744. // const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
  745. if (acceptance) {
  746.  
  747. // [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
  748. const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];
  749.  
  750. newElement.__rm3Tag003__ = eKey;
  751. // pool.push(entryRecord);
  752. entryRecords.set(newElement, entryRecord);
  753. // newElement.__rm3Tag001__ = creatorTag;
  754. // newElement.__rm3Tag002__ = componentTag;
  755.  
  756. // newElement.__rm3Flg001__ = cntE.__dataEnabled;
  757. // // console.log(5928, cntE.data, cntE.__data)
  758. // if (!defaultValues.has(eKey)){
  759.  
  760. // const o = {};
  761.  
  762. // if('data' in cntE) o.data = cpy(cntE.data);
  763. // if('__data' in cntE) o.__data = cpy(cntE.__data);
  764. // defaultValues.set(eKey, o);
  765.  
  766. // }
  767.  
  768. // if(!noValues.has(eKey)){
  769. // const o = {};
  770. // o.data = true;
  771. // try {
  772.  
  773. // if (!cntE.data) o.data = cntE.data;
  774. // } catch (e) { }
  775.  
  776. // o.__data = true;
  777. // try {
  778.  
  779. // if (!cntE.__data) o.__data = cntE.__data;
  780. // } catch (e) { }
  781. // noValues.set(eKey, o)
  782. // }
  783.  
  784. } else {
  785. // console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
  786. }
  787.  
  788.  
  789. } catch (e) {
  790. console.warn(e);
  791. }
  792. return newElement;
  793.  
  794.  
  795. }
  796.  
  797. document.createElement9512 = document.createElement;
  798. document.createElement = function (a) {
  799. const r = document.createElement9512(a);
  800. try {
  801. const cnt = insp(r);
  802. if (cnt.createComponent_ && !cnt.createComponent9512_) {
  803. const cProto = getProto(r);
  804. if (cProto.createComponent_ === cnt.createComponent_) {
  805.  
  806. if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {
  807.  
  808. cProto.createComponent9512_ = cProto.createComponent_;
  809.  
  810. cProto.createComponent_ = createComponentDefine_;
  811. DEBUG_OPT && hookTos.add(a);
  812. }
  813. } else {
  814.  
  815. if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
  816. cnt.createComponent9512_ = cnt.createComponent_;
  817.  
  818. cnt.createComponent_ = createComponentDefine_;
  819. DEBUG_OPT && hookTos.add(a);
  820. }
  821. }
  822.  
  823. }
  824.  
  825. } catch (e) {
  826. console.warn(e)
  827. }
  828.  
  829.  
  830. return r;
  831. }
  832.  
  833. rm3.checkWhetherUnderParent = () => {
  834. const r = [];
  835. for (const operation of operations) {
  836. const elm = operation[0].deref();
  837. if (operation[2] > 0) {
  838.  
  839. r.push([!!elm, elm?.nodeName.toLowerCase(), elm?.parentNode === null]);
  840. }
  841. }
  842. return r;
  843. }
  844.  
  845. rm3.hookTags = () => {
  846.  
  847. const r = new Set();
  848. for (const operation of operations) {
  849. const elm = operation[0].deref();
  850. if (elm && elm.is) {
  851.  
  852. r.add(elm.is)
  853. }
  854. }
  855. return [...r];
  856. }
  857.  
  858.  
  859. DEBUG_OPT && (rm3.reuseRecord = () => {
  860. return [...reuseRecord_]; // [[debug]]
  861. });
  862.  
  863. DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);
  864.  
  865. }
  866.  
  867. (rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.
  868.  
  869. })();