Skip to content

Possible memory leak in autorefresh.js #7085

@LZTWilliam

Description

@LZTWilliam

Version:

codemirror: 5.65.12
Browser: Chrome 120.0.6099.217 (I believe this problem is browser-irrelevent)

Problem:

Code Analysis: As is shown in code below, when cm.display.wrapper.offsetHeight == 0 the autoRefresh addon register listeners of mouseup/keyup event on window, and continiously check the wrapper's offsetHeight. As soon as the offsetHeight check pass, these listeners will be unregistered.

Abnormal Situation: But under the situation that the offsetHeight of wrapper element is keeps 0 during the whole lifetime of a CodeMirror instance, the listener will never be released, causing the instance be referenced by listener on window, and never be gc'd.

My Case: In my circumstance, I have a dialog contains 2 CodeMirror instance, one of which is hidden by display: none. The dialog and its content is destroyed after closing, and re-create if opened again. So the hidden instance might never be rendered, and the wrapper's offsetHeight keeps 0(I'm using CodeMirror constructor to create instance, BTW).
I believe attempts to destroy instance have no use to this problem. For the stopListening function can only be called by check.

// addon/display/autorefresh.js
CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
  if (cm.state.autoRefresh) {
    stopListening(cm, cm.state.autoRefresh)
    cm.state.autoRefresh = null
  }
  if (val && cm.display.wrapper.offsetHeight == 0)
    startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
})

function startListening(cm, state) {
  function check() {

    // >>What if the offsetHeight is always 0 during the whole lifetime of CodeMirror instance?<<
    if (cm.display.wrapper.offsetHeight) {

      stopListening(cm, state)
      if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
        cm.refresh()
    } else {
      state.timeout = setTimeout(check, state.delay)
    }
  }
  state.timeout = setTimeout(check, state.delay)
  state.hurry = function() {
    clearTimeout(state.timeout)
    state.timeout = setTimeout(check, 50)
  }
  CodeMirror.on(window, "mouseup", state.hurry)
  CodeMirror.on(window, "keyup", state.hurry)
}

function stopListening(_cm, state) {
  clearTimeout(state.timeout)
  CodeMirror.off(window, "mouseup", state.hurry)
  CodeMirror.off(window, "keyup", state.hurry)
}

My problem can be resolved in some other way (for example, don't instantiate 2 CodeMirror instance). So we can focus on the possible bug itself.
I'm in hurry so can not providing a minimize reproduction demo for now. If you need one please let me know.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions