wildskyf / TextareaCache

Browser Add-on: Automatically save the content in Textarea.
MIT License
66 stars 7 forks source link

Suggestion on content-script TODO: PERFORMANCE ISSUE #112

Closed tsaost closed 1 month ago

tsaost commented 2 years ago

I've tested the code and seems to be work fine, please see if you can use it in your next release. Thanks.

        function attachEvent() {

            const cacheRule = [
                'textarea',
                'iframe',
                '[contentEditable]',
                '[role="textbox"]',
                '[aria-multiline="true"]'
            ].map(rule => (rule + `:not([${SAVE_TARGET}])`) ).join(',');

            attacheEventTimer = null;
            const editables = document.querySelectorAll(cacheRule); 
            console.log(window.location.href + " attachEvent " +
                        "editables.length:" + editables.length);
            for (const ta of editables) {
                const rn = Math.random();
                const isTEXTAREA = ta.tagName === 'TEXTAREA';
                // Mark ta with SAVE_TARGET so that script will not
                // another keyup event listener to it.
                ta.setAttribute(SAVE_TARGET, true);
                ta.dataset.tcId = isTEXTAREA ? rn : `w-${rn}`;
                ta.addEventListener('keyup', me.saveToStorage);
            }
        }

        // TODO: PERFORMANCE ISSUE
        //    Some textarea might not appear when document finished loading,
        //    but appear when user does something, code here is used to
        //    check every two seconds.
        // browser.runtime.sendMessage({behavior: 'get_options'}).
        // then(setting =>
        //   window.setInterval(attachEvent, setting.intervalToSave));
        browser.runtime.sendMessage({behavior: 'get_options'}).
        then(setting => {
             const timeout = setting.intervalToSave * 2;
             console.log(window.location.href +
                         " attacheEventTimer(" + timeout + ")");
             attacheEventTimer = setTimeout(attachEvent, timeout);
             obseveDOM(document.body, _ => {
                 // Use a timer so that if there is rapid change of the DOM
                 // the script will not call attachEvent repeatedly
                 if (!attacheEventTimer) {
                     attacheEventTimer = setTimeout(attachEvent, timeout);
                 }
             });
        });
    },

let attacheEventTimer;

// https://stackoverflow.com/questions/3219758/detect-changes-in-the-dom
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
const MutationObserver = window.MutationObserver ||
        window.WebKitMutationObserver;

function obseveDOM(obj, callback) {
    if (obj && obj.nodeType === 1) {
        // https://developer.mozilla.org/en-US/docs/Web/API/Element
        // Can only obser nodeType === Node.ELEMENT_NODE (1)
        const observer = new MutationObserver(callback);
        // start observing obj for changes in children
        observer.observe(obj, { childList: true, subtree: true });
        return observer; // can call objserver.disconnect();
    }
}
GHolk commented 1 month ago

I has rewrote the scan interval logic. Its performance is better now. (Including pause in background and prevent setInterval performance problem.)

In fact, you can stop scanning intervally now. The focused textarea will still get cached anyway.