Bug 1517349 - [release 115] [workers] Update sources and tabs when workers are created and destroyed. (#7623). r=dwalsh
authorJason Laster <jlaster@mozilla.com>
Wed, 02 Jan 2019 16:59:09 -0500
changeset 509433 0340904ea2bc20c873c977d2f93e5124bd4dedef
parent 509432 2dbeeb593cbfb5aecd4f5fea1c404e5d7cb2dd5f
child 509434 b2e2a91389d1908f5bba913cd91e6a7b68c3963d
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdwalsh
bugs1517349
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1517349 - [release 115] [workers] Update sources and tabs when workers are created and destroyed. (#7623). r=dwalsh
devtools/client/debugger/new/src/actions/debuggee.js
devtools/client/debugger/new/src/actions/tabs.js
devtools/client/debugger/new/src/client/firefox/commands.js
devtools/client/debugger/new/src/components/PrimaryPanes/index.js
devtools/client/debugger/new/src/reducers/debuggee.js
--- a/devtools/client/debugger/new/src/actions/debuggee.js
+++ b/devtools/client/debugger/new/src/actions/debuggee.js
@@ -1,14 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import type { Action, ThunkArgs } from "./types";
+import { closeTabsForMissingThreads } from "./tabs";
 
 export function updateWorkers() {
-  return async function({ dispatch, client }: ThunkArgs) {
+  return async function({ dispatch, getState, client }: ThunkArgs) {
     const { workers } = await client.fetchWorkers();
     dispatch(({ type: "SET_WORKERS", workers }: Action));
+
+    dispatch(closeTabsForMissingThreads(workers));
   };
 }
--- a/devtools/client/debugger/new/src/actions/tabs.js
+++ b/devtools/client/debugger/new/src/actions/tabs.js
@@ -12,23 +12,26 @@
 import { isOriginalId } from "devtools-source-map";
 
 import { removeDocument } from "../utils/editor";
 import { selectSource } from "./sources";
 
 import {
   getSourcesByURLs,
   getSourceTabs,
+  getSourceFromId,
   getNewSelectedSourceId,
   removeSourceFromTabList,
   removeSourcesFromTabList
 } from "../selectors";
 
+import { getMainThread } from "../reducers/pause";
+
 import type { Action, ThunkArgs } from "./types";
-import type { Source } from "../types";
+import type { Source, Worker } from "../types";
 
 export function updateTab(source: Source, framework: string): Action {
   const { url, id: sourceId, thread } = source;
   const isOriginal = isOriginalId(source.id);
 
   return {
     type: "UPDATE_TAB",
     url,
@@ -88,8 +91,32 @@ export function closeTabs(urls: string[]
 
     const tabs = removeSourcesFromTabList(getSourceTabs(getState()), sources);
     dispatch(({ type: "CLOSE_TABS", sources, tabs }: Action));
 
     const sourceId = getNewSelectedSourceId(getState(), tabs);
     dispatch(selectSource(sourceId));
   };
 }
+
+export function closeTabsForMissingThreads(workers: Worker[]) {
+  return ({ dispatch, getState }: ThunkArgs) => {
+    const oldTabs = getSourceTabs(getState());
+    const mainThread = getMainThread(getState());
+    const removed = [];
+    for (const { sourceId } of oldTabs) {
+      if (sourceId) {
+        const source = getSourceFromId(getState(), sourceId);
+        if (
+          source.thread != mainThread &&
+          !workers.some(({ actor }) => actor == source.thread)
+        ) {
+          removed.push(source);
+        }
+      }
+    }
+    const tabs = removeSourcesFromTabList(oldTabs, removed);
+    dispatch({ type: "CLOSE_TABS", removed, tabs });
+
+    const sourceId = getNewSelectedSourceId(getState(), tabs);
+    dispatch(selectSource(sourceId));
+  };
+}
--- a/devtools/client/debugger/new/src/client/firefox/commands.js
+++ b/devtools/client/debugger/new/src/client/firefox/commands.js
@@ -401,54 +401,41 @@ async function createSources(client: Thr
   const { sources } = await client.getSources();
   return (
     sources &&
     sources.map(packet => createSource(client.actor, packet, { supportsWasm }))
   );
 }
 
 async function fetchSources(): Promise<any[]> {
-  let sources = await createSources(threadClient);
+  const sources = await createSources(threadClient);
 
   // NOTE: this happens when we fetch sources and then immediately navigate
   if (!sources) {
     return [];
   }
 
-  if (features.windowlessWorkers) {
-    // Also fetch sources from any workers.
-    workerClients = await updateWorkerClients({
-      threadClient,
-      debuggerClient,
-      tabTarget,
-      workerClients
-    });
-
-    const workerNames = Object.getOwnPropertyNames(workerClients);
-    workerNames.forEach(actor => {
-      const workerSources = createSources(workerClients[actor].thread);
-      if (workerSources) {
-        sources = sources.concat(workerSources);
-      }
-    });
-  }
-
   return sources;
 }
 
 async function fetchWorkers(): Promise<{ workers: Worker[] }> {
   if (features.windowlessWorkers) {
     workerClients = await updateWorkerClients({
       tabTarget,
       debuggerClient,
       threadClient,
       workerClients
     });
 
     const workerNames = Object.getOwnPropertyNames(workerClients);
+
+    workerNames.forEach(actor => {
+      createSources(workerClients[actor].thread);
+    });
+
     return {
       workers: workerNames.map(actor =>
         createWorker(actor, workerClients[actor].url)
       )
     };
   }
 
   if (!supportsWorkers(tabTarget)) {
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/index.js
@@ -9,17 +9,18 @@ import { sortBy } from "lodash";
 import { connect } from "../../utils/connect";
 import { Tab, Tabs, TabList, TabPanels } from "react-aria-components/src/tabs";
 import { formatKeyShortcut } from "../../utils/text";
 import actions from "../../actions";
 import {
   getRelativeSources,
   getActiveSearch,
   getSelectedPrimaryPaneTab,
-  getWorkerDisplayName
+  getWorkerDisplayName,
+  isValidThread
 } from "../../selectors";
 import { features, prefs } from "../../utils/prefs";
 import "./Sources.css";
 import classnames from "classnames";
 
 import Outline from "./Outline";
 import SourcesTree from "./SourcesTree";
 
@@ -33,17 +34,18 @@ type State = {
 type Props = {
   selectedTab: SelectedPrimaryPaneTabType,
   sources: SourcesMapByThread,
   horizontal: boolean,
   sourceSearchOn: boolean,
   setPrimaryPaneTab: typeof actions.setPrimaryPaneTab,
   setActiveSearch: typeof actions.setActiveSearch,
   closeActiveSearch: typeof actions.closeActiveSearch,
-  getWorkerDisplayName: string => string
+  getWorkerDisplayName: string => string,
+  isValidThread: string => boolean
 };
 
 class PrimaryPanes extends Component<Props, State> {
   constructor(props: Props) {
     super(props);
 
     this.state = {
       alphabetizeOutline: prefs.alphabetizeOutline
@@ -91,17 +93,19 @@ class PrimaryPanes extends Component<Pro
       >
         {outline}
       </Tab>
     ];
   }
 
   renderThreadSources() {
     const threads = sortBy(
-      Object.getOwnPropertyNames(this.props.sources),
+      Object.getOwnPropertyNames(this.props.sources).filter(
+        this.props.isValidThread
+      ),
       this.props.getWorkerDisplayName
     );
 
     return threads.map(thread => <SourcesTree thread={thread} key={thread} />);
   }
 
   render() {
     const { selectedTab } = this.props;
@@ -127,17 +131,18 @@ class PrimaryPanes extends Component<Pro
     );
   }
 }
 
 const mapStateToProps = state => ({
   selectedTab: getSelectedPrimaryPaneTab(state),
   sources: getRelativeSources(state),
   sourceSearchOn: getActiveSearch(state) === "source",
-  getWorkerDisplayName: thread => getWorkerDisplayName(state, thread)
+  getWorkerDisplayName: thread => getWorkerDisplayName(state, thread),
+  isValidThread: thread => isValidThread(state, thread)
 });
 
 const connector = connect(
   mapStateToProps,
   {
     setPrimaryPaneTab: actions.setPrimaryPaneTab,
     setActiveSearch: actions.setActiveSearch,
     closeActiveSearch: actions.closeActiveSearch
--- a/devtools/client/debugger/new/src/reducers/debuggee.js
+++ b/devtools/client/debugger/new/src/reducers/debuggee.js
@@ -9,16 +9,17 @@
  * @module reducers/debuggee
  */
 
 import { List } from "immutable";
 import type { Record } from "../utils/makeRecord";
 import type { Worker } from "../types";
 import type { Action } from "../actions/types";
 import makeRecord from "../utils/makeRecord";
+import { getMainThread } from "./pause";
 
 export type WorkersList = List<Worker>;
 
 type DebuggeeState = {
   workers: WorkersList
 };
 
 export const createDebuggeeState: () => Record<DebuggeeState> = makeRecord({
@@ -45,9 +46,21 @@ export const getWorkerDisplayName = (sta
     if (actor == thread) {
       return `Worker #${index}`;
     }
     index++;
   }
   return "";
 };
 
+export const isValidThread = (state: OuterState, thread: string) => {
+  if (thread == getMainThread((state: any))) {
+    return true;
+  }
+  for (const { actor } of state.debuggee.workers) {
+    if (actor == thread) {
+      return true;
+    }
+  }
+  return false;
+};
+
 type OuterState = { debuggee: DebuggeeState };