Greasy Fork is available in English.

YouTube RM3 - Reduce Memory Usage by Reusing Components

YouTubeコンポーネントを再利用することで、長期的なメモリ使用量の削減を目指す、バックグラウンドで実行されるシンプルなツールです。

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name        YouTube RM3 - Reduce Memory Usage by Reusing Components
// @namespace   Violentmonkey Scripts
// @version     0.1.0019
// @license     MIT
// @match       https://www.youtube.com/*
// @match       https://studio.youtube.com/live_chat*
//
//
// @author              CY Fung
// @run-at              document-start
// @grant               none
// @unwrap
// @allFrames           true
// @inject-into         page
//
// @compatible          firefox Violentmonkey
// @compatible          firefox Tampermonkey
// @compatible          firefox FireMonkey
// @compatible          chrome Violentmonkey
// @compatible          chrome Tampermonkey
// @compatible          opera Violentmonkey
// @compatible          opera Tampermonkey
// @compatible          safari Stay
// @compatible          edge Violentmonkey
// @compatible          edge Tampermonkey
// @compatible          brave Violentmonkey
// @compatible          brave Tampermonkey
//
// @description         A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
// @description:en      A simple tool that runs in the background to reuse YouTube components, thereby reducing memory usage over time.
// @description:ja      YouTubeコンポーネントを再利用することで、長期的なメモリ使用量の削減を目指す、バックグラウンドで実行されるシンプルなツールです。
// @description:zh-TW   一個在背景執行的簡易工具,可重複利用 YouTube 元件,從而在長期減少記憶體使用量。
// @description:zh-CN   一个在后台运行的简单工具,通过重复利用 YouTube 组件,从而在长期减少内存使用量。
//
// ==/UserScript==

const rm3 = window.rm3 = {};
// console.log(3001);

