Remove uiSourceCodes from NetworkPersistenceManager on project removal

The `NetworkPersistenceManager` contains
`networkUISourceCodeForEncodedPath`, a map from paths to
`UISourceCodes`. Scripts are not properly cleared from this map upon
page navigation and accumulate in the map, whereas other types of
resources are removed upon navigation.
This CL ensures that when a project is removed, its `UISourceCodes`
are removed from `networkUISourceCodeForEncodedPath`.

Fixed: 1422576
Change-Id: I1df732b74ddc3feacc6e54cbf3a0c40b3291858b
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/4315992
Commit-Queue: Wolfgang Beyer <wolfi@chromium.org>
Reviewed-by: Jaroslav Sevcik <jarin@chromium.org>
diff --git a/front_end/models/persistence/NetworkPersistenceManager.ts b/front_end/models/persistence/NetworkPersistenceManager.ts
index be73627..6e9feaa 100644
--- a/front_end/models/persistence/NetworkPersistenceManager.ts
+++ b/front_end/models/persistence/NetworkPersistenceManager.ts
@@ -771,6 +771,9 @@
   }
 
   private async onProjectRemoved(project: Workspace.Workspace.Project): Promise<void> {
+    for (const uiSourceCode of project.uiSourceCodes()) {
+      await this.networkUISourceCodeRemoved(uiSourceCode);
+    }
     if (project === this.projectInternal) {
       await this.setProject(null);
     }
diff --git a/test/unittests/front_end/models/persistence/NetworkPersistenceManager_test.ts b/test/unittests/front_end/models/persistence/NetworkPersistenceManager_test.ts
index 61aaba2..882bf1b 100644
--- a/test/unittests/front_end/models/persistence/NetworkPersistenceManager_test.ts
+++ b/test/unittests/front_end/models/persistence/NetworkPersistenceManager_test.ts
@@ -548,13 +548,15 @@
 
   it('is aware of which \'.headers\' files are currently active', done => {
     const workspace = Workspace.Workspace.WorkspaceImpl.instance();
+    const project = {
+      type: () => Workspace.Workspace.projectTypes.Network,
+    } as Workspace.Workspace.Project;
     const networkUISourceCode = {
       url: () => 'https://www.example.com/hello/world/index.html',
-      project: () => ({
-        type: () => Workspace.Workspace.projectTypes.Network,
-      }),
+      project: () => project,
       contentType: () => Common.ResourceType.resourceTypes.Document,
     } as Workspace.UISourceCode.UISourceCode;
+    project.uiSourceCodes = () => [networkUISourceCode];
 
     const eventURLs: string[] = [];
     networkPersistenceManager.addEventListener(
@@ -577,9 +579,23 @@
       project: () => networkPersistenceManager.project(),
     } as Workspace.UISourceCode.UISourceCode));
 
+    workspace.dispatchEventToListeners(Workspace.Workspace.Events.ProjectRemoved, project);
+
     setTimeout(() => {
       assert.deepStrictEqual(
           eventURLs, ['file:///path/to/overrides/.headers', 'file:///path/to/overrides/www.example.com/.headers']);
+      assert.isFalse(networkPersistenceManager.hasMatchingNetworkUISourceCodeForHeaderOverridesFile({
+        url: () => 'file:///path/to/overrides/www.example.com/.headers',
+        project: () => networkPersistenceManager.project(),
+      } as Workspace.UISourceCode.UISourceCode));
+      assert.isFalse(networkPersistenceManager.hasMatchingNetworkUISourceCodeForHeaderOverridesFile({
+        url: () => 'file:///path/to/overrides/.headers',
+        project: () => networkPersistenceManager.project(),
+      } as Workspace.UISourceCode.UISourceCode));
+      assert.isFalse(networkPersistenceManager.hasMatchingNetworkUISourceCodeForHeaderOverridesFile({
+        url: () => 'file:///path/to/overrides/www.foo.com/.headers',
+        project: () => networkPersistenceManager.project(),
+      } as Workspace.UISourceCode.UISourceCode));
       done();
     }, 0);
   });