General questions

Answered

Memory Leak on Grabber.

Hi!

So when I don't use a custom grabber it doesn't leak when I close it but when I do use my custom grabber ( provided) every time I make an instance of myscript it still is in memory.

Thank you very much for your help! Ive tried to call the unload() function on the editor which does call my detach function in the grabber but STILL leaks. 

  

Register function  

   const customGrabber = {
      attach,
      detach
    };
MyScript.register(this.editorElement, {
      recognitionParams: {
        type: 'MATH',
        apiVersion: 'V4',
        server: {
          applicationKey: ' PUBLIC',
          hmacKey: 'PUBLIC',
          websocket: {
            pingEnabled: false,
            autoReconnect: true
          }
        },
        v4: {
          lang: 'en_US',
          export: {
            'image-resolution': 300,
            jiix: {
              'bounding-box': false,
              strokes: false,
              text: {
                chars: false,
                words: true
              }
            }
          },
          renderer: {
            debug: {
              'draw-text-boxes': false,
              'draw-image-boxes': false
            }
          },
          math: {
            guide: {
              enable: true
            },
            mimeTypes: ['application/x-latex', 'application/mathml+xml', 'application/vnd.myscript.jiix'],
            solver: {
              enable: true,
              'fractional-part-digits': 3,
              'decimal-separator': '.',
              'rounding-mode': 'half up',
              'angle-unit': 'deg'
            },
            margin: {
              bottom: 1,
              left: 1,
              right: 1,
              top: 1
            }
          },
          text: {
            guide: {
              enable: this.bSolve
            },
            smartGuide: true,
            smartGuideFadeOut: {
              enable: true,
              duration: 3000
            },
            mimeTypes: ['text/plain', 'application/vnd.myscript.jiix'],
            margin: {
              top: 20,
              left: 10,
              right: 10
            }
          }
        }
      },
      // triggers: {
      //   exportContent: 'QUIET_PERIOD',
      //   addStrokes: 'QUIET_PERIOD'
      // }
    }, undefined, undefined, {
      grabber: customGrabber
    });

 Grabber Functions:

 

 // Custom grabber
    function attach(element, editor) {
      // Used to unfocus any selection for better pointer events
      function unfocus() {
        if (window.getSelection().type !== 'None') {
          window.getSelection()
            .removeAllRanges();
        }
      }
      let pointerDownOnInput = false;
      let pointerDownOnInputPoint;
      const context = {
        options: { passive: true },
        listeners: [{
          types: ['pointerdown'],
          listener:
            function pointerDownHandler(evt) { // Trigger a pointerDown

              const pointerDownOnEditor = evt.target.id === editor.domElement.id || evt.target.classList.contains('ms-canvas');
              if (this.activePointerId) {
                if (this.activePointerId === evt.pointerId) {
                  console.log(`${evt.type} event with the same id without any pointer up`, evt.pointerId);
                }
              } else if ((evt.button !== 2) && (evt.buttons !== 2) && pointerDownOnEditor) { // Ignore right click
                this.activePointerId = evt.pointerId;
                // Hack for iOS 9 Safari : pointerId has to be int so -1 if > max value
                const pointerId = evt.pointerId > 2147483647 ? -1 : evt.pointerId;
                unfocus();
                //evt.stopPropagation();
                editor.pointerDown(extractPoint(evt, element, editor.configuration), evt.pointerType, pointerId);
              }
              clearIdleTimer();
            }
        }, {
          types: ['pointermove'],
          listener:
            function pointerMoveHandler(evt) { // Trigger a pointerMove
              // Only considering the active pointer
              if (this.activePointerId && this.activePointerId === evt.pointerId) {
                unfocus();
                editor.pointerMove(extractPoint(evt, element, editor.configuration));
              } else if (pointerDownOnInput) {
                const point = extractPoint(evt, element, editor.configuration);
                const diffX = Math.abs(pointerDownOnInputPoint.x - point.x);
                const diffY = Math.abs(pointerDownOnInputPoint.y - point.y);
                // mMaxDiffX = Math.max(diffX, mMaxDiffX);
                const cond1 = diffX < 1 && diffY > 1; // && mMaxDiffX < 15;
                const cond2 = diffX > 1 && diffY > 1; // && mMaxDiffX < 15;
                if (cond1 || cond2) {
                  this.activePointerId = evt.pointerId;
                  // Hack for iOS 9 Safari : pointerId has to be int so -1 if > max value
                  unfocus();
                  const pointerId = evt.pointerId > 2147483647 ? -1 : evt.pointerId;
                  editor.pointerDown(pointerDownOnInputPoint, evt.pointerType, pointerId);
                }
              }
            }
        }, {
          types: ['pointerup'],
          listener:
            function pointerUpHandler(evt) { // Trigger a pointerUp
              pointerDownOnInput = false;
              if (evt.detail) { evt = evt.detail; }
              if (this.activePointerId && this.activePointerId === evt.pointerId) { // Only considering the active pointer
                this.activePointerId = undefined; // Managing the active pointer
                //evt.stopPropagation();
                editor.pointerUp(extractPoint(evt, element, editor.configuration));
              }
              resetIdleTimer();
            }
        }]
      };

      context.listeners.forEach((item) => {
        item.types.forEach(type => element.addEventListener(type, item.listener, context.options));
      });
      //return null;
    }

    function detach(element, context) {
      context.listeners.forEach((item) => {
        item.types.forEach(type => element.removeEventListener(type, item.listener, context.options));
      });
    }

 



 


Best Answer

Hey Oliver,

I fixed it but I have no idea why the fix is the fix. So, all the inner functions inside of the attach function I moved them out and put them on the global level.

Thanks,

Daniel


sorry forgot to add, line 77 in the second javascript snip is return context;

Dear Daniel,


thank you for contacting us.


Currently, after looking at your code, it appears the grabber listeners are properly deleted, and we are not able to see what may be wrong.


When you say the grabber still leaks, we are not sure to exactly understand what you mean: Did you do a memory profiling? What are the objects that are not released? Did you compare side by side the objects with and without the grabber? ...?


Thank you,


Best regards,


Olivier

Hey Oliver,

I''m using this in angular 7 and profiling with google chrome's memory tools and like in the register function when I change  the grabber to your default one  so line 77  in the first code snippet to :

 

 }, undefined, undefined, undefined );

it doesn't leak but with the code I've given you here  this is what the memory tool is telling me:

image

and I have an instance of this for every time I opened the script. On top of that  what is also incredibly weird is I've attempted commenting out everything in attach and just return null and I commented out everything in detach and like that creates this same leak still.

Thank you very much for your time,

Daniel 

Answer

Hey Oliver,

I fixed it but I have no idea why the fix is the fix. So, all the inner functions inside of the attach function I moved them out and put them on the global level.

Thanks,

Daniel

Dear Daniel,


thank you for the update.


Best regards,


Olivier