(() => {

  const DEBUG_OPT = false;
  const CONFIRM_TIME = 4000;
  const CHECK_INTERVAL = 400;
  const DEBUG_dataChangeReflection = true;


  /** @type {globalThis.PromiseConstructor} */
  const Promise = (async () => { })().constructor; // YouTube hacks Promise in WaterFox Classic and "Promise.resolve(0)" nevers resolve.

  // https://qiita.com/piroor/items/02885998c9f76f45bfa0
  // https://gist.github.com/piroor/829ecb32a52c2a42e5393bbeebe5e63f
  function uniq(array) {
    return [...new Set(array)];
  };


  rm3.uniq = uniq; // [[debug]]
  DEBUG_OPT && (rm3.location= location.href);


  rm3.inspect = () => {
    return uniq([...document.getElementsByTagName('*')].filter(e => e?.polymerController?.createComponent_).map(e => e.nodeName)); // [[debug]]
  }


  const insp = o => o ? (o.polymerController || o.inst || o || 0) : (o || 0);
  const indr = o => insp(o).$ || o.$ || 0;

  const getProto = (element) => {
    if (element) {
      const cnt = insp(element);
      return cnt.constructor.prototype || null;
    }
    return null;
  }


  const LinkedArray = (() => {


    class Node {
      constructor(value) {
        this.value = value;
        this.next = null;
        this.prev = null;
      }
    }

    class LinkedArray {
      constructor() {
        this.head = null;
        this.tail = null;
        this.length = 0;
      }

      push(value) {
        const newNode = new Node(value);
        if (this.length === 0) {
          this.head = newNode;
          this.tail = newNode;
        } else {
          this.tail.next = newNode;
          newNode.prev = this.tail;
          this.tail = newNode;
        }
        this.length++;
        return this.length;
      }

      pop() {
        if (this.length === 0) return undefined;
        const removedNode = this.tail;
        if (this.length === 1) {
          this.head = null;
          this.tail = null;
        } else {
          this.tail = removedNode.prev;
          this.tail.next = null;
          removedNode.prev = null;
        }
        this.length--;
        return removedNode.value;
      }

      unshift(value) {
        const newNode = new Node(value);
        if (this.length === 0) {
          this.head = newNode;
          this.tail = newNode;
        } else {
          newNode.next = this.head;
          this.head.prev = newNode;
          this.head = newNode;
        }
        this.length++;
        return this.length;
      }

      shift() {
        if (this.length === 0) return undefined;
        const removedNode = this.head;
        if (this.length === 1) {
          this.head = null;
          this.tail = null;
        } else {
          this.head = removedNode.next;
          this.head.prev = null;
          removedNode.next = null;
        }
        this.length--;
        return removedNode.value;
      }

      size() {
        return this.length;
      }

      // Get a node by index (0-based)
      getNode(index) {
        if (index < 0 || index >= this.length) return null;

        let current;
        let counter;

        // Optimization: start from closest end
        if (index < this.length / 2) {
          current = this.head;
          counter = 0;
          while (counter !== index) {
            current = current.next;
            counter++;
          }
        } else {
          current = this.tail;
          counter = this.length - 1;
          while (counter !== index) {
            current = current.prev;
            counter--;
          }
        }
        return current;
      }

      // Get value by index
      get(index) {
        const node = this.getNode(index);
        return node ? node.value : undefined;
      }

      // Find the first node with the given value and return both node and index
      findNode(value) {
        let current = this.head;
        let idx = 0;
        while (current) {
          if (current.value === value) {
            return { node: current, index: idx };
          }
          current = current.next;
          idx++;
        }
        return { node: null, index: -1 };
      }

      toArray() {
        const arr = [];
        let current = this.head;
        while (current) {
          arr.push(current.value);
          current = current.next;
        }
        return arr;
      }

      // Insert a new value before a given node (provided you already have the node reference)
      insertBeforeNode(node, newValue) {
        if (!node) {
          this.unshift(newValue);
          return true;
        }

        if (node === this.head) {
          // If the target is the head, just unshift
          this.unshift(newValue);
        } else {
          const newNode = new Node(newValue);
          const prevNode = node.prev;

          prevNode.next = newNode;
          newNode.prev = prevNode;
          newNode.next = node;
          node.prev = newNode;

          this.length++;
        }
        return true;
      }

      // Insert a new value after a given node (provided you already have the node reference)
      insertAfterNode(node, newValue) {

        if (!node) {
          this.push(newValue);
          return true;
        }

        if (node === this.tail) {
          // If the target is the tail, just push
          this.push(newValue);
        } else {
          const newNode = new Node(newValue);
          const nextNode = node.next;

          node.next = newNode;
          newNode.prev = node;
          newNode.next = nextNode;
          nextNode.prev = newNode;

          this.length++;
        }
        return true;
      }

      // Insert a new value before the first occurrence of an existing value (search by value)
      insertBefore(existingValue, newValue) {
        const { node } = this.findNode(existingValue);
        if (!node) return false; // Not found
        return this.insertBeforeNode(node, newValue);
      }

      // Insert a new value after the first occurrence of an existing value (search by value)
      insertAfter(existingValue, newValue) {
        const { node } = this.findNode(existingValue);
        if (!node) return false; // Not found
        return this.insertAfterNode(node, newValue);
      }



      // Delete a given node from the list
      deleteNode(node) {
        if (!node) return false;

        if (this.length === 1 && node === this.head && node === this.tail) {
          // Only one element in the list
          this.head = null;
          this.tail = null;
        } else if (node === this.head) {
          // Node is the head
          this.head = node.next;
          this.head.prev = null;
          node.next = null;
        } else if (node === this.tail) {
          // Node is the tail
          this.tail = node.prev;
          this.tail.next = null;
          node.prev = null;
        } else {
          // Node is in the middle
          const prevNode = node.prev;
          const nextNode = node.next;
          prevNode.next = nextNode;
          nextNode.prev = prevNode;
          node.prev = null;
          node.next = null;
        }

        this.length--;
        return true;
      }

    }


    LinkedArray.Node = Node;
    return LinkedArray;
  })();



  class LimitedSizeSet extends Set {
    constructor(n) {
      super();
      this.limit = n;
    }

    add(key) {
      if (!super.has(key)) {
        super.add(key);
        let n = super.size - this.limit;
        if (n > 0) {
          const iterator = super.values();
          do {
            const firstKey = iterator.next().value; // Get the first (oldest) key
            super.delete(firstKey); // Delete the oldest key
          } while (--n > 0)
        }
      }
    }

    removeAdd(key) {
      super.delete(key);
      this.add(key);
    }

  }



  if (!document.createElement9512 && typeof document.createElement === 'function' && document.createElement.length === 1) {

    // sizing of Map / Set. Shall limit ?

    const hookTos = new Set(); // [[debug]]
    DEBUG_OPT && (rm3.hookTos = hookTos);

    // const reusePool = new Map(); // xx858
    const entryRecords = new WeakMap(); // a weak link between element and record

    // rm3.list = [];

    const operations = rm3.operations = new Set(); // to find out the "oldest elements"

    const availablePools = rm3.availablePools = new Map(); // those "old elements" can be used
    let lastTimeCheck = 0;

    const reuseRecord_ = new LimitedSizeSet(256); // [[debug]]
    const reuseCount_ = new Map();

    let noTimeCheck = false;

    // const defaultValues = new Map();
    // const noValues = new Map();

    const timeCheck = () => {
      // regularly check elements are old enough to put into the available pools
      // 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.

      const ct = Date.now();
      if (ct - lastTimeCheck < CHECK_INTERVAL || noTimeCheck) return;
      lastTimeCheck = ct;
      noTimeCheck = true;

      // 16,777,216
      if (hookTos.size > 777216) hookTos.clear(); // just debug usage, dont concern
      if (operations.size > 7777216) {
        // extremely old elements in operations mean they have no attach/detach action. so no reuse as well. they are just trash in memory.
        // as no checking of the weakRef.deref() being null or not, those trash could be already cleaned. However we don't concern this.
        // (not to count whether they are actual memory trash or not)
        const half = operations.size >>> 1;
        let i = 0;
        for (const value of operations) {
          if (i++ > half) break;
          operations.delete(value);
        }
      }

      // {
      //   // smallest to largest
      //   // past to recent

      //   const iterator = operations[Symbol.iterator]();
      //   console.log(1831, '------------------------')
      //   while (true) {
      //     const iteratorResult = iterator.next(); // 順番に値を取りだす
      //     if (iteratorResult.done) break; // 取り出し終えたなら、break
      //     console.log(1835, iteratorResult.value[3])
      //   }

      //   console.log(1839, '------------------------')
      // }

      // Set iterator
      // s.add(2) s.add(6) s.add(1) s.add(3)
      // next: 2 -> 6 -> 1 -> 3
      // op1 (oldest) -> op2 -> op3 -> op4 (latest)
      const iterator = operations[Symbol.iterator]();

      const targetTime = ct - CONFIRM_TIME;

      const pivotNodes = new WeakMap();

      while (true) {
        const iteratorResult = iterator.next(); // 順番に値を取りだす
        if (iteratorResult.done) break; // 取り出し終えたなら、break
        const entryRecord = iteratorResult.value;
        if (entryRecord[3] > targetTime) break;

        if (!entryRecord[4] && entryRecord[1] < 0 && entryRecord[2] > 0) {
          const element = entryRecord[0].deref();
          const eKey = (element || 0).__rm3Tag003__;
          if (!eKey) {
            operations.delete(entryRecord);
          } else if (element.isConnected === false && insp(element).isAttached === false) {
            entryRecord[4] = true;

            let availablePool = availablePools.get(eKey);
            if (!availablePool) availablePools.set(eKey, availablePool = new LinkedArray());
            if (!(availablePool instanceof LinkedArray)) throw new Error();
            DEBUG_OPT && console.log(3885,'add key', eKey, availablePools.size)
            // rm3.showSize = ()=>availablePools.size
            // setTimeout(()=>{
            //   // window?.euu1 = availablePools
            //   // window?.euu2 = availablePools.size
            //   console.log(availablePools.size)
            // }, 8000)
            let pivotNode = pivotNodes.get(availablePool);
            if (!pivotNode) pivotNodes.set(availablePool, pivotNode = availablePool.head) // cached the previous newest node (head) as pivotNode

            availablePool.insertBeforeNode(pivotNode, entryRecord); // head = newest, tail = oldest

          }
        }

      }
      noTimeCheck = false;

    }

    const attachedDefine = function () {

      Promise.resolve().then(timeCheck);
      try {
        const hostElement = this?.hostElement;
        if (hostElement instanceof HTMLElement) {
          const entryRecord = entryRecords.get(hostElement);
          if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === true && this?.isAttached === true) {
            noTimeCheck = true;
            const ct = Date.now();
            entryRecord[1] = ct;
            entryRecord[2] = -1;
            entryRecord[3] = ct;
            operations.delete(entryRecord);
            operations.add(entryRecord);
            noTimeCheck = false;
            // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
            // entryRecord[4] is not required to be updated here.
          }
        }
      } catch (e) { }
      return this.attached9512();
    }
    const detachedDefine = function () {

      Promise.resolve().then(timeCheck);
      try {
        const hostElement = this?.hostElement;
        if (hostElement instanceof HTMLElement) {
          const entryRecord = entryRecords.get(hostElement);
          if (entryRecord && entryRecord[0].deref() === hostElement && hostElement.isConnected === false && this?.isAttached === false) {
            noTimeCheck= true;
            const ct = Date.now();
            entryRecord[2] = ct;
            entryRecord[1] = -1;
            entryRecord[3] = ct;
            operations.delete(entryRecord);
            operations.add(entryRecord);
            noTimeCheck= false;
            // note: because of performance prespective, deletion for availablePools[eKey]'s linked element would not be done here.
            // entryRecord[4] is not required to be updated here.
          }
        }
      } catch (e) { }

      return this.detached9512();
    }


    // function cpy(x) {
    //   if (!x) return x;
    //   try {
    //     if (typeof x === 'object' && typeof x.length ==='number' && typeof x.slice === 'function') {
    //       x = x.slice(0)
    //     } else if (typeof x === 'object' && !x.length) {
    //       x = JSON.parse(JSON.stringify(x));
    //     } else {
    //       return Object.assign({}, x);
    //     }
    //   } catch (e) { }
    //   return x;
    // }

    async function digestMessage(message) {
      const msgUint8 = new TextEncoder().encode(message); // (utf-8 の) Uint8Array にエンコードする
      const hashBuffer = await crypto.subtle.digest("SHA-256", msgUint8); // メッセージをハッシュする
      const hashArray = Array.from(new Uint8Array(hashBuffer)); // バッファーをバイト列に変換する
      const hashHex = hashArray
        .map((b) => b.toString(16).padStart(2, "0"))
        .join(""); // バイト列を 16 進文字列に変換する
      return hashHex.toUpperCase();
    }


    let onPageContainer = null;

    const createComponentDefine_ = function (a, b, c) {

      Promise.resolve().then(timeCheck);


      const creatorTag = this?.is || this?.nodeName?.toLowerCase() || '';

      const componentTag = typeof a === 'string' ? a : ((a || 0).component || '');



      const eKey = creatorTag && componentTag ? `${creatorTag}.${componentTag}` : '*'; // '*' for play-safe
      const availablePool = availablePools.get(eKey);

      try {

        if (availablePool instanceof LinkedArray) {
          noTimeCheck = true;

          let node = availablePool.tail; // oldest

          while (node instanceof LinkedArray.Node) {
            const entryRecord = node.value;
            const prevNode = node.prev;

            let ok = false;
            let elm = null;
            if (entryRecord[1] < 0 && entryRecord[2] > 0 && entryRecord[4]) {
              elm = entryRecord[0].deref();
              // elm && console.log(3882, (elm.__shady_native_textContent || elm.textContent))
              if (elm && elm instanceof HTMLElement && elm.isConnected === false && insp(elm).isAttached === false && elm.parentNode === null) {
                ok = true;
              }
            }

            if (ok) {

              // useEntryRecord = entryRecord;
              entryRecord[4] = false;
              // console.log('nodeDeleted', 1, entryRecord[0].deref().nodeName)
              availablePool.deleteNode(node);
              // break;

              if (!onPageContainer) {
                let p = document.createElement('noscript');
                document.body.prepend(p);
                onPageContainer = p;
              }

              onPageContainer.appendChild(elm); // to fix some issues for the rendered elements

              const cnt = insp(elm);


              cnt.__dataInvalid = false;
              // cnt._initializeProtoProperties(cnt.data)

              // window.meaa = cnt.$.container;
              if (typeof (cnt.__data || 0) === 'object') {
                cnt.__data = Object.assign({}, cnt.__data);
              }
              cnt.__dataPending = {};
              cnt.__dataOld = {};

              try {
                cnt.markDirty();
              } catch (e) { }
              try {
                cnt.markDirtyVisibilityObserver();
              } catch (e) { }

              try{
                cnt.wasPrescan = cnt.wasVisible = !1
              }catch(e){}


              // try{
              //   cnt._setPendingProperty('data', Object.assign({}, cntData), !0);
              // }catch(e){}
              // try {
              //   cnt._flushProperties();
              // } catch (e) { }

              if (DEBUG_OPT && DEBUG_dataChangeReflection) {

                let jC1 = null;
                let jC2 = null;
                const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
                try {
                  jC1 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
                  // console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
                } catch (e) {
                  console.warn(e);
                }

                setTimeout(() => {
                  try {
                    jC2 = (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent);
                    // console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
                  } catch (e) {
                    console.warn(e);
                  }

                  (async () => {



                    jC1 = await digestMessage(jC1);
                    jC2 = await digestMessage(jC2);

                    console.log(83804, jKey, jC1.substring(0, 7), jC2.substring(0, 7))

                  })()


                }, 1000);
              }

              // // try{

              // //             console.log(83801, JSON.stringify(cntData))
              // // }catch(e){
              // //   console.warn(e);
              // // }

              // const jKey = `${Math.floor(Math.random() * 314159265359 + 314159265359).toString(36)}`;
              // try{

              //             console.log(83802, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
              // }catch(e){
              //   console.warn(e);
              // }

              // setTimeout(()=>{
              // // try{

              // //             console.log(83803, JSON.stringify(cntData))
              // // }catch(e){
              // //   console.warn(e);
              // // }
              // try{

              //             console.log(83804, jKey, (cnt.hostElement.__shady_native_textContent || cnt.hostElement.textContent))
              // }catch(e){
              //   console.warn(e);
              // }
              // }, 1000);


              // reference
              // https://www.youtube.com/s/desktop/c01ea7e3/jsbin/live_chat_polymer.vflset/live_chat_polymer.js
              // a.prototype._initializeProtoProperties = function(c) {
              //     this.__data = Object.create(c);
              //     this.__dataPending = Object.create(c);
              //     this.__dataOld = {}
              // }

              // ------- (NO USE) ------
              //
              // a.prototype._initializeProperties = function() {
              //     this.__dataProto && (this._initializeProtoProperties(this.__dataProto),
              //     this.__dataProto = null);
              //     b.prototype._initializeProperties.call(this)
              // }
              // ;
              // a.prototype._initializeProtoProperties = function(c) {
              //     for (var d in c)
              //         this._setProperty(d, c[d])
              // }
              //
              // ------- (NO USE) ------


              // // cnt.__dataReady = false;
              // cnt.__dataInvalid = true;
              // cnt.__dataEnabled = false; // tbc
              // // if('__dataEnabled' in cnt)   cnt.__dataEnabled = false;
              // // if ('__dataReady' in cnt && typeof cnt.__dataReady === 'boolean') cnt.__dataReady = false;
              // // if ('__dataInvalid' in cnt && typeof cnt.__dataInvalid === 'boolean') cnt.__dataInvalid = true;

              // // try {
              // //   if ('data' in cnt) cnt.data = null;
              // //   if ('__data' in cnt) cnt.__data = null;
              // // } catch (e) {
              // //   console.warn(e)
              // // }

              // // try {
              // //   if ('data' in cnt) cnt.data = {};
              // //   if ('__data' in cnt) cnt.__data = {};
              // // } catch (e) {
              // //   console.warn(e)
              // // }











              // //     const noValue = noValues.get(eKey);
              // //     if(noValue){
              // //       if(!noValue.data) cnt.data = noValue.data;
              // //       if(!noValue.__data) cnt.data = noValue.__data;
              // //     }

              // //     const defaultValue = defaultValues.get(eKey);
              // //     if (defaultValue) {

              // //     try {
              // //       if ('data' in defaultValue) cnt.data = cpy(cnt.data);
              // //       if ('__data' in defaultValue) cnt.__data = cpy(cnt.__data);
              // // } catch (e) {
              // //       console.warn(e)
              // //     }
              // //     }

              // //     const flg001 = elm.__rm3Flg001__;
              // //     if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;







              // // const flg001 = elm.__rm3Flg001__;
              // // if (cnt.__dataEnabled !== flg001) cnt.__dataEnabled = flg001;


              // if (cnt.__dataPending && typeof cnt.__dataPending === 'object') cnt.__dataPending = null;
              // if (cnt.__dataOld && typeof cnt.__dataOld === 'object') cnt.__dataOld = null;

              // // cnt.__dataInstanceProps = null;
              // if (cnt.__dataCounter && typeof cnt.__dataCounter === 'number') cnt.__dataCounter = 0;
              // // cnt.__serializing = !1;



              // if ('__dataClientsInitialized' in cnt || '__dataClientsReady' in cnt) {

              //   if ('__dataClientsInitialized' in cnt !== '__dataClientsReady' in cnt) {

              //     console.log('[rm3-warning] __dataClientsInitialized and __dataClientsReady should exist in the controller');

              //   }

              //   cnt.__dataClientsReady = !1;
              //   cnt.__dataLinkedPaths = cnt.__dataToNotify = cnt.__dataPendingClients = null;
              //   cnt.__dataHasPaths = !1;
              //   cnt.__dataCompoundStorage = null; // cnt.__dataCompoundStorage = cnt.__dataCompoundStorage || null;
              //   cnt.__dataHost = null; // cnt.__dataHost = cnt.__dataHost || null;
              //   if (!cnt.__dataTemp) cnt.__dataTemp = {}; // cnt.__dataTemp = {};
              //   cnt.__dataClientsInitialized = !1;

              // }

              if (entryRecord[5] < 1e9) entryRecord[5] += 1;
              DEBUG_OPT && Promise.resolve().then(() => console.log(`${eKey} reuse`, entryRecord)); // give some time for attach process
              DEBUG_OPT && reuseRecord_.add([Date.now(), cnt.is, entryRecord]);
              DEBUG_OPT && reuseCount_.set(cnt.is, (reuseCount_.get(cnt.is) || 0) + 1)
              if (rm3.reuseCount < 1e9) rm3.reuseCount++;

              return elm;


            }

            // console.log('condi88', entryRecord[1] < 0 , entryRecord[2] > 0 , !!entryRecord[4], !!entryRecord[0].deref())

            entryRecord[4] = false;

            // console.log(entryRecord);
            // console.log('nodeDeleted',2, entryRecord[0]?.deref()?.nodeName)
            availablePool.deleteNode(node);
            node = prevNode;

          }
          // for(const ) availablePool
          // noTimeCheck = false;
        }

      } catch (e) {
        console.warn(e)
      }
      noTimeCheck = false;


      // console.log('createComponentDefine_', a, b, c)

      // if (!reusePool.has(componentTag)) reusePool.set(componentTag, new LinkedArray()); // xx858

      // const pool = reusePool.get(componentTag); // xx858
      // if (!(pool instanceof LinkedArray)) throw new Error(); // xx858


      const newElement = this.createComponent9512_(a, b, c);
      // if(componentTag.indexOf( 'ticker')>=0)console.log(1883, a,newElement)

      try {

        const cntE = insp(newElement);
        if (!cntE.attached9512 && cntE.attached) {

          const cProtoE = getProto(newElement);


          if (cProtoE.attached === cntE.attached) {

            if (!cProtoE.attached9512 && typeof cProtoE.attached === 'function' && cProtoE.attached.length === 0) {

              cProtoE.attached9512 = cProtoE.attached;

              cProtoE.attached = attachedDefine;
              // hookTos.add(a);
            }
          } else {

            if (typeof cntE.attached === 'function' && cntE.attached.length === 3) {
              cntE.attached9512 = cntE.attached;

              cntE.attached = attachedDefine;
              // hookTos.add(a);
            }
          }


        }

        if (!cntE.detached9512 && cntE.detached) {

          const cProtoE = getProto(newElement);


          if (cProtoE.detached === cntE.detached) {

            if (!cProtoE.detached9512 && typeof cProtoE.detached === 'function' && cProtoE.detached.length === 0) {

              cProtoE.detached9512 = cProtoE.detached;

              cProtoE.detached = detachedDefine;
              // hookTos.add(a);
            }
          } else {

            if (typeof cntE.detached === 'function' && cntE.detached.length === 3) {
              cntE.detached9512 = cntE.detached;

              cntE.detached = detachedDefine;
              // hookTos.add(a);
            }
          }


        }


        const acceptance = true;
        // const acceptance = !cntE.__dataReady && cntE.__dataInvalid !== false; // we might need to change the acceptance condition along with YouTube Coding updates.
        if (acceptance) {

          // [[ weak ElementNode, attached time, detached time, time of change, inside availablePool, reuse count ]]
          const entryRecord = [new WeakRef(newElement), -1, -1, -1, false, 0];

          newElement.__rm3Tag003__ = eKey;
          // pool.push(entryRecord);
          entryRecords.set(newElement, entryRecord);
          //   newElement.__rm3Tag001__ = creatorTag;
          //   newElement.__rm3Tag002__ = componentTag;

          //   newElement.__rm3Flg001__ = cntE.__dataEnabled;
          // // console.log(5928, cntE.data, cntE.__data)
          // if (!defaultValues.has(eKey)){

          //   const o = {};

          //   if('data' in cntE) o.data = cpy(cntE.data);
          //   if('__data' in cntE) o.__data = cpy(cntE.__data);
          //    defaultValues.set(eKey, o);

          // }

          // if(!noValues.has(eKey)){
          //   const o = {};
          //   o.data = true;
          //   try {

          //     if (!cntE.data) o.data = cntE.data;
          //   } catch (e) { }

          //   o.__data = true;
          //   try {

          //     if (!cntE.__data) o.__data = cntE.__data;
          //   } catch (e) { }
          //   noValues.set(eKey, o)
          // }

        } else {
          // console.log(5920, cntE.__dataReady, cntE.__dataInvalid)
        }


      } catch (e) {
        console.warn(e);
      }
      return newElement;


    }

    document.createElement9512 = document.createElement;
    document.createElement = function (a) {
      const r = document.createElement9512(a);
      try {
        const cnt = insp(r);
        if (cnt.createComponent_ && !cnt.createComponent9512_) {
          const cProto = getProto(r);
          if (cProto.createComponent_ === cnt.createComponent_) {

            if (!cProto.createComponent9512_ && typeof cProto.createComponent_ === 'function' && cProto.createComponent_.length === 3) {

              cProto.createComponent9512_ = cProto.createComponent_;

              cProto.createComponent_ = createComponentDefine_;
              DEBUG_OPT && hookTos.add(a);
            }
          } else {

            if (typeof cnt.createComponent_ === 'function' && cnt.createComponent_.length === 3) {
              cnt.createComponent9512_ = cnt.createComponent_;

              cnt.createComponent_ = createComponentDefine_;
              DEBUG_OPT && hookTos.add(a);
            }
          }

        }

      } catch (e) {
        console.warn(e)
      }


      return r;
    }

    rm3.checkWhetherUnderParent = () => {
      const r = [];
      for (const operation of operations) {
        const elm = operation[0].deref();
        if (operation[2] > 0) {

          r.push([!!elm, elm?.nodeName.toLowerCase(), elm?.parentNode === null]);
        }
      }
      return r;
    }

    rm3.hookTags = () => {

      const r = new Set();
      for (const operation of operations) {
        const elm = operation[0].deref();
        if (elm && elm.is) {

          r.add(elm.is)
        }
      }
      return [...r];
    }


    DEBUG_OPT && (rm3.reuseRecord = () => {
      return [...reuseRecord_]; // [[debug]]
    });

    DEBUG_OPT && (rm3.reuseCount_ = reuseCount_);

  }

  (rm3.reuseCount = 0); // window.rm3 will be zero initially if this script has no runtime complier error in the initialization phase.

})();