Memory leak fixes for several views (#7057)

* Change the mount utility to use Vue's createApp and defineComponent methods

* Fix display layout memory leaks caused by `getSelectionContext`

* fix some display layout leaks due to use of slots

* Fix imagery memory leak (removed span tag). NOTE: CompassRose svg leaks memory - must test on firefox to see if this is a Chrome leak.

* Fix ActionsAPI action collection and applicable actions leak.

* Fix flexible layout memory leaks - remove listeners on unmount. NOTE: One type of overlay plot (Rover Yaw) is still leaking.

* pass in the el on mount

* e2e test config and spec changes

* Remove mounting of limit lines. Use components directly

* test: remove `.only()`

* Fix display layout memory leaks

* Enable passing tests

* e2e README and appActions should be what master has.

* lint: add word to cspell list

* lint: fixes

* lint:fix

* fix: revert `el` change

* fix: remove empty span

* fix: creating shapes in displayLayout

* fix: avoid `splice` as it loses reactivity

* test: reduce timeout time

* quick fixes

* add prod mode and convert the test config to select the correct mode

* Fix webpack prod config

* Add launch flag for exposing window.gc

* never worked

* explicit naming

* rename

* We don't need to destroy view providers

* test: increase timeout time

* test: unskip all mem tests

* fix(vue-loader): disable static hoisting

* chore: run `test:perf:memory`

* Don't destroy view providers

* Move context menu once listener to beforeUnmount instead.

* Disconnect all resize observers on unmount

* Delete Test vue component

* Use beforeUnmount and remove splice(0) in favor of [] for emptying arrays

* re-structure

* fix: unregister listener in pane.vue

* test: tweak timeouts

* chore: lint:fix

* test: unskip perf tests

* fix: unregister events properly

* fix: unregister listener

* fix: unregister listener

* fix: unregister listener

* fix: use `unmounted()`

* fix: unregister listeners

* fix: unregister listener properly

* chore: lint:fix

* test: fix imagery layer toggle test

* test: increase timeout

* Don't use anonymous functions for listeners

* Destroy objects and event listeners properly

* Delete config stores that are created by components

* Use the right unmount hook. Destroy mounted view on unmount.

* Use unmounted, not beforeUnmounted

* Lint fixes

* Fix time strip memory leak

* Undo unneeded change for memory leaks.

* chore: combine common webpack configs

---------

Co-authored-by: Jesse Mazzella <jesse.d.mazzella@nasa.gov>
Co-authored-by: John Hill <john.c.hill@nasa.gov>
This commit is contained in:
Shefali Joshi
2023-09-20 10:34:05 -07:00
committed by GitHub
parent 61e7050391
commit b8949db767
65 changed files with 887 additions and 393 deletions

View File

@ -25,14 +25,16 @@
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<div
class="c-box-view u-style-receiver js-style-receiver"
:class="[styleClass]"
:style="style"
></div>
<template #content>
<div
class="c-box-view u-style-receiver js-style-receiver"
:class="[styleClass]"
:style="style"
></div>
</template>
</layout-frame>
</template>
@ -115,10 +117,18 @@ export default {
this.initSelect
);
},
unmounted() {
beforeUnmount() {
if (this.removeSelectable) {
this.removeSelectable();
}
},
methods: {
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};
</script>

View File

@ -145,7 +145,7 @@ function getItemDefinition(itemType, ...options) {
export default {
components: components,
inject: ['openmct', 'objectPath', 'options', 'objectUtils', 'currentView'],
inject: ['openmct', 'objectPath', 'options', 'currentView'],
props: {
domainObject: {
type: Object,
@ -222,13 +222,21 @@ export default {
this.composition.load();
this.gridDimensions = [this.$el.offsetWidth, this.$el.scrollHeight];
this.openmct.objects.observe(this.domainObject, 'configuration.items', (items) => {
this.layoutItems = items;
});
this.unObserveItems = this.openmct.objects.observe(
this.domainObject,
'configuration.items',
(items) => {
this.layoutItems = [...items];
}
);
this.watchDisplayResize();
},
unmounted() {
beforeUnmount() {
if (this.unObserveItems) {
this.unObserveItems();
}
this.unwatchDisplayResize();
this.openmct.selection.off('change', this.setSelection);
this.composition.off('add', this.addChild);
this.composition.off('remove', this.removeChild);
@ -252,9 +260,15 @@ export default {
this.$el.click();
},
watchDisplayResize() {
const resizeObserver = new ResizeObserver(() => this.updateGrid());
this.unwatchDisplayResize();
this.resizeObserver = new ResizeObserver(this.updateGrid);
resizeObserver.observe(this.$el);
this.resizeObserver.observe(this.$el);
},
unwatchDisplayResize() {
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
},
addElement(itemType, element) {
this.addItem(itemType + '-view', element);
@ -624,7 +638,7 @@ export default {
return this.openmct.objects.makeKeyString(item.identifier) !== keyString;
}
});
this.layoutItems = layoutItems;
this.layoutItems = [...layoutItems];
this.mutate('configuration.items', layoutItems);
this.clearSelection();
},

View File

@ -25,14 +25,16 @@
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<div
class="c-ellipse-view u-style-receiver js-style-receiver"
:class="[styleClass]"
:style="style"
></div>
<template #content>
<div
class="c-ellipse-view u-style-receiver js-style-receiver"
:class="[styleClass]"
:style="style"
></div>
</template>
</layout-frame>
</template>
@ -115,10 +117,18 @@ export default {
this.initSelect
);
},
unmounted() {
beforeUnmount() {
if (this.removeSelectable) {
this.removeSelectable();
}
},
methods: {
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};
</script>

View File

@ -25,10 +25,12 @@
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<div class="c-image-view" :class="[styleClass]" :style="style"></div>
<template #content>
<div class="c-image-view" :style="style"></div>
</template>
</layout-frame>
</template>
@ -118,10 +120,18 @@ export default {
this.initSelect
);
},
unmounted() {
beforeUnmount() {
if (this.removeSelectable) {
this.removeSelectable();
}
},
methods: {
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};
</script>

View File

@ -29,7 +29,7 @@
}"
:style="style"
>
<slot></slot>
<slot name="content"></slot>
<div class="c-frame__move-bar" @mousedown.left="startMove($event)"></div>
</div>
</template>

View File

@ -20,24 +20,26 @@
at runtime from the About dialog for additional information.
-->
<template>
<LayoutFrame
<layout-frame
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<ObjectFrame
v-if="domainObject"
ref="objectFrame"
:domain-object="domainObject"
:object-path="currentObjectPath"
:has-frame="item.hasFrame"
:show-edit-view="false"
:layout-font-size="item.fontSize"
:layout-font="item.font"
/>
</LayoutFrame>
<template #content>
<ObjectFrame
v-if="domainObject"
ref="objectFrame"
:domain-object="domainObject"
:object-path="currentObjectPath"
:has-frame="item.hasFrame"
:show-edit-view="false"
:layout-font-size="item.fontSize"
:layout-font="item.font"
/>
</template>
</layout-frame>
</template>
<script>
@ -104,8 +106,7 @@ export default {
data() {
return {
domainObject: undefined,
currentObjectPath: [],
mutablePromise: undefined
currentObjectPath: []
};
},
watch: {
@ -168,6 +169,12 @@ export default {
delete this.immediatelySelect;
}
});
},
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};

View File

@ -25,42 +25,44 @@
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<div
v-if="domainObject"
ref="telemetryViewWrapper"
class="c-telemetry-view u-style-receiver"
:class="[itemClasses]"
:style="styleObject"
:data-font-size="item.fontSize"
:data-font="item.font"
@contextmenu.prevent="showContextMenu"
@mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip"
>
<div class="is-status__indicator" :title="`This item is ${status}`"></div>
<div v-if="showLabel" class="c-telemetry-view__label">
<div class="c-telemetry-view__label-text">
{{ domainObject.name }}
</div>
</div>
<template #content>
<div
v-if="showValue"
:title="fieldName"
class="c-telemetry-view__value"
:class="[telemetryClass]"
v-if="domainObject"
ref="telemetryViewWrapper"
class="c-telemetry-view u-style-receiver"
:class="[itemClasses]"
:style="styleObject"
:data-font-size="item.fontSize"
:data-font="item.font"
@contextmenu.prevent="showContextMenu"
@mouseover.ctrl="showToolTip"
@mouseleave="hideToolTip"
>
<div class="c-telemetry-view__value-text">
{{ telemetryValue }}
<span v-if="unit && item.showUnits" class="c-telemetry-view__value-text__unit">
{{ unit }}
</span>
<div class="is-status__indicator" :title="`This item is ${status}`"></div>
<div v-if="showLabel" class="c-telemetry-view__label">
<div class="c-telemetry-view__label-text">
{{ domainObject.name }}
</div>
</div>
<div
v-if="showValue"
:title="fieldName"
class="c-telemetry-view__value"
:class="[telemetryClass]"
>
<div class="c-telemetry-view__value-text">
{{ telemetryValue }}
<span v-if="unit && item.showUnits" class="c-telemetry-view__value-text__unit">
{{ unit }}
</span>
</div>
</div>
</div>
</div>
</template>
</layout-frame>
</template>
@ -388,6 +390,12 @@ export default {
async showToolTip() {
const { BELOW } = this.openmct.tooltips.TOOLTIP_LOCATIONS;
this.buildToolTip(await this.getObjectPath(), BELOW, 'telemetryViewWrapper');
},
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};

View File

@ -25,18 +25,20 @@
:item="item"
:grid-size="gridSize"
:is-editing="isEditing"
@move="(gridDelta) => $emit('move', gridDelta)"
@endMove="() => $emit('endMove')"
@move="move"
@endMove="endMove"
>
<div
class="c-text-view u-style-receiver js-style-receiver"
:data-font-size="item.fontSize"
:data-font="item.font"
:class="[styleClass]"
:style="style"
>
<div class="c-text-view__text">{{ item.text }}</div>
</div>
<template #content>
<div
class="c-text-view u-style-receiver js-style-receiver"
:data-font-size="item.fontSize"
:data-font="item.font"
:class="[styleClass]"
:style="style"
>
<div class="c-text-view__text">{{ item.text }}</div>
</div>
</template>
</layout-frame>
</template>
@ -127,10 +129,18 @@ export default {
this.initSelect
);
},
unmounted() {
beforeUnmount() {
if (this.removeSelectable) {
this.removeSelectable();
}
},
methods: {
move(gridDelta) {
this.$emit('move', gridDelta);
},
endMove() {
this.$emit('endMove');
}
}
};
</script>