Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🚑 Fix severe memory leak (high memory consumption) #858

Merged
merged 2 commits into from
Jun 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/NeonCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ class NeonCore {
}
return new Promise((resolve): void => {
promise.then(entry => {
// delete unnecessary SVG object reference;
// otherwise, this is not garbage collected!
entry.svg = null;

const currentMEI = entry.mei;
const message: VerovioMessage = {
id: uuidv4(),
Expand Down Expand Up @@ -398,7 +402,7 @@ class NeonCore {
const svg = this.parser.parseFromString(
svgText,
'image/svg+xml'
).documentElement as unknown as SVGSVGElement;
).documentElement as HTMLElement & SVGSVGElement;
this.neonCache.set(pageURI, {
mei: mei,
svg: svg,
Expand Down
36 changes: 22 additions & 14 deletions src/NeonView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@ import NeonCore from './NeonCore';
import { parseManifest } from './utils/NeonManifest';
import { prepareEditMode } from './utils/EditControls';
import setBody from './utils/template/Template';
import * as Types from './Types';
import * as Interfaces from './Interfaces';
import { ModalWindow } from './utils/ModalWindow';
import { NeonManifest, EditorAction, Attributes } from './Types';
import {
InfoInterface,
ModalWindowInterface,
NeonViewParams,
NeumeEditInterface,
TextEditInterface,
TextViewInterface,
ViewInterface
} from './Interfaces';


/**
Expand All @@ -14,31 +22,31 @@ import { ModalWindow } from './utils/ModalWindow';
*/
class NeonView {
/** The manifest describing what to load and where to find it. */
manifest: Types.NeonManifest;
manifest: NeonManifest;
/** Module that displays rendered MEI. */
view: Interfaces.ViewInterface;
view: ViewInterface;
/** Name of the document loaded. */
name: string;
/** Module that handles managing resources, rendering SVGs. */
core: NeonCore;
/** Module that provides additional information on musical elements. */
info: Interfaces.InfoInterface;
info: InfoInterface;
/** Module that allows editing of musical elements. */
NeumeEdit: Interfaces.NeumeEditInterface;
NeumeEdit: NeumeEditInterface;
/** Module that allows viewing of syllable text. */
textView: Interfaces.TextViewInterface;
textView: TextViewInterface;
/** Module that allows editing of syllable text. */
TextEdit: Interfaces.TextEditInterface;
TextEdit: TextEditInterface;
/** Module that controls state and content of Neon modal windows */
modal: Interfaces.ModalWindowInterface;
modal: ModalWindowInterface;

params: Interfaces.NeonViewParams;
params: NeonViewParams;


/**
* Constructor for NeonView. Sets mode and passes constructors.
*/
constructor (params: Interfaces.NeonViewParams) {
constructor (params: NeonViewParams) {
if (!parseManifest(params.manifest)) {
console.error('Unable to parse the manifest');
}
Expand All @@ -50,7 +58,7 @@ class NeonView {
/**
* Set up Neon for any provided editing modules.
*/
setupEdit(params: Interfaces.NeonViewParams): void {
setupEdit(params: NeonViewParams): void {
if (params.NeumeEdit !== undefined || (params.TextEdit !== undefined && params.TextView !== undefined)) {
// Set up display for edit button
prepareEditMode(this);
Expand Down Expand Up @@ -135,7 +143,7 @@ class NeonView {
* @param action - The editor toolkit action object.
* @param pageURI - The URI of the page to perform the action on
*/
edit (action: Types.EditorAction, pageURI: string): Promise<boolean> {
edit (action: EditorAction, pageURI: string): Promise<boolean> {
return this.core.edit(action, pageURI);
}

Expand All @@ -144,7 +152,7 @@ class NeonView {
* @param elementId - The unique ID of the musical element.
* @param pageURI - The URI of the page the element is found on.
*/
getElementAttr (elementID: string, pageURI: string): Promise<Types.Attributes> {
getElementAttr (elementID: string, pageURI: string): Promise<Attributes> {
return this.core.getElementAttr(elementID, pageURI);
}

Expand Down
25 changes: 13 additions & 12 deletions src/SingleView/SingleView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class SingleView implements ViewInterface {
private updateCallbacks: Array<() => void>;
private group: SVGSVGElement;
private bg: SVGImageElement;
private mei: SVGSVGElement;
private svg: SVGSVGElement;
zoomHandler: ZoomHandler;
private displayPanel: DisplayPanel;
readonly pageURI: string;
Expand Down Expand Up @@ -55,12 +55,13 @@ class SingleView implements ViewInterface {
reader.readAsDataURL(blob);
});

this.mei = document.createElementNS('http://www.w3.org/svg', 'svg') as SVGSVGElement;
this.mei.id = 'mei_output';
this.mei.classList.add('neon-container', 'active-page');
// It is better named svg, to avoid confusion with the actual MEI file.
this.svg = document.createElementNS('http://www.w3.org/svg', 'svg') as SVGSVGElement;
this.svg.id = 'mei_output';
this.svg.classList.add('neon-container', 'active-page');

this.group.appendChild(this.bg);
this.group.appendChild(this.mei);
this.group.appendChild(this.svg);
this.container.appendChild(this.group);

this.zoomHandler = new ZoomHandler();
Expand All @@ -79,12 +80,12 @@ class SingleView implements ViewInterface {
* @param svg - New rendered SVG to use.
*/
updateSVG (svg: SVGSVGElement): void {
this.group.replaceChild(svg, this.mei);
this.mei = svg;
this.mei.id = 'mei_output';
this.mei.classList.add('neon-container', 'active-page');
const height = parseInt(this.mei.getAttribute('height'));
const width = parseInt(this.mei.getAttribute('width'));
this.group.replaceChild(svg, this.svg);
this.svg = svg;
this.svg.id = 'mei_output';
this.svg.classList.add('neon-container', 'active-page');
const height = parseInt(this.svg.getAttribute('height'));
const width = parseInt(this.svg.getAttribute('width'));

this.bg.setAttribute('height', height.toString());
this.bg.setAttribute('width', width.toString());
Expand Down Expand Up @@ -208,4 +209,4 @@ class SingleView implements ViewInterface {
}
}

export { SingleView as default };
export { SingleView as default };