Repository: vscode
Issue: Memory leak in gettingStarted
Labels: bug,freeze-slow-crash-leak,getting-started

<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- 🕮 Read our guide about submitting issues: https://github.com/microsoft/vscode/wiki/Submitting-Bugs-and-Suggestions -->
<!-- 🔎 Search existing issues to avoid creating duplicates. -->
<!-- 🧪 Test using the latest Insiders build to see if your issue has already been fixed: https://code.visualstudio.com/insiders/ -->
<!-- 💡 Instead of creating your report here, use 'Report Issue' from the 'Help' menu in VS Code to pre-fill useful information. -->
<!-- 🔧 Launch with `code --disable-extensions` to check. -->
Does this issue occur when all extensions are disabled?: Yes

<!-- 🪓 If you answered No above, use 'Help: Start Extension Bisect' from Command Palette to try to identify the cause. -->
<!-- 📣 Issues caused by an extension need to be reported directly to the extension publisher. The 'Help > Report Issue' dialog can assist with this. -->
- VS Code Version: 1.90.2	
- OS Version: Ubuntu 24.04

Steps to Reproduce:

1. Open the welcome fundamentals view
2. Expand "Rich support for all your languages"
3. Expand "Choose you theme"
4. Repeat expanding either element several times
5. Notice that the number of webview event listeners grows each time

```json
{
  "eventListenersWithStackTrace": [
    {
      "type": "drop",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8035",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24774)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24770)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68224)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 291,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:469:60"],
      "originalName": null
    },
    {
      "type": "drop",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8037",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24774)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24770)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68448)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 291,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:469:60"],
      "originalName": null
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.10635",
      "stack": ["listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)"],
      "count": 194,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    },
    {
      "type": "message",
      "description": "B=>{if(!(!this.g||B?.data?.target!==this.a)){if(B.origin!==this.ob(this.g)){console.log(`Skipped renderer receiving message due to mismatched origins: ${B.origin} ${this.ob}`);return}if(B.data.channel===\"webview-ready\"){if(this.H)return;this.Q.debug(`Webview(${this.a}): webview ready`),this.H=B.ports[0],this.H.onmessage=W=>{const F=this.I.get(W.data.channel);if(!F){console.log(`No handlers found for '${W.data.channel}'`);return}F?.forEach(j=>j(W.data.data,W))},this.n?.classList.add(\"ready\"),this.s.type===P.Type.Initializing&&this.s.pendingMessages.forEach(({channel:W,data:F,resolve:j})=>j(this.pb(W,F))),this.s=P.Ready,z.dispose()}}}",
      "objectId": "-6222731808989745711.4.10217",
      "stack": ["listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24942)"],
      "count": 193,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:486:86"],
      "originalName": "e"
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8433",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24829)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68224)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 97,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    },
    {
      "type": "dragend",
      "description": "()=>{this.mb()}",
      "objectId": "-6222731808989745711.4.8435",
      "stack": [
        "listener (/resources/app/out/vs/workbench/workbench.desktop.main.js:1264:24846)",
        "new u (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30819)",
        "r (resources/app/out/vs/workbench/workbench.desktop.main.js:138:30983)",
        "o.mountTo (resources/app/out/vs/workbench/workbench.desktop.main.js:1265:24829)",
        "ve.hc (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:68448)",
        "ve.ic (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:72222)",
        "ve.Yb (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:66403)",
        "HTMLButtonElement.<anonymous> (resources/app/out/vs/workbench/workbench.desktop.main.js:2386:65333)"
      ],
      "count": 97,
      "originalStack": ["/src/vs/workbench/contrib/webview/browser/webviewElement.ts:475:66"],
      "originalName": null
    }
  ],
  "isLeak": true
}

```


### Test script

```sh
git clone git@github.com:SimonSiefke/vscode-memory-leak-finder.git &&
cd vscode-memory-leak-finder &&
git checkout v5.47.0 &&
npm ci &&
node packages/cli/bin/test.js --cwd packages/e2e --only welcome-page.edit --measure event-listeners-with-stack-trace --runs 97 --measure-after --check-leaks &&
cat .vscode-memory-leak-finder-results/event-listeners-with-stack-trace/welcome-page.edit.json
```

### Additional Information

It seems, every time when expanding a section in the gettingStartedView, a new webview is created:

```ts
// gettingStarted.ts
private async buildMediaComponent(stepId: string, forceRebuild: boolean = false) {
		if (stepToExpand.media.type === 'svg') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ title: undefined, options: { disableServiceWorker: true }, contentOptions: {}, extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		} else if (stepToExpand.media.type === 'markdown') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ options: {}, contentOptions: { localResourceRoots: [stepToExpand.media.root], allowScripts: true }, title: '', extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		}
	}
}
```

### Possible solution


A possible solution could be to to dispose the mediaDisposables disposables before creating another webview:

```ts
// gettingStarted.ts
private async buildMediaComponent(stepId: string, forceRebuild: boolean = false) {
        this.mediaDisposables.clear(); // clean up old webview disposables
		if (stepToExpand.media.type === 'svg') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ title: undefined, options: { disableServiceWorker: true }, contentOptions: {}, extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		} else if (stepToExpand.media.type === 'markdown') {
			this.webview = this.mediaDisposables.add(this.webviewService.createWebviewElement({ options: {}, contentOptions: { localResourceRoots: [stepToExpand.media.root], allowScripts: true }, title: '', extension: undefined }));
			this.webview.mountTo(this.stepMediaComponent, this.window);
		}
	}
}


```