Bug 1533418 - Support multiple workers as roots. r=jlast
authorDavid Walsh <dwalsh@mozilla.com>
Wed, 08 May 2019 07:14:44 +0000
changeset 531877 51988a2d186aa08ab16d0ae5aa0d71bee1a7ffad
parent 531876 071e3e7156387b323d52ae3231a8bbffd408d87a
child 531878 e8597b0c4aab785871b6a2a8b7146aec80ad91e1
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlast
bugs1533418
milestone68.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 1533418 - Support multiple workers as roots. r=jlast http://dbg-workers.glitch.me Updated tests in SourcesTree.spec.js to pass and added test to ensure sources are added to the correct thread Differential Revision: https://phabricator.services.mozilla.com/D29225
devtools/client/debugger/images/moz.build
devtools/client/debugger/images/window.svg
devtools/client/debugger/src/actions/navigation.js
devtools/client/debugger/src/actions/source-tree.js
devtools/client/debugger/src/actions/sources/tests/blackbox.spec.js
devtools/client/debugger/src/actions/tests/setProjectDirectoryRoot.spec.js
devtools/client/debugger/src/actions/tests/source-tree.spec.js
devtools/client/debugger/src/actions/types/index.js
devtools/client/debugger/src/client/firefox/create.js
devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js
devtools/client/debugger/src/components/PrimaryPanes/SourcesTreeItem.js
devtools/client/debugger/src/components/PrimaryPanes/index.js
devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTree.spec.js
devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/PrimaryPanes.spec.js.snap
devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
devtools/client/debugger/src/components/QuickOpenModal.js
devtools/client/debugger/src/components/SecondaryPanes/Worker.js
devtools/client/debugger/src/components/shared/AccessibleImage.css
devtools/client/debugger/src/reducers/debuggee.js
devtools/client/debugger/src/reducers/source-tree.js
devtools/client/debugger/src/reducers/sources.js
devtools/client/debugger/src/reducers/tests/sources.spec.js
devtools/client/debugger/src/types.js
devtools/client/debugger/src/utils/source.js
devtools/client/debugger/src/utils/sources-tree/addToTree.js
devtools/client/debugger/src/utils/sources-tree/collapseTree.js
devtools/client/debugger/src/utils/sources-tree/createTree.js
devtools/client/debugger/src/utils/sources-tree/index.js
devtools/client/debugger/src/utils/sources-tree/moz.build
devtools/client/debugger/src/utils/sources-tree/sortTree.js
devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/collapseTree.spec.js.snap
devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/sortTree.spec.js.snap
devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/updateTree.spec.js.snap
devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js
devtools/client/debugger/src/utils/sources-tree/tests/collapseTree.spec.js
devtools/client/debugger/src/utils/sources-tree/tests/getDirectories.spec.js
devtools/client/debugger/src/utils/sources-tree/tests/sortTree.spec.js
devtools/client/debugger/src/utils/sources-tree/tests/updateTree.spec.js
devtools/client/debugger/src/utils/sources-tree/tests/utils.spec.js
devtools/client/debugger/src/utils/sources-tree/updateTree.js
devtools/client/debugger/src/utils/sources-tree/utils.js
devtools/client/debugger/test/mochitest/browser_dbg-asm.js
devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js
devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js
devtools/client/debugger/test/mochitest/browser_dbg-sources-arrow-keys.js
devtools/client/debugger/test/mochitest/browser_dbg-sources.js
devtools/client/framework/test/test_browser_toolbox_debugger.js
--- a/devtools/client/debugger/images/moz.build
+++ b/devtools/client/debugger/images/moz.build
@@ -41,10 +41,11 @@ DevToolsModules(
     'resume.svg',
     'rewind.svg',
     'search.svg',
     'stepIn.svg',
     'stepOut.svg',
     'stepOver.svg',
     'tab.svg',
     'whole-word-match.svg',
+    'window.svg',
     'worker.svg',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/images/window.svg
@@ -0,0 +1,4 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path fill="context-fill" d="M13 1H3a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h11a2 2 0 0 0 2-2V4a3 3 0 0 0-3-3zm1 11a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V6h12zm0-7H2V4a1 1 0 0 1 1-1h10a1 1 0 0 1 1 1z"></path></svg>
\ No newline at end of file
--- a/devtools/client/debugger/src/actions/navigation.js
+++ b/devtools/client/debugger/src/actions/navigation.js
@@ -53,17 +53,17 @@ export function willNavigate(event: Obje
 }
 
 export function connect(url: string, actor: string, canRewind: boolean) {
   return async function({ dispatch }: ThunkArgs) {
     await dispatch(updateWorkers());
     dispatch(
       ({
         type: "CONNECT",
-        mainThread: { url, actor, type: -1 },
+        mainThread: { url, actor, type: -1, name: "" },
         canRewind
       }: Action)
     );
   };
 }
 
 /**
  * @memberof actions/navigation
--- a/devtools/client/debugger/src/actions/source-tree.js
+++ b/devtools/client/debugger/src/actions/source-tree.js
@@ -1,29 +1,14 @@
 /* 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, FocusItem, ThunkArgs } from "./types";
-import type { Context } from "../types";
+import type { TreeNode } from "../utils/sources-tree/types";
 
-export function setExpandedState(thread: string, expanded: Set<string>) {
-  return ({ dispatch, getState }: ThunkArgs) => {
-    dispatch(
-      ({
-        type: "SET_EXPANDED_STATE",
-        thread,
-        expanded
-      }: Action)
-    );
-  };
+export function setExpandedState(expanded: Set<string>) {
+  return { type: "SET_EXPANDED_STATE", expanded };
 }
 
-export function focusItem(cx: Context, item: FocusItem) {
-  return ({ dispatch, getState }: ThunkArgs) => {
-    dispatch({
-      type: "SET_FOCUSED_SOURCE_ITEM",
-      cx,
-      item
-    });
-  };
+export function focusItem(item: TreeNode) {
+  return { type: "SET_FOCUSED_SOURCE_ITEM", item };
 }
--- a/devtools/client/debugger/src/actions/sources/tests/blackbox.spec.js
+++ b/devtools/client/debugger/src/actions/sources/tests/blackbox.spec.js
@@ -25,21 +25,15 @@ describe("blackbox", () => {
     await dispatch(actions.toggleBlackBox(cx, foo1Source));
 
     const fooSource = selectors.getSource(getState(), "foo1");
 
     if (!fooSource) {
       throw new Error("foo should exist");
     }
 
-    const { thread } = selectors.getSourceActorsForSource(
-      getState(),
-      foo1Source.id
-    )[0];
-    const displayedSources = selectors.getDisplayedSourcesForThread(
-      getState(),
-      thread
+    const displayedSources = selectors.getDisplayedSources(getState());
+    expect(displayedSources.FakeThread[fooSource.id].isBlackBoxed).toEqual(
+      true
     );
-
-    expect(displayedSources[fooSource.id].isBlackBoxed).toEqual(true);
     expect(fooSource.isBlackBoxed).toEqual(true);
   });
 });
--- a/devtools/client/debugger/src/actions/tests/setProjectDirectoryRoot.spec.js
+++ b/devtools/client/debugger/src/actions/tests/setProjectDirectoryRoot.spec.js
@@ -6,18 +6,16 @@
 
 import {
   createStore,
   selectors,
   actions,
   makeSource
 } from "../../utils/test-head";
 
-import type { Source } from "../../types";
-
 const { getProjectDirectoryRoot, getDisplayedSources } = selectors;
 
 describe("setProjectDirectoryRoot", () => {
   it("should set domain directory as root", async () => {
     const { dispatch, getState, cx } = createStore();
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     expect(getProjectDirectoryRoot(getState())).toBe("example.com");
   });
@@ -49,24 +47,25 @@ describe("setProjectDirectoryRoot", () =
     });
     const { dispatch, getState, cx } = store;
     await dispatch(actions.newGeneratedSource(makeSource("js/scopes.js")));
     await dispatch(actions.newGeneratedSource(makeSource("lib/vendor.js")));
 
     dispatch(actions.setProjectDirectoryRoot(cx, "localhost:8000/examples/js"));
 
     const filteredSourcesByThread = getDisplayedSources(getState());
-    const filteredSources = Object.values(filteredSourcesByThread)[0];
-    const firstSource: Source = (Object.values(filteredSources)[0]: any);
+    const filteredSources = (Object.values(
+      filteredSourcesByThread.FakeThread
+    ): any)[0];
 
-    expect(firstSource.url).toEqual(
+    expect(filteredSources.url).toEqual(
       "http://localhost:8000/examples/js/scopes.js"
     );
 
-    expect(firstSource.relativeUrl).toEqual("scopes.js");
+    expect(filteredSources.relativeUrl).toEqual("scopes.js");
   });
 
   it("should update the child directory ", () => {
     const { dispatch, getState, cx } = createStore({
       getBreakableLines: async () => []
     });
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com"));
     dispatch(actions.setProjectDirectoryRoot(cx, "example.com/foo/bar"));
--- a/devtools/client/debugger/src/actions/tests/source-tree.spec.js
+++ b/devtools/client/debugger/src/actions/tests/source-tree.spec.js
@@ -7,13 +7,13 @@
 import { actions, selectors, createStore } from "../../utils/test-head";
 const { getExpandedState } = selectors;
 
 describe("source tree", () => {
   it("should set the expanded state", () => {
     const { dispatch, getState } = createStore();
     const expandedState = new Set(["foo", "bar"]);
 
-    expect(getExpandedState(getState(), "FakeThread")).toEqual(undefined);
-    dispatch(actions.setExpandedState("FakeThread", expandedState));
-    expect(getExpandedState(getState(), "FakeThread")).toEqual(expandedState);
+    expect(getExpandedState(getState())).toEqual(new Set([]));
+    dispatch(actions.setExpandedState(expandedState));
+    expect(getExpandedState(getState())).toEqual(expandedState);
   });
 });
--- a/devtools/client/debugger/src/actions/types/index.js
+++ b/devtools/client/debugger/src/actions/types/index.js
@@ -66,20 +66,17 @@ type UpdateTabAction = {|
   +isOriginal?: boolean,
   +sourceId?: string
 |};
 
 type NavigateAction =
   | {| +type: "CONNECT", +mainThread: MainThread, +canRewind: boolean |}
   | {| +type: "NAVIGATE", +mainThread: MainThread |};
 
-export type FocusItem = {
-  thread: string,
-  item: TreeNode
-};
+export type FocusItem = TreeNode;
 
 export type SourceTreeAction =
   | {| +type: "SET_EXPANDED_STATE", +thread: string, +expanded: any |}
   | {| +type: "SET_FOCUSED_SOURCE_ITEM", +cx: Context, item: FocusItem |};
 
 export type ProjectTextSearchAction =
   | {| +type: "ADD_QUERY", +cx: Context, +query: string |}
   | {|
--- a/devtools/client/debugger/src/client/firefox/create.js
+++ b/devtools/client/debugger/src/client/firefox/create.js
@@ -72,11 +72,12 @@ export function createPause(
   };
 }
 
 export function createWorker(actor: string, url: string) {
   return {
     actor,
     url,
     // Ci.nsIWorkerDebugger.TYPE_DEDICATED
-    type: 0
+    type: 0,
+    name: ""
   };
 }
--- a/devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/SourcesTree.js
@@ -11,119 +11,125 @@ import { connect } from "../../utils/con
 
 // Selectors
 import {
   getShownSource,
   getSelectedSource,
   getDebuggeeUrl,
   getExpandedState,
   getProjectDirectoryRoot,
-  getDisplayedSourcesForThread,
+  getDisplayedSources,
   getFocusedSourceItem,
-  getWorkerByThread,
-  getWorkerCount,
   getContext
 } from "../../selectors";
 
 import { getGeneratedSourceByURL } from "../../reducers/sources";
 
 // Actions
 import actions from "../../actions";
 
 // Components
-import AccessibleImage from "../shared/AccessibleImage";
 import SourcesTreeItem from "./SourcesTreeItem";
 import ManagedTree from "../shared/ManagedTree";
 
 // Utils
 import {
   createTree,
   getDirectories,
   isDirectory,
   getSourceFromNode,
   nodeHasChildren,
   updateTree
 } from "../../utils/sources-tree";
+import { parse } from "../../utils/url";
 import { getRawSourceURL } from "../../utils/source";
 
-import { getDisplayName } from "../../utils/workers";
-import { features } from "../../utils/prefs";
-
 import type {
   TreeNode,
   TreeDirectory,
   ParentMap
 } from "../../utils/sources-tree/types";
-import type { Worker, Source, Context } from "../../types";
-import type { SourcesMap, State as AppState } from "../../reducers/types";
+import type { Source, Context, Thread } from "../../types";
+import type {
+  SourcesMapByThread,
+  State as AppState
+} from "../../reducers/types";
 import type { Item } from "../shared/ManagedTree";
 
 type Props = {
   cx: Context,
-  thread: string,
-  worker: Worker,
-  sources: SourcesMap,
+  threads: Thread[],
+  sources: SourcesMapByThread,
   sourceCount: number,
   shownSource?: Source,
   selectedSource?: Source,
   debuggeeUrl: string,
   projectRoot: string,
   expanded: Set<string>,
   selectSource: typeof actions.selectSource,
   setExpandedState: typeof actions.setExpandedState,
   focusItem: typeof actions.focusItem,
-  focused: TreeNode,
-  workerCount: number
+  focused: TreeNode
 };
 
 type State = {
   parentMap: ParentMap,
   sourceTree: TreeDirectory,
   uncollapsedTree: TreeDirectory,
   listItems?: any,
   highlightItems?: any
 };
 
 type SetExpanded = (item: TreeNode, expanded: boolean, altKey: boolean) => void;
 
+function shouldAutoExpand(depth, item, debuggeeUrl) {
+  if (depth !== 1) {
+    return false;
+  }
+
+  const { host } = parse(debuggeeUrl);
+  return item.name === host;
+}
+
 class SourcesTree extends Component<Props, State> {
-  mounted: boolean;
-
   constructor(props: Props) {
     super(props);
-    const { debuggeeUrl, sources, projectRoot } = this.props;
+    const { debuggeeUrl, sources, threads } = this.props;
 
     this.state = createTree({
-      projectRoot,
       debuggeeUrl,
-      sources
+      sources,
+      threads
     });
   }
 
   componentWillReceiveProps(nextProps: Props) {
     const {
       projectRoot,
       debuggeeUrl,
       sources,
       shownSource,
-      selectedSource
+      selectedSource,
+      threads
     } = this.props;
     const { uncollapsedTree, sourceTree } = this.state;
 
     if (
       projectRoot != nextProps.projectRoot ||
       debuggeeUrl != nextProps.debuggeeUrl ||
+      threads != nextProps.threads ||
       nextProps.sourceCount === 0
     ) {
       // early recreate tree because of changes
-      // to project root, debugee url or lack of sources
+      // to project root, debuggee url or lack of sources
       return this.setState(
         createTree({
           sources: nextProps.sources,
-          debuggeeUrl: nextProps.debuggeeUrl
+          debuggeeUrl: nextProps.debuggeeUrl,
+          threads: nextProps.threads
         })
       );
     }
 
     if (nextProps.shownSource && nextProps.shownSource != shownSource) {
       const listItems = getDirectories(nextProps.shownSource, sourceTree);
       return this.setState({ listItems });
     }
@@ -140,67 +146,64 @@ class SourcesTree extends Component<Prop
     }
 
     // NOTE: do not run this every time a source is clicked,
     // only when a new source is added
     if (nextProps.sources != this.props.sources) {
       this.setState(
         updateTree({
           newSources: nextProps.sources,
+          threads: nextProps.threads,
           prevSources: sources,
           debuggeeUrl,
           uncollapsedTree,
           sourceTree
         })
       );
     }
   }
 
   selectItem = (item: TreeNode) => {
     if (item.type == "source" && !Array.isArray(item.contents)) {
       this.props.selectSource(this.props.cx, item.contents.id);
     }
   };
 
   onFocus = (item: TreeNode) => {
-    this.props.focusItem(this.props.cx, { thread: this.props.thread, item });
+    this.props.focusItem(item);
   };
 
   onActivate = (item: TreeNode) => {
     this.selectItem(item);
   };
 
   // NOTE: we get the source from sources because item.contents is cached
   getSource(item: TreeNode): ?Source {
-    const source = getSourceFromNode(item);
-    if (source) {
-      return this.props.sources[source.id];
-    }
-
-    return null;
+    return getSourceFromNode(item);
   }
 
   getPath = (item: TreeNode): string => {
-    const path = `${item.path}/${item.name}`;
+    const { path } = item;
     const source = this.getSource(item);
 
     if (!source || isDirectory(item)) {
       return path;
     }
 
     const blackBoxedPart = source.isBlackBoxed ? ":blackboxed" : "";
+
     return `${path}/${source.id}/${blackBoxedPart}`;
   };
 
   onExpand = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(this.props.thread, expandedState);
+    this.props.setExpandedState(expandedState);
   };
 
   onCollapse = (item: Item, expandedState: Set<string>) => {
-    this.props.setExpandedState(this.props.thread, expandedState);
+    this.props.setExpandedState(expandedState);
   };
 
   isEmpty() {
     const { sourceTree } = this.state;
     return sourceTree.contents.length === 0;
   }
 
   renderEmptyElement(message) {
@@ -225,53 +228,59 @@ class SourcesTree extends Component<Prop
         return sourceContents.contents[0].contents;
       }
       return sourceContents.contents;
     }
 
     return sourceTree.contents;
   };
 
+  getChildren = (item: $Shape<TreeDirectory>) => {
+    return nodeHasChildren(item) ? item.contents : [];
+  };
+
   renderItem = (
     item: TreeNode,
     depth: number,
     focused: boolean,
     _,
     expanded: boolean,
     { setExpanded }: { setExpanded: SetExpanded }
   ) => {
-    const { debuggeeUrl, projectRoot } = this.props;
+    const { debuggeeUrl, projectRoot, threads } = this.props;
 
     return (
       <SourcesTreeItem
         item={item}
+        threads={threads}
         depth={depth}
         focused={focused}
+        autoExpand={shouldAutoExpand(depth, item, debuggeeUrl)}
         expanded={expanded}
         focusItem={this.onFocus}
         selectItem={this.selectItem}
         source={this.getSource(item)}
         debuggeeUrl={debuggeeUrl}
         projectRoot={projectRoot}
         setExpanded={setExpanded}
       />
     );
   };
 
   renderTree() {
     const { expanded, focused } = this.props;
+
     const { highlightItems, listItems, parentMap } = this.state;
 
     const treeProps = {
       autoExpandAll: false,
-      autoExpandDepth: expanded ? 0 : 1,
+      autoExpandDepth: 1,
       expanded,
       focused,
-      getChildren: (item: $Shape<TreeDirectory>) =>
-        nodeHasChildren(item) ? item.contents : [],
+      getChildren: this.getChildren,
       getParent: (item: $Shape<TreeNode>) => parentMap.get(item),
       getPath: this.getPath,
       getRoots: this.getRoots,
       highlightItems,
       itemHeight: 21,
       key: this.isEmpty() ? "empty" : "full",
       listItems,
       onCollapse: this.onCollapse,
@@ -281,117 +290,75 @@ class SourcesTree extends Component<Prop
       renderItem: this.renderItem,
       preventBlur: true
     };
 
     return <ManagedTree {...treeProps} />;
   }
 
   renderPane(...children) {
-    const { projectRoot, thread } = this.props;
+    const { projectRoot } = this.props;
 
     return (
       <div
         key="pane"
         className={classnames("sources-pane", {
-          "sources-list-custom-root": projectRoot,
-          thread
+          "sources-list-custom-root": projectRoot
         })}
       >
         {children}
       </div>
     );
   }
 
-  renderThreadHeader() {
-    const { worker, workerCount } = this.props;
-
-    if (!features.windowlessWorkers || workerCount == 0) {
-      return null;
-    }
-
-    if (worker) {
-      return (
-        <div className="node thread-header" key="thread-header">
-          <AccessibleImage className="worker" />
-          <span className="label">{getDisplayName(worker)}</span>
-        </div>
-      );
-    }
-
-    return (
-      <div className="node thread-header" key="thread-header">
-        <AccessibleImage className={"file"} />
-        <span className="label">{L10N.getStr("mainThread")}</span>
-      </div>
-    );
-  }
-
   render() {
-    const { worker } = this.props;
-
-    if (!features.windowlessWorkers && worker) {
-      return null;
-    }
-
     return this.renderPane(
-      this.renderThreadHeader(),
       this.isEmpty() ? (
         this.renderEmptyElement(L10N.getStr("noSourcesText"))
       ) : (
         <div key="tree" className="sources-list">
           {this.renderTree()}
         </div>
       )
     );
   }
 }
 
 function getSourceForTree(
   state: AppState,
-  displayedSources: SourcesMap,
-  source: ?Source,
-  thread
+  displayedSources: SourcesMapByThread,
+  source: ?Source
 ): ?Source {
   if (!source) {
     return null;
   }
 
-  source = displayedSources[source.id];
   if (!source || !source.isPrettyPrinted) {
     return source;
   }
 
   return getGeneratedSourceByURL(state, getRawSourceURL(source.url));
 }
 
 const mapStateToProps = (state, props) => {
   const selectedSource = getSelectedSource(state);
   const shownSource = getShownSource(state);
-  const focused = getFocusedSourceItem(state);
-  const thread = props.thread;
-  const displayedSources = getDisplayedSourcesForThread(state, thread);
+  const displayedSources = getDisplayedSources(state);
 
   return {
+    threads: props.threads,
     cx: getContext(state),
-    shownSource: getSourceForTree(state, displayedSources, shownSource, thread),
-    selectedSource: getSourceForTree(
-      state,
-      displayedSources,
-      selectedSource,
-      thread
-    ),
+    shownSource: getSourceForTree(state, displayedSources, shownSource),
+    selectedSource: getSourceForTree(state, displayedSources, selectedSource),
     debuggeeUrl: getDebuggeeUrl(state),
-    expanded: getExpandedState(state, props.thread),
-    focused: focused && focused.thread == props.thread ? focused.item : null,
+    expanded: getExpandedState(state),
+    focused: getFocusedSourceItem(state),
     projectRoot: getProjectDirectoryRoot(state),
     sources: displayedSources,
-    sourceCount: Object.values(displayedSources).length,
-    worker: getWorkerByThread(state, thread),
-    workerCount: getWorkerCount(state)
+    sourceCount: Object.values(displayedSources).length
   };
 };
 
 export default connect(
   mapStateToProps,
   {
     selectSource: actions.selectSource,
     setExpandedState: actions.setExpandedState,
--- a/devtools/client/debugger/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -6,47 +6,52 @@
 
 import React, { Component } from "react";
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { showMenu } from "devtools-contextmenu";
 
 import SourceIcon from "../shared/SourceIcon";
 import AccessibleImage from "../shared/AccessibleImage";
+import { getDisplayName, isWorker } from "../../utils/workers";
 
 import {
   getGeneratedSourceByURL,
   getHasSiblingOfSameName,
   hasPrettySource as checkHasPrettySource,
-  getContext
+  getContext,
+  getMainThread
 } from "../../selectors";
 import actions from "../../actions";
 
 import {
   isOriginal as isOriginalSource,
   getSourceQueryString,
   isUrlExtension,
   shouldBlackbox
 } from "../../utils/source";
 import { isDirectory } from "../../utils/sources-tree";
 import { copyToTheClipboard } from "../../utils/clipboard";
 import { features } from "../../utils/prefs";
 
 import type { TreeNode } from "../../utils/sources-tree/types";
-import type { Source, Context } from "../../types";
+import type { Source, Context, MainThread, Thread } from "../../types";
 
 type Props = {
+  autoExpand: ?boolean,
   cx: Context,
   debuggeeUrl: string,
   projectRoot: string,
   source: ?Source,
   item: TreeNode,
   depth: number,
   focused: boolean,
   expanded: boolean,
+  threads: Thread[],
+  mainThread: MainThread,
   hasMatchingGeneratedSource: boolean,
   hasSiblingOfSameName: boolean,
   hasPrettySource: boolean,
   focusItem: TreeNode => void,
   selectItem: TreeNode => void,
   setExpanded: (TreeNode, boolean, boolean) => void,
   clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
   setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot,
@@ -60,57 +65,27 @@ type MenuOption = {
   label: string,
   disabled: boolean,
   click: () => any
 };
 
 type ContextMenu = Array<MenuOption>;
 
 class SourceTreeItem extends Component<Props, State> {
-  getIcon(item: TreeNode, depth: number) {
-    const { debuggeeUrl, projectRoot, source, hasPrettySource } = this.props;
-
-    if (item.path === "webpack://") {
-      return <AccessibleImage className="webpack" />;
-    } else if (item.path === "ng://") {
-      return <AccessibleImage className="angular" />;
-    } else if (isUrlExtension(item.path) && depth === 0) {
-      return <AccessibleImage className="extension" />;
+  componentDidMount() {
+    const { autoExpand, item } = this.props;
+    if (autoExpand) {
+      this.props.setExpanded(item, true, false);
     }
-
-    if (depth === 0 && projectRoot === "") {
-      return (
-        <AccessibleImage
-          className={classnames("globe-small", {
-            debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
-          })}
-        />
-      );
-    }
-
-    if (isDirectory(item)) {
-      return <AccessibleImage className="folder" />;
-    }
-
-    if (hasPrettySource) {
-      return <AccessibleImage className="prettyPrint" />;
-    }
-
-    if (source) {
-      return <SourceIcon source={source} />;
-    }
-
-    return null;
   }
 
   onClick = (e: MouseEvent) => {
     const { item, focusItem, selectItem } = this.props;
 
     focusItem(item);
-
     if (!isDirectory(item)) {
       selectItem(item);
     }
   };
 
   onContextMenu = (event: Event, item: TreeNode) => {
     const copySourceUri2Label = L10N.getStr("copySourceUri2");
     const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
@@ -203,18 +178,79 @@ class SourceTreeItem extends Component<P
     const { item, expanded } = this.props;
     return isDirectory(item) ? (
       <AccessibleImage className={classnames("arrow", { expanded })} />
     ) : (
       <span className="img no-arrow" />
     );
   }
 
-  renderItemName() {
-    const { item } = this.props;
+  renderIcon(item: TreeNode, depth: number) {
+    const {
+      debuggeeUrl,
+      projectRoot,
+      source,
+      hasPrettySource,
+      threads
+    } = this.props;
+
+    if (item.name === "webpack://") {
+      return <AccessibleImage className="webpack" />;
+    } else if (item.name === "ng://") {
+      return <AccessibleImage className="angular" />;
+    } else if (isUrlExtension(item.path) && depth === 1) {
+      return <AccessibleImage className="extension" />;
+    }
+
+    // Threads level
+    if (depth === 0 && projectRoot === "") {
+      const thread = threads.find(thrd => thrd.actor == item.name);
+
+      if (thread) {
+        const icon = thread === this.props.mainThread ? "window" : "worker";
+        return (
+          <AccessibleImage
+            className={classnames(icon, {
+              debuggee: debuggeeUrl && debuggeeUrl.includes(item.name)
+            })}
+          />
+        );
+      }
+    }
+
+    if (isDirectory(item)) {
+      // Domain level
+      if (depth === 1) {
+        return <AccessibleImage className="globe-small" />;
+      }
+      return <AccessibleImage className="folder" />;
+    }
+
+    if (hasPrettySource) {
+      return <AccessibleImage className="prettyPrint" />;
+    }
+
+    if (source) {
+      return <SourceIcon source={source} />;
+    }
+
+    return null;
+  }
+
+  renderItemName(depth) {
+    const { item, threads } = this.props;
+
+    if (depth === 0) {
+      const thread = threads.find(({ actor }) => actor == item.name);
+      if (thread) {
+        return isWorker(thread)
+          ? getDisplayName((thread: any))
+          : L10N.getStr("mainThread");
+      }
+    }
 
     switch (item.name) {
       case "ng://":
         return "Angular";
       case "webpack://":
         return "Webpack";
       default:
         return `${unescape(item.name)}`;
@@ -248,20 +284,19 @@ class SourceTreeItem extends Component<P
     return (
       <div
         className={classnames("node", { focused })}
         key={item.path}
         onClick={this.onClick}
         onContextMenu={e => this.onContextMenu(e, item)}
       >
         {this.renderItemArrow()}
-        {this.getIcon(item, depth)}
+        {this.renderIcon(item, depth)}
         <span className="label">
-          {" "}
-          {this.renderItemName()}
+          {this.renderItemName(depth)}
           {query} {suffix}
         </span>
       </div>
     );
   }
 }
 
 function getHasMatchingGeneratedSource(state, source: ?Source) {
@@ -271,16 +306,17 @@ function getHasMatchingGeneratedSource(s
 
   return !!getGeneratedSourceByURL(state, source.url);
 }
 
 const mapStateToProps = (state, props) => {
   const { source } = props;
   return {
     cx: getContext(state),
+    mainThread: getMainThread(state),
     hasMatchingGeneratedSource: getHasMatchingGeneratedSource(state, source),
     hasSiblingOfSameName: getHasSiblingOfSameName(state, source),
     hasPrettySource: source ? checkHasPrettySource(state, source.id) : false
   };
 };
 
 export default connect(
   mapStateToProps,
--- a/devtools/client/debugger/src/components/PrimaryPanes/index.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/index.js
@@ -122,19 +122,17 @@ class PrimaryPanes extends Component<Pro
           <AccessibleImage className="breadcrumb" />
           <span className="sources-clear-root-label">{rootLabel}</span>
         </button>
       </div>
     );
   }
 
   renderThreadSources() {
-    return this.props.threads.map(({ actor }) => (
-      <SourcesTree thread={actor} key={actor} />
-    ));
+    return <SourcesTree threads={this.props.threads} />;
   }
 
   render() {
     const { selectedTab, projectRoot } = this.props;
     const activeIndex = selectedTab === "sources" ? 0 : 1;
 
     return (
       <Tabs
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTree.spec.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTree.spec.js
@@ -78,34 +78,33 @@ describe("SourcesTree", () => {
         );
       });
 
       it("updates tree with a new item", async () => {
         const { component, props } = render();
         const newSource = createMockSource(
           "server1.conn13.child1/43",
           "http://mdn.com/four.js",
-          true
+          true,
+          ""
         );
+
         const newThreadSources = {
           ...props.sources.FakeThread,
           "server1.conn13.child1/43": newSource
         };
 
         await component.setProps({
           ...props,
-          sources: {
-            ...props.sources,
-            ...newThreadSources
-          }
+          sources: newThreadSources
         });
 
         expect(
-          component.state("uncollapsedTree").contents[0].contents
-        ).toHaveLength(6);
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
       });
 
       it("updates sources if sources are emptied", async () => {
         const { component, props, defaultState } = render();
 
         expect(defaultState.uncollapsedTree.contents).toHaveLength(1);
 
         await component.setProps({
@@ -115,71 +114,126 @@ describe("SourcesTree", () => {
         });
 
         expect(component.state("uncollapsedTree").contents).toHaveLength(0);
       });
 
       it("recreates tree if projectRoot is changed", async () => {
         const { component, props, defaultState } = render();
         const sources = {
-          "server1.conn13.child1/41": createMockSource(
-            "server1.conn13.child1/41",
-            "http://mozilla.com/three.js"
-          )
+          FakeThread: {
+            "server1.conn13.child1/41": createMockSource(
+              "server1.conn13.child1/41",
+              "http://mozilla.com/three.js"
+            )
+          }
         };
 
-        expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(
-          5
-        );
+        expect(
+          defaultState.uncollapsedTree.contents[0].contents[0].contents
+        ).toHaveLength(5);
 
         await component.setProps({
           ...props,
           sources,
           projectRoot: "mozilla"
         });
 
         expect(
           component.state("uncollapsedTree").contents[0].contents
         ).toHaveLength(1);
       });
 
-      it("recreates tree if debugeeUrl is changed", async () => {
+      it("recreates tree if debuggeeUrl is changed", async () => {
         const { component, props, defaultState } = render();
         const sources = {
-          "server1.conn13.child1/41": createMockSource(
-            "server1.conn13.child1/41",
-            "http://mdn.com/three.js"
-          )
+          FakeThread: {
+            "server1.conn13.child1/41": createMockSource(
+              "server1.conn13.child1/41",
+              "http://mdn.com/three.js",
+              true,
+              ""
+            )
+          }
         };
 
-        expect(defaultState.uncollapsedTree.contents[0].contents).toHaveLength(
-          5
-        );
+        expect(
+          defaultState.uncollapsedTree.contents[0].contents[0].contents
+        ).toHaveLength(5);
 
         await component.setProps({
           ...props,
           debuggeeUrl: "mozilla",
           sources
         });
 
         expect(
           component.state("uncollapsedTree").contents[0].contents
         ).toHaveLength(1);
       });
     });
 
+    describe("updates threads", () => {
+      it("adds sources to the correct thread", async () => {
+        const { component, props } = render();
+        const newSource = createMockSource(
+          "server1.conn13.child1/43",
+          "http://mdn.com/four.js",
+          true,
+          ""
+        );
+
+        const newThreadSources = {
+          FakeThread1: {
+            "server1.conn13.child1/43": newSource
+          }
+        };
+
+        expect(component.state("uncollapsedTree").contents[0].name).toEqual(
+          "FakeThread"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
+        expect(component.state("uncollapsedTree").contents[1]).toEqual(
+          undefined
+        );
+
+        await component.setProps({
+          ...props,
+          sources: {
+            ...props.sources,
+            ...newThreadSources
+          }
+        });
+
+        expect(component.state("uncollapsedTree").contents[0].name).toEqual(
+          "FakeThread"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[0].contents[0].contents
+        ).toHaveLength(5);
+
+        expect(component.state("uncollapsedTree").contents[1].name).toEqual(
+          "FakeThread1"
+        );
+        expect(
+          component.state("uncollapsedTree").contents[1].contents[0].contents
+        ).toHaveLength(1);
+      });
+    });
+
     describe("updates highlighted items", () => {
       it("updates highlightItems if selectedSource changes", async () => {
         const { component, props } = render();
         const mockSource = createMockSource(
           "server1.conn13.child1/41",
           "http://mdn.com/three.js",
           false,
-          null,
-          "FakeThread"
+          null
         );
         await component.setProps({
           ...props,
           selectedSource: mockSource
         });
         expect(component).toMatchSnapshot();
       });
     });
@@ -242,162 +296,146 @@ describe("SourcesTree", () => {
 
     it("onExpand", async () => {
       const { component, props } = render();
       const expandedState = ["x", "y"];
       await component
         .find("ManagedTree")
         .props()
         .onExpand({}, expandedState);
-      expect(props.setExpandedState).toHaveBeenCalledWith(
-        "FakeThread",
-        expandedState
-      );
+      expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
     });
 
     it("onCollapse", async () => {
       const { component, props } = render();
       const expandedState = ["y", "z"];
       await component
         .find("ManagedTree")
         .props()
         .onCollapse({}, expandedState);
-      expect(props.setExpandedState).toHaveBeenCalledWith(
-        "FakeThread",
-        expandedState
-      );
+      expect(props.setExpandedState).toHaveBeenCalledWith(expandedState);
     });
 
     it("getParent", async () => {
       const { component } = render();
       const item = component.state("sourceTree").contents[0].contents[0];
       const parent = component
         .find("ManagedTree")
         .props()
         .getParent(item);
 
-      expect(parent.path).toEqual("mdn.com");
-      expect(parent.contents).toHaveLength(5);
+      expect(parent.path).toEqual("FakeThread");
+      expect(parent.contents[0].contents).toHaveLength(5);
     });
   });
 
   describe("getPath", () => {
     it("should return path for item", async () => {
       const { instance } = render();
       const path = instance.getPath(createMockItem());
-      expect(path).toEqual(
-        "http://mdn.com/one.js/one.js/server1.conn13.child1/39/"
-      );
-    });
-
-    it("should return path for blackboxedboxed item", async () => {
-      const item = createMockItem(
-        "http://mdn.com/blackboxed.js",
-        "blackboxed.js",
-        { id: "server1.conn13.child1/59" }
-      );
-
-      const sources = {
-        "server1.conn13.child1/59": createMockSource(
-          "server1.conn13.child1/59",
-          "http://mdn.com/blackboxed.js",
-          true
-        )
-      };
-
-      const { instance } = render({ sources });
-      const path = instance.getPath(item);
-      expect(path).toEqual(
-        "http://mdn.com/blackboxed.js/blackboxed.js/server1.conn13.child1/59/:blackboxed"
-      );
+      expect(path).toEqual("http://mdn.com/one.js/server1.conn13.child1/39/");
     });
 
     it("should return path for generated item", async () => {
       const { instance } = render();
       const pathOriginal = instance.getPath(
         createMockItem("http://mdn.com/four.js", "four.js", {
           id: "server1.conn13.child1/42/originalSource-sha"
         })
       );
       expect(pathOriginal).toEqual(
-        "http://mdn.com/four.js/four.js/server1.conn13.child1/42/originalSource-sha/"
+        "http://mdn.com/four.js/server1.conn13.child1/42/originalSource-sha/"
       );
 
       const pathGenerated = instance.getPath(
         createMockItem("http://mdn.com/four.js", "four.js", {
           id: "server1.conn13.child1/42"
         })
       );
       expect(pathGenerated).toEqual(
-        "http://mdn.com/four.js/four.js/server1.conn13.child1/42/"
+        "http://mdn.com/four.js/server1.conn13.child1/42/"
       );
     });
   });
 });
 
 function generateDefaults(overrides: Object) {
   const defaultSources = {
-    "server1.conn13.child1/39": createMockSource(
-      "server1.conn13.child1/39",
-      "http://mdn.com/one.js"
-    ),
-    "server1.conn13.child1/40": createMockSource(
-      "server1.conn13.child1/40",
-      "http://mdn.com/two.js"
-    ),
-    "server1.conn13.child1/41": createMockSource(
-      "server1.conn13.child1/41",
-      "http://mdn.com/three.js"
-    ),
-    "server1.conn13.child1/42/originalSource-sha": createMockSource(
-      "server1.conn13.child1/42/originalSource-sha",
-      "http://mdn.com/four.js"
-    ),
-    "server1.conn13.child1/42": createMockSource(
-      "server1.conn13.child1/42",
-      "http://mdn.com/four.js",
-      false,
-      "data:application/json?charset=utf?dsffewrsf"
-    )
+    FakeThread: {
+      "server1.conn13.child1/39": createMockSource(
+        "server1.conn13.child1/39",
+        "http://mdn.com/one.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/40": createMockSource(
+        "server1.conn13.child1/40",
+        "http://mdn.com/two.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/41": createMockSource(
+        "server1.conn13.child1/41",
+        "http://mdn.com/three.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/42/originalSource-sha": createMockSource(
+        "server1.conn13.child1/42/originalSource-sha",
+        "http://mdn.com/four.js",
+        false,
+        null
+      ),
+      "server1.conn13.child1/42": createMockSource(
+        "server1.conn13.child1/42",
+        "http://mdn.com/four.js",
+        false,
+        "data:application/json?charset=utf?dsffewrsf"
+      )
+    }
   };
+
   return {
     cx: mockcx,
-    thread: "FakeThread",
     autoExpandAll: true,
     selectSource: jest.fn(),
     setExpandedState: jest.fn(),
     sources: defaultSources,
     debuggeeUrl: "http://mdn.com",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
     focusItem: jest.fn(),
     projectRoot: "",
+    threads: [
+      {
+        name: "FakeThread",
+        actor: "FakeThread"
+      },
+      {
+        name: "FakeThread1",
+        actor: "FakeThread1"
+      }
+    ],
     ...overrides
   };
 }
 
 function render(overrides = {}) {
   const props = generateDefaults(overrides);
   // $FlowIgnore
   const component = shallow(<SourcesTree.WrappedComponent {...props} />);
   const defaultState = component.state();
   const instance = component.instance();
 
   instance.shouldComponentUpdate = () => true;
 
   return { component, props, defaultState, instance };
 }
 
-function createMockSource(
-  id,
-  url,
-  isBlackBoxed = false,
-  sourceMapURL = null,
-  thread = ""
-) {
+function createMockSource(id, url, isBlackBoxed = false, sourceMapURL = null) {
   return {
     ...makeMockSource(url, id),
     isBlackBoxed,
     sourceMapURL
   };
 }
 
 function createMockDirectory(path = "folder/", name = "folder", contents = []) {
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
@@ -370,31 +370,36 @@ function generateDefaults(overrides) {
     debuggeeUrl: "http://mdn.com",
     projectRoot: "",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
     toggleBlackBox: jest.fn(),
     selectItem: jest.fn(),
     focusItem: jest.fn(),
     setExpanded: jest.fn(),
+    threads: [{ name: "Main Thread" }],
     ...overrides
   };
 }
 
 function render(overrides = {}) {
   const props = generateDefaults(overrides);
   // $FlowIgnore
   const component = shallow(<SourcesTreeItem.WrappedComponent {...props} />);
   const defaultState = component.state();
   const instance = component.instance();
 
   return { component, props, defaultState, instance };
 }
 
-function createMockDirectory(path = "folder/", name = "folder", contents = []) {
+function createMockDirectory(
+  path = "domain/subfolder",
+  name = "folder",
+  contents = []
+) {
   return {
     type: "directory",
     name,
     path,
     contents
   };
 }
 
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/PrimaryPanes.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/PrimaryPanes.spec.js.snap
@@ -52,16 +52,19 @@ exports[`PrimaryPanes with custom root r
           />
           <span
             className="sources-clear-root-label"
           >
             mdn.com
           </span>
         </button>
       </div>
+      <Connect(SourcesTree)
+        threads={Array []}
+      />
     </div>
     <Connect(Outline)
       alphabetizeOutline={false}
       onAlphabetizeClick={[Function]}
     />
   </TabPanels>
 </Tabs>
 `;
@@ -118,16 +121,19 @@ exports[`PrimaryPanes with custom root r
           />
           <span
             className="sources-clear-root-label"
           >
             custom
           </span>
         </button>
       </div>
+      <Connect(SourcesTree)
+        threads={Array []}
+      />
     </div>
     <Connect(Outline)
       alphabetizeOutline={false}
       onAlphabetizeClick={[Function]}
     />
   </TabPanels>
 </Tabs>
 `;
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
@@ -1,35 +1,22 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`SourcesTree After changing expanded nodes Shows the tree with four.js, five.js and six.js expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
-      autoExpandDepth={0}
+      autoExpandDepth={1}
       expanded={
         Array [
           "four.js",
           "five.js",
           "six.js",
         ]
       }
       getChildren={[Function]}
@@ -46,60 +33,34 @@ exports[`SourcesTree After changing expa
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree Should show a 'No Sources' message if there are no sources 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="no-sources-message"
     key="empty"
   >
     This page has no sources.
   </div>
 </div>
 `;
 
 exports[`SourcesTree Should show the tree with nothing expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
@@ -115,39 +76,26 @@ exports[`SourcesTree Should show the tre
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree When loading initial source Shows the tree with one.js, two.js and three.js expanded 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
-      autoExpandDepth={0}
+      autoExpandDepth={1}
       expanded={
         Array [
           "one.js",
           "two.js",
           "three.js",
         ]
       }
       getChildren={[Function]}
@@ -164,33 +112,20 @@ exports[`SourcesTree When loading initia
       renderItem={[Function]}
     />
   </div>
 </div>
 `;
 
 exports[`SourcesTree on receiving new props updates highlighted items updates highlightItems if selectedSource changes 1`] = `
 <div
-  className="sources-pane thread"
+  className="sources-pane"
   key="pane"
 >
   <div
-    className="node thread-header"
-    key="thread-header"
-  >
-    <AccessibleImage
-      className="file"
-    />
-    <span
-      className="label"
-    >
-      Main Thread
-    </span>
-  </div>
-  <div
     className="sources-list"
     key="tree"
   >
     <ManagedTree
       autoExpandAll={false}
       autoExpandDepth={1}
       getChildren={[Function]}
       getParent={[Function]}
@@ -207,17 +142,17 @@ exports[`SourcesTree on receiving new pr
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/three.js",
               "sourceMapURL": null,
               "url": "http://mdn.com/three.js",
             },
             "name": "three.js",
-            "path": "mdn.com/three.js",
+            "path": "FakeThread/mdn.com/three.js",
             "type": "source",
           },
           Object {
             "contents": Array [
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/42",
                   "introductionType": undefined,
@@ -226,90 +161,189 @@ exports[`SourcesTree on receiving new pr
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/four.js",
                   "sourceMapURL": "data:application/json?charset=utf?dsffewrsf",
                   "url": "http://mdn.com/four.js",
                 },
                 "name": "four.js",
-                "path": "mdn.com/four.js",
+                "path": "FakeThread/mdn.com/four.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/42/originalSource-sha",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/four.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/four.js",
                 },
                 "name": "four.js",
-                "path": "mdn.com/four.js",
+                "path": "FakeThread/mdn.com/four.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/39",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/one.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/one.js",
                 },
                 "name": "one.js",
-                "path": "mdn.com/one.js",
+                "path": "FakeThread/mdn.com/one.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/41",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/three.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/three.js",
                 },
                 "name": "three.js",
-                "path": "mdn.com/three.js",
+                "path": "FakeThread/mdn.com/three.js",
                 "type": "source",
               },
               Object {
                 "contents": Object {
                   "id": "server1.conn13.child1/40",
                   "introductionType": undefined,
                   "introductionUrl": null,
                   "isBlackBoxed": false,
                   "isExtension": false,
                   "isPrettyPrinted": false,
                   "isWasm": false,
                   "relativeUrl": "http://mdn.com/two.js",
                   "sourceMapURL": null,
                   "url": "http://mdn.com/two.js",
                 },
                 "name": "two.js",
-                "path": "mdn.com/two.js",
+                "path": "FakeThread/mdn.com/two.js",
                 "type": "source",
               },
             ],
             "name": "mdn.com",
-            "path": "mdn.com",
+            "path": "FakeThread/mdn.com",
+            "type": "directory",
+          },
+          Object {
+            "contents": Array [
+              Object {
+                "contents": Array [
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/42",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/four.js",
+                      "sourceMapURL": "data:application/json?charset=utf?dsffewrsf",
+                      "url": "http://mdn.com/four.js",
+                    },
+                    "name": "four.js",
+                    "path": "FakeThread/mdn.com/four.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/42/originalSource-sha",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/four.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/four.js",
+                    },
+                    "name": "four.js",
+                    "path": "FakeThread/mdn.com/four.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/39",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/one.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/one.js",
+                    },
+                    "name": "one.js",
+                    "path": "FakeThread/mdn.com/one.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/41",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/three.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/three.js",
+                    },
+                    "name": "three.js",
+                    "path": "FakeThread/mdn.com/three.js",
+                    "type": "source",
+                  },
+                  Object {
+                    "contents": Object {
+                      "id": "server1.conn13.child1/40",
+                      "introductionType": undefined,
+                      "introductionUrl": null,
+                      "isBlackBoxed": false,
+                      "isExtension": false,
+                      "isPrettyPrinted": false,
+                      "isWasm": false,
+                      "relativeUrl": "http://mdn.com/two.js",
+                      "sourceMapURL": null,
+                      "url": "http://mdn.com/two.js",
+                    },
+                    "name": "two.js",
+                    "path": "FakeThread/mdn.com/two.js",
+                    "type": "source",
+                  },
+                ],
+                "name": "mdn.com",
+                "path": "FakeThread/mdn.com",
+                "type": "directory",
+              },
+            ],
+            "name": "FakeThread",
+            "path": "FakeThread",
             "type": "directory",
           },
         ]
       }
       itemHeight={21}
       key="full"
       onActivate={[Function]}
       onCollapse={[Function]}
--- a/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
+++ b/devtools/client/debugger/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
@@ -70,16 +70,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -117,16 +122,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -193,16 +205,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show (mapped) for duplicate source items 1`] = `
 Object {
   "component": <div
@@ -280,16 +297,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -329,16 +351,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -412,34 +441,51 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item 1`] = `
 Object {
   "component": <div
     className="node"
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <span
       className="img no-arrow"
     />
-    <AccessibleImage
-      className="globe-small"
+    <Connect(SourceIcon)
+      source={
+        Object {
+          "id": "server1.conn13.child1/39",
+          "introductionType": undefined,
+          "introductionUrl": null,
+          "isBlackBoxed": false,
+          "isExtension": false,
+          "isPrettyPrinted": false,
+          "isWasm": false,
+          "relativeUrl": "http://mdn.com/one.js",
+          "url": "http://mdn.com/one.js",
+        }
+      }
     />
     <span
       className="label"
     >
        
       root
        
     </span>
@@ -482,16 +528,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -531,31 +582,50 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <span
             className="img no-arrow"
           />
-          <AccessibleImage
-            className="globe-small"
+          <Connect(SourceIcon)
+            source={
+              Object {
+                "id": "server1.conn13.child1/39",
+                "introductionType": undefined,
+                "introductionUrl": null,
+                "isBlackBoxed": false,
+                "isExtension": false,
+                "isPrettyPrinted": false,
+                "isWasm": false,
+                "relativeUrl": "http://mdn.com/one.js",
+                "url": "http://mdn.com/one.js",
+              }
+            }
           />
           <span
             className="label"
           >
              
             root
              
           </span>
@@ -597,16 +667,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item as debuggee 1`] = `
 Object {
   "component": <div
@@ -614,17 +689,17 @@ Object {
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="globe-small debuggee"
+      className="folder"
     />
     <span
       className="label"
     >
        
       http://mdn.com
        
     </span>
@@ -657,16 +732,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -696,31 +776,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="globe-small debuggee"
+            className="folder"
           />
           <span
             className="label"
           >
              
             http://mdn.com
              
           </span>
@@ -752,16 +839,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show domain item as debuggee with focus and arrow 1`] = `
 Object {
   "component": <div
@@ -769,17 +861,17 @@ Object {
     key="root"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="globe-small debuggee"
+      className="folder"
     />
     <span
       className="label"
     >
        
       http://mdn.com
        
     </span>
@@ -813,16 +905,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -853,31 +950,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="globe-small debuggee"
+            className="folder"
           />
           <span
             className="label"
           >
              
             http://mdn.com
              
           </span>
@@ -910,34 +1014,39 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show focused item for folder with expanded arrow 1`] = `
 Object {
   "component": <div
     className="node focused"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow expanded"
     />
     <AccessibleImage
-      className="folder"
+      className="globe-small"
     />
     <span
       className="label"
     >
        
       folder
        
     </span>
@@ -953,24 +1062,29 @@ Object {
       "debuggeeUrl": "http://mdn.com",
       "depth": 1,
       "expanded": true,
       "focusItem": [MockFunction],
       "focused": true,
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -980,40 +1094,47 @@ Object {
           depth={1}
           expanded={true}
           focusItem={[MockFunction]}
           focused={true}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow expanded"
           />
           <AccessibleImage
-            className="folder"
+            className="globe-small"
           />
           <span
             className="label"
           >
              
             folder
              
           </span>
@@ -1028,24 +1149,29 @@ Object {
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": true,
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for angular item 1`] = `
 Object {
   "component": <div
@@ -1095,16 +1221,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1133,16 +1264,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1188,26 +1326,31 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for folder with arrow 1`] = `
 Object {
   "component": <div
     className="node"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
       className="folder"
@@ -1229,24 +1372,29 @@ Object {
     "props": Object {
       "clearProjectDirectoryRoot": [MockFunction],
       "debuggeeUrl": "http://mdn.com",
       "expanded": false,
       "focusItem": [MockFunction],
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1254,25 +1402,32 @@ Object {
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
           expanded={false}
           focusItem={[MockFunction]}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1300,42 +1455,47 @@ Object {
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for folder with expanded arrow 1`] = `
 Object {
   "component": <div
     className="node"
-    key="folder/"
+    key="domain/subfolder"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow expanded"
     />
     <AccessibleImage
-      className="folder"
+      className="globe-small"
     />
     <span
       className="label"
     >
        
       folder
        
     </span>
@@ -1351,24 +1511,29 @@ Object {
       "debuggeeUrl": "http://mdn.com",
       "depth": 1,
       "expanded": true,
       "focusItem": [MockFunction],
       "focused": false,
       "item": Object {
         "contents": Array [],
         "name": "folder",
-        "path": "folder/",
+        "path": "domain/subfolder",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
       "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1378,40 +1543,47 @@ Object {
           depth={1}
           expanded={true}
           focusItem={[MockFunction]}
           focused={false}
           item={
             Object {
               "contents": Array [],
               "name": "folder",
-              "path": "folder/",
+              "path": "domain/subfolder",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
           setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow expanded"
           />
           <AccessibleImage
-            className="folder"
+            className="globe-small"
           />
           <span
             className="label"
           >
              
             folder
              
           </span>
@@ -1426,24 +1598,29 @@ Object {
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": false,
     "item": Object {
       "contents": Array [],
       "name": "folder",
-      "path": "folder/",
+      "path": "domain/subfolder",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
     "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for moz-extension item 1`] = `
 Object {
   "component": <div
@@ -1451,17 +1628,17 @@ Object {
     key="moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856"
     onClick={[Function]}
     onContextMenu={[Function]}
   >
     <AccessibleImage
       className="arrow"
     />
     <AccessibleImage
-      className="extension"
+      className="folder"
     />
     <span
       className="label"
     >
        
       moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856
        
     </span>
@@ -1494,16 +1671,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1533,31 +1715,38 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
           onContextMenu={[Function]}
         >
           <AccessibleImage
             className="arrow"
           />
           <AccessibleImage
-            className="extension"
+            className="folder"
           />
           <span
             className="label"
           >
              
             moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856
              
           </span>
@@ -1589,16 +1778,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for webpack item 1`] = `
 Object {
   "component": <div
@@ -1648,16 +1842,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1686,16 +1885,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1741,16 +1947,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show source item with source icon 1`] = `
 Object {
   "component": <div
@@ -1822,16 +2033,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -1870,16 +2086,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -1947,16 +2170,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show source item with source icon with focus 1`] = `
 Object {
   "component": <div
@@ -2029,16 +2257,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -2078,16 +2311,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
           onClick={[Function]}
@@ -2156,16 +2396,21 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should unescape escaped source URLs 1`] = `
 Object {
   "component": <div
@@ -2237,16 +2482,21 @@ Object {
         "introductionUrl": null,
         "isBlackBoxed": false,
         "isExtension": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "relativeUrl": "http://mdn.com/one.js",
         "url": "http://mdn.com/one.js",
       },
+      "threads": Array [
+        Object {
+          "name": "Main Thread",
+        },
+      ],
       "toggleBlackBox": [MockFunction],
     },
     "refs": Object {},
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
@@ -2285,16 +2535,23 @@ Object {
               "isBlackBoxed": false,
               "isExtension": false,
               "isPrettyPrinted": false,
               "isWasm": false,
               "relativeUrl": "http://mdn.com/one.js",
               "url": "http://mdn.com/one.js",
             }
           }
+          threads={
+            Array [
+              Object {
+                "name": "Main Thread",
+              },
+            ]
+          }
           toggleBlackBox={[MockFunction]}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
           onClick={[Function]}
@@ -2362,12 +2619,17 @@ Object {
       "introductionUrl": null,
       "isBlackBoxed": false,
       "isExtension": false,
       "isPrettyPrinted": false,
       "isWasm": false,
       "relativeUrl": "http://mdn.com/one.js",
       "url": "http://mdn.com/one.js",
     },
+    "threads": Array [
+      Object {
+        "name": "Main Thread",
+      },
+    ],
     "toggleBlackBox": [MockFunction],
   },
 }
 `;
--- a/devtools/client/debugger/src/components/QuickOpenModal.js
+++ b/devtools/client/debugger/src/components/QuickOpenModal.js
@@ -425,21 +425,22 @@ export class QuickOpenModal extends Comp
       </Modal>
     );
   }
 }
 
 /* istanbul ignore next: ignoring testing of redux connection stuff */
 function mapStateToProps(state) {
   const selectedSource = getSelectedSource(state);
+  const displayedSources = getDisplayedSourcesList(state);
 
   return {
     cx: getContext(state),
     enabled: getQuickOpenEnabled(state),
-    sources: formatSources(getDisplayedSourcesList(state), getTabs(state)),
+    sources: formatSources(displayedSources, getTabs(state)),
     selectedSource,
     selectedContentLoaded: selectedSource
       ? !!getSourceContent(state, selectedSource.id)
       : undefined,
     symbols: formatSymbols(getSymbols(state, selectedSource)),
     symbolsLoading: isSymbolsLoading(state, selectedSource),
     query: getQuickOpenQuery(state),
     searchType: getQuickOpenType(state),
--- a/devtools/client/debugger/src/components/SecondaryPanes/Worker.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Worker.js
@@ -39,17 +39,17 @@ export class Worker extends Component<Pr
       <div
         className={classnames("worker", {
           selected: thread.actor == currentThread
         })}
         key={thread.actor}
         onClick={this.onSelectThread}
       >
         <div className="icon">
-          <AccessibleImage className={worker ? "worker" : "file"} />
+          <AccessibleImage className={worker ? "worker" : "window"} />
         </div>
         <div className="label">{label}</div>
         {isPaused ? (
           <div className="pause-badge">
             <AccessibleImage className="pause" />
           </div>
         ) : null}
       </div>
--- a/devtools/client/debugger/src/components/shared/AccessibleImage.css
+++ b/devtools/client/debugger/src/components/shared/AccessibleImage.css
@@ -75,16 +75,21 @@ html[dir="rtl"] .img.arrow {
   mask-image: url(resource://devtools/client/debugger/images/globe.svg);
 }
 
 .img.globe-small {
   mask-image: url(resource://devtools/client/debugger/images/globe-small.svg);
   mask-size: 12px 12px;
 }
 
+.img.window {
+  mask-image: url(resource://devtools/client/debugger/images/window.svg);
+  mask-size: 12px 12px;
+}
+
 .img.file {
   mask-image: url(resource://devtools/client/debugger/images/file-small.svg);
   mask-size: 12px 12px;
 }
 
 .img.folder {
   mask-image: url(resource://devtools/client/debugger/images/folder.svg);
 }
--- a/devtools/client/debugger/src/reducers/debuggee.js
+++ b/devtools/client/debugger/src/reducers/debuggee.js
@@ -5,74 +5,91 @@
 // @flow
 
 /**
  * Debuggee reducer
  * @module reducers/debuggee
  */
 
 import { sortBy } from "lodash";
+import { createSelector } from "reselect";
+
 import { getDisplayName } from "../utils/workers";
 
-import type { MainThread, WorkerList } from "../types";
+import type { Selector } from "./types";
+import type { MainThread, WorkerList, Thread } from "../types";
 import type { Action } from "../actions/types";
 
 export type DebuggeeState = {
   workers: WorkerList,
   mainThread: MainThread
 };
 
 export function initialDebuggeeState(): DebuggeeState {
-  return { workers: [], mainThread: { actor: "", url: "", type: -1 } };
+  return {
+    workers: [],
+    mainThread: { actor: "", url: "", type: -1, name: "" }
+  };
 }
 
 export default function debuggee(
   state: DebuggeeState = initialDebuggeeState(),
   action: Action
 ): DebuggeeState {
   switch (action.type) {
     case "CONNECT":
       return {
         ...state,
-        mainThread: action.mainThread
+        mainThread: { ...action.mainThread, name: L10N.getStr("mainThread") }
       };
     case "INSERT_WORKERS":
-      return {
-        ...state,
-        workers: [...state.workers, ...action.workers]
-      };
+      return insertWorkers(state, action.workers);
     case "REMOVE_WORKERS":
       const { workers } = action;
       return {
         ...state,
         workers: state.workers.filter(w => !workers.includes(w.actor))
       };
     case "NAVIGATE":
       return {
         ...initialDebuggeeState(),
         mainThread: action.mainThread
       };
     default:
       return state;
   }
 }
 
+function insertWorkers(state, workers) {
+  const formatedWorkers = workers.map(worker => ({
+    ...worker,
+    name: getDisplayName(worker)
+  }));
+
+  return {
+    ...state,
+    workers: [...state.workers, ...formatedWorkers]
+  };
+}
+
 export const getWorkers = (state: OuterState) => state.debuggee.workers;
 
 export const getWorkerCount = (state: OuterState) => getWorkers(state).length;
 
 export function getWorkerByThread(state: OuterState, thread: string) {
   return getWorkers(state).find(worker => worker.actor == thread);
 }
 
 export function getMainThread(state: OuterState): MainThread {
   return state.debuggee.mainThread;
 }
 
 export function getDebuggeeUrl(state: OuterState): string {
   return getMainThread(state).url;
 }
 
-export function getThreads(state: OuterState) {
-  return [getMainThread(state), ...sortBy(getWorkers(state), getDisplayName)];
-}
+export const getThreads: Selector<Thread[]> = createSelector(
+  getMainThread,
+  getWorkers,
+  (mainThread, workers) => [mainThread, ...sortBy(workers, getDisplayName)]
+);
 
 type OuterState = { debuggee: DebuggeeState };
--- a/devtools/client/debugger/src/reducers/source-tree.js
+++ b/devtools/client/debugger/src/reducers/source-tree.js
@@ -4,46 +4,55 @@
 
 // @flow
 
 /**
  * Source tree reducer
  * @module reducers/source-tree
  */
 
-import type { SourceTreeAction } from "../actions/types";
+import type { SourceTreeAction, FocusItem } from "../actions/types";
 
 export type SourceTreeState = {
-  expanded: { [string]: Set<string> }
+  expanded: Set<string>,
+  focusedItem: ?FocusItem
 };
 
 export function InitialState(): SourceTreeState {
   return {
-    expanded: {}
+    expanded: new Set(),
+    focusedItem: null
   };
 }
 
 export default function update(
   state: SourceTreeState = InitialState(),
   action: SourceTreeAction
 ): SourceTreeState {
   switch (action.type) {
     case "SET_EXPANDED_STATE":
       return updateExpanded(state, action);
+
+    case "SET_FOCUSED_SOURCE_ITEM":
+      return { ...state, focusedItem: action.item };
   }
 
   return state;
 }
 
 function updateExpanded(state, action) {
   return {
     ...state,
-    expanded: { ...state.expanded, [action.thread]: new Set(action.expanded) }
+    expanded: new Set(action.expanded)
   };
 }
 
 type OuterState = {
   sourceTree: SourceTreeState
 };
 
-export function getExpandedState(state: OuterState, thread: string) {
-  return state.sourceTree.expanded[thread];
+export function getExpandedState(state: OuterState) {
+  return state.sourceTree.expanded;
 }
+
+export function getFocusedSourceItem(state: OuterState): ?FocusItem {
+  return state.sourceTree.focusedItem;
+}
--- a/devtools/client/debugger/src/reducers/sources.js
+++ b/devtools/client/debugger/src/reducers/sources.js
@@ -63,18 +63,19 @@ import type { PendingSelectedLocation, S
 import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
 import type { LoadSourceAction } from "../actions/types/SourceAction";
 import { uniq } from "lodash";
 
 export type SourcesMap = { [SourceId]: Source };
 type SourcesContentMap = {
   [SourceId]: AsyncValue<SourceContent> | null
 };
+export type SourcesMapByThread = { [ThreadId]: SourcesMap };
+
 export type BreakpointPositionsMap = { [SourceId]: BreakpointPositions };
-export type SourcesMapByThread = { [ThreadId]: SourcesMap };
 type SourceActorMap = { [SourceId]: Array<SourceActorId> };
 
 type UrlsMap = { [string]: SourceId[] };
 type PlainUrlsMap = { [string]: string[] };
 
 type SourceResource = Resource<{
   ...Source
 }>;
@@ -847,19 +848,20 @@ const getDisplayedSourceIDs: GetDisplaye
           sourceIDsByThread[thread] = new Set();
         }
         sourceIDsByThread[thread].add(sourceId);
       }
     }
     return sourceIDsByThread;
   }
 );
+
 type GetDisplayedSourcesSelector = (
   OuterState & SourceActorOuterState
-) => { [ThreadId]: { [SourceId]: Source } };
+) => SourcesMapByThread;
 export const getDisplayedSources: GetDisplayedSourcesSelector = createSelector(
   state => state.sources.sources,
   getDisplayedSourceIDs,
   (sources, idsByThread) => {
     const result = {};
 
     for (const thread of Object.keys(idsByThread)) {
       for (const id of idsByThread[thread]) {
@@ -869,27 +871,16 @@ export const getDisplayedSources: GetDis
         result[thread][id] = getResource(sources, id);
       }
     }
 
     return result;
   }
 );
 
-export function getDisplayedSourcesForThread(
-  state: OuterState & SourceActorOuterState,
-  thread: string
-): SourcesMap {
-  return getDisplayedSources(state)[thread] || {};
-}
-
-export function getFocusedSourceItem(state: OuterState): ?FocusItem {
-  return state.sources.focusedItem;
-}
-
 export function getSourceActorsForSource(
   state: OuterState & SourceActorOuterState,
   id: SourceId
 ): Array<SourceActor> {
   const actors = state.sources.actors[id];
   if (!actors) {
     return [];
   }
--- a/devtools/client/debugger/src/reducers/tests/sources.spec.js
+++ b/devtools/client/debugger/src/reducers/tests/sources.spec.js
@@ -76,53 +76,49 @@ describe("sources reducer", () => {
 describe("sources selectors", () => {
   it("should return all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = true;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         cx: mockcx,
-        // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       }),
       sourceActors: undefined
     };
     const insertAction = {
       type: "INSERT_SOURCE_ACTORS",
       items: mockSourceActors
     };
     state = {
       sources: update(state.sources, insertAction),
       sourceActors: updateSourceActors(state.sourceActors, insertAction)
     };
-    const selectedDisplayedSources = getDisplayedSources(state);
-    const threadSources = selectedDisplayedSources.foo;
-    expect(Object.values(threadSources)).toHaveLength(3);
+    const threadSources = getDisplayedSources(state);
+    expect(Object.values(threadSources.foo)).toHaveLength(3);
   });
 
   it("should omit all extensions when chrome preference enabled", () => {
     prefs.chromeAndExtenstionsEnabled = false;
     let state = initialSourcesState();
     state = {
       sources: update(state, {
         type: "ADD_SOURCES",
         cx: mockcx,
-        // coercing to a Source for the purpose of this test
         sources: ((mockedSources: any): Source[])
       }),
       sourceActors: undefined
     };
 
     const insertAction = {
       type: "INSERT_SOURCE_ACTORS",
       items: mockSourceActors
     };
 
     state = {
       sources: update(state.sources, insertAction),
       sourceActors: updateSourceActors(state.sourceActors, insertAction)
     };
-    const selectedDisplayedSources = getDisplayedSources(state);
-    const threadSources = selectedDisplayedSources.foo;
-    expect(Object.values(threadSources)).toHaveLength(1);
+    const threadSources = getDisplayedSources(state);
+    expect(Object.values(threadSources.foo)).toHaveLength(1);
   });
 });
--- a/devtools/client/debugger/src/types.js
+++ b/devtools/client/debugger/src/types.js
@@ -442,23 +442,25 @@ export type Scope = {|
     parameterNames: string[]
   },
   type: string
 |};
 
 export type MainThread = {
   +actor: ThreadId,
   +url: string,
-  +type: number
+  +type: number,
+  +name: string
 };
 
 export type Worker = {
   +actor: ThreadId,
   +url: string,
-  +type: number
+  +type: number,
+  +name: string
 };
 
 export type Thread = MainThread & Worker;
 export type ThreadList = Array<Thread>;
 export type WorkerList = Array<Worker>;
 
 export type Cancellable = {
   cancel: () => void
--- a/devtools/client/debugger/src/utils/source.js
+++ b/devtools/client/debugger/src/utils/source.js
@@ -486,15 +486,15 @@ export function getSourceQueryString(sou
   if (!source) {
     return;
   }
 
   return parseURL(getRawSourceURL(source.url)).search;
 }
 
 export function isUrlExtension(url: string) {
-  return /^(chrome|moz)-extension:\//.test(url);
+  return /\/?(chrome|moz)-extension:\//.test(url);
 }
 
 export function getPlainUrl(url: string): string {
   const queryStart = url.indexOf("?");
   return queryStart !== -1 ? url.slice(0, queryStart) : url;
 }
--- a/devtools/client/debugger/src/utils/sources-tree/addToTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/addToTree.js
@@ -85,25 +85,35 @@ function findOrCreateNode(
 /*
  * walk the source tree to the final node for a given url,
  * adding new nodes along the way
  */
 function traverseTree(
   url: ParsedURL,
   tree: TreeDirectory,
   debuggeeHost: ?string,
-  source: Source
+  source: Source,
+  thread: string
 ): TreeNode {
   const parts = url.path.split("/").filter(p => p !== "");
   parts.unshift(url.group);
+  if (thread) {
+    parts.unshift(thread);
+  }
 
   let path = "";
   return parts.reduce((subTree, part, index) => {
-    path = path ? `${path}/${part}` : part;
-    const debuggeeHostIfRoot = index === 0 ? debuggeeHost : null;
+    if (index == 0 && thread) {
+      path = thread;
+    } else {
+      path = `${path}/${part}`;
+    }
+
+    const debuggeeHostIfRoot = index === 1 ? debuggeeHost : null;
+
     return findOrCreateNode(
       parts,
       subTree,
       path,
       part,
       index,
       url,
       debuggeeHostIfRoot,
@@ -160,21 +170,22 @@ function addSourceToNode(
 
 /**
  * @memberof utils/sources-tree
  * @static
  */
 export function addToTree(
   tree: TreeDirectory,
   source: Source,
-  debuggeeHost: ?string
+  debuggeeHost: ?string,
+  thread: string
 ) {
   const url = getURL(source, debuggeeHost);
 
   if (isInvalidUrl(url, source)) {
     return;
   }
 
-  const finalNode = traverseTree(url, tree, debuggeeHost, source);
+  const finalNode = traverseTree(url, tree, debuggeeHost, source, thread);
 
   // $FlowIgnore
   finalNode.contents = addSourceToNode(finalNode, url, source);
 }
--- a/devtools/client/debugger/src/utils/sources-tree/collapseTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/collapseTree.js
@@ -13,18 +13,19 @@ import type { TreeDirectory, TreeNode } 
  */
 function _collapseTree(node: TreeNode, depth: number): TreeNode {
   // Node is a folder.
   if (node.type === "directory") {
     if (!Array.isArray(node.contents)) {
       console.log(`Expected array at: ${node.path}`);
     }
 
-    // Node is not a root/domain node, and only contains 1 item.
-    if (depth > 1 && node.contents.length === 1) {
+    // Node is not a (1) thread and (2) root/domain node,
+    // and only contains 1 item.
+    if (depth > 2 && node.contents.length === 1) {
       const next = node.contents[0];
       // Do not collapse if the next node is a leaf node.
       if (next.type === "directory") {
         if (!Array.isArray(next.contents)) {
           console.log(
             `Expected array at: ${next.name} -- ${
               node.name
             } -- ${JSON.stringify(next.contents)}`
deleted file mode 100644
--- a/devtools/client/debugger/src/utils/sources-tree/createTree.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/* 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 { addToTree } from "./addToTree";
-import { collapseTree } from "./collapseTree";
-import { createDirectoryNode, createParentMap } from "./utils";
-import { getDomain } from "./treeOrder";
-
-import type { SourcesMap } from "../../reducers/types";
-import type { TreeDirectory } from "./types";
-
-type Params = {
-  sources: SourcesMap,
-  debuggeeUrl: string
-};
-
-export function createTree({ sources, debuggeeUrl }: Params) {
-  const uncollapsedTree = createDirectoryNode("root", "", []);
-  const debuggeeHost = getDomain(debuggeeUrl);
-
-  for (const sourceId in sources) {
-    const source = sources[sourceId];
-    addToTree(uncollapsedTree, source, debuggeeHost);
-  }
-
-  const sourceTree = collapseTree((uncollapsedTree: TreeDirectory));
-
-  return {
-    uncollapsedTree,
-    sourceTree,
-    parentMap: createParentMap(sourceTree)
-  };
-}
--- a/devtools/client/debugger/src/utils/sources-tree/index.js
+++ b/devtools/client/debugger/src/utils/sources-tree/index.js
@@ -6,16 +6,15 @@
 
 /**
  * Utils for Sources Tree Component
  * @module utils/sources-tree
  */
 
 export { addToTree } from "./addToTree";
 export { collapseTree } from "./collapseTree";
-export { createTree } from "./createTree";
 export { formatTree } from "./formatTree";
 export { getDirectories } from "./getDirectories";
 export { getFilenameFromPath, getURL } from "./getURL";
-export { sortEntireTree, sortTree } from "./sortTree";
-export { updateTree } from "./updateTree";
+export { sortTree } from "./sortTree";
+export { createTree, updateTree } from "./updateTree";
 
 export * from "./utils";
--- a/devtools/client/debugger/src/utils/sources-tree/moz.build
+++ b/devtools/client/debugger/src/utils/sources-tree/moz.build
@@ -5,17 +5,16 @@
 
 DIRS += [
 
 ]
 
 CompiledModules(
     'addToTree.js',
     'collapseTree.js',
-    'createTree.js',
     'formatTree.js',
     'getDirectories.js',
     'getURL.js',
     'index.js',
     'sortTree.js',
     'treeOrder.js',
     'updateTree.js',
     'utils.js',
--- a/devtools/client/debugger/src/utils/sources-tree/sortTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/sortTree.js
@@ -9,35 +9,16 @@ import { nodeHasChildren, isExactUrlMatc
 import type { TreeDirectory } from "./types";
 
 /**
  * Look at the nodes in the source tree, and determine the index of where to
  * insert a new node. The ordering is index -> folder -> file.
  * @memberof utils/sources-tree
  * @static
  */
-export function sortEntireTree(
-  tree: TreeDirectory,
-  debuggeeUrl: string = ""
-): TreeDirectory {
-  if (nodeHasChildren(tree)) {
-    const contents = sortTree(tree, debuggeeUrl).map((subtree: any) =>
-      sortEntireTree(subtree)
-    );
-    return { ...tree, contents };
-  }
-  return tree;
-}
-
-/**
- * Look at the nodes in the source tree, and determine the index of where to
- * insert a new node. The ordering is index -> folder -> file.
- * @memberof utils/sources-tree
- * @static
- */
 export function sortTree(tree: TreeDirectory, debuggeeUrl: string = "") {
   return (tree.contents: any).sort((previousNode, currentNode) => {
     const currentNodeIsDir = nodeHasChildren(currentNode);
     const previousNodeIsDir = nodeHasChildren(previousNode);
     if (currentNode.name === "(index)") {
       return 1;
     } else if (previousNode.name === "(index)") {
       return -1;
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
@@ -1,83 +1,96 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`sources-tree addToTree can add a file to an intermediate directory 1`] = `
 " - root path= 
-  - unpkg.com path=unpkg.com 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 
-      - mode path=unpkg.com/codemirror@5.1/mode 
-        - xml path=unpkg.com/codemirror@5.1/mode/xml 
-          - xml.js path=unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - unpkg.com path=FakeThread/unpkg.com 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 
+        - mode path=FakeThread/unpkg.com/codemirror@5.1/mode 
+          - xml path=FakeThread/unpkg.com/codemirror@5.1/mode/xml 
+            - xml.js path=FakeThread/unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree correctly parses file sources 1`] = `
 " - root path= 
-  - file:// path=file:// 
-    - a path=file:///a 
-      - b.js path=file:///a/b.js source_id=actor1 
+  - FakeThread path=FakeThread 
+    - file:// path=FakeThread/file:// 
+      - a path=FakeThread/file:///a 
+        - b.js path=FakeThread/file:///a/b.js source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree does not attempt to add two of the same directory 1`] = `
 " - root path= 
-  - davidwalsh.name path=davidwalsh.name 
-    - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
-    - wp-content path=davidwalsh.name/wp-content 
-      - prism.js path=davidwalsh.name/wp-content/prism.js source_id=server1.conn13.child1/39 
+  - FakeThread path=FakeThread 
+    - davidwalsh.name path=FakeThread/davidwalsh.name 
+      - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
+      - wp-content path=FakeThread/davidwalsh.name/wp-content 
+        - prism.js path=FakeThread/davidwalsh.name/wp-content/prism.js source_id=server1.conn13.child1/39 
 "
 `;
 
 exports[`sources-tree addToTree does not attempt to add two of the same file 1`] = `
 " - root path= 
-  - davidwalsh.name path=davidwalsh.name 
-    - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - davidwalsh.name path=FakeThread/davidwalsh.name 
+      - (index) path=https://davidwalsh.name/ source_id=server1.conn13.child1/39 
+      - util.js path=FakeThread/davidwalsh.name/util.js source_id=server1.conn13.child1/37 
+  - FakeThread2 path=FakeThread2 
+    - davidwalsh.name path=FakeThread2/davidwalsh.name 
+      - util.js path=FakeThread2/davidwalsh.name/util.js source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree does not mangle encoded URLs 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 path=example.com/foo/B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 source_id=actor1 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - foo path=FakeThread/example.com/foo 
+        - B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 path=FakeThread/example.com/foo/B9724220.131821496;dc_ver=42.111;sz=468x60;u_sd=2;dc_adk=2020465299;ord=a53rpc;dc_rfl=1,https%3A%2F%2Fdavidwalsh.name%2F$0;xdt=1 source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree excludes javascript: URLs from the tree 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - source1.js path=example.com/source1.js source_id=actor2 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - source1.js path=FakeThread/example.com/source1.js source_id=actor2 
 "
 `;
 
 exports[`sources-tree addToTree name does not include query params 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - name.js path=example.com/foo/name.js source_id=actor1 
+  - FakeThread path=FakeThread 
+    - example.com path=FakeThread/example.com 
+      - foo path=FakeThread/example.com/foo 
+        - name.js path=FakeThread/example.com/foo/name.js source_id=actor1 
 "
 `;
 
 exports[`sources-tree addToTree replaces a file with a directory 1`] = `
 " - root path= 
-  - unpkg.com path=unpkg.com 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 
-      - mode path=unpkg.com/codemirror@5.1/mode 
-        - xml path=unpkg.com/codemirror@5.1/mode/xml 
-          - xml.js path=unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
-    - codemirror@5.1 path=unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
+  - FakeThread path=FakeThread 
+    - unpkg.com path=FakeThread/unpkg.com 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 
+        - mode path=FakeThread/unpkg.com/codemirror@5.1/mode 
+          - xml path=FakeThread/unpkg.com/codemirror@5.1/mode/xml 
+            - xml.js path=FakeThread/unpkg.com/codemirror@5.1/mode/xml/xml.js source_id=server1.conn13.child1/39 
+      - codemirror@5.1 path=FakeThread/unpkg.com/codemirror@5.1 source_id=server1.conn13.child1/37 
 "
 `;
 
 exports[`sources-tree addToTree supports data URLs 1`] = `
 " - root path= 
-  - (no domain) path=(no domain) 
-    - data:text/html,<script>console.log(123)</script> path=data:text/html,<script>console.log(123)</script> source_id=server1.conn13.child1/39 
+  - FakeThread path=FakeThread 
+    - (no domain) path=FakeThread/(no domain) 
+      - data:text/html,<script>console.log(123)</script> path=data:text/html,<script>console.log(123)</script> source_id=server1.conn13.child1/39 
 "
 `;
 
 exports[`sources-tree addToTree uses debuggeeUrl as default 1`] = `
 " - root path= 
   - localhost:4242 path=localhost:4242 
     - components path=localhost:4242/components 
       - Header.js path=localhost:4242/components/Header.js source_id=undefined 
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/collapseTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/collapseTree.spec.js.snap
@@ -1,38 +1,42 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`sources tree collapseTree can collapse a single source 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with a deeper level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c/d path=example.com/a/b/c/d 
-        - e.js path=example.com/a/b/c/d/e.js source_id=actor2 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c/d path=Main Thread/example.com/a/b/c/d 
+          - e.js path=Main Thread/example.com/a/b/c/d/e.js source_id=actor2 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with a shallower level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
-      - x.js path=example.com/a/b/x.js source_id=actor3 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
+        - x.js path=Main Thread/example.com/a/b/x.js source_id=actor3 
 "
 `;
 
 exports[`sources tree collapseTree correctly merges in a collapsed source with the same level 1`] = `
 " - root path= 
-  - example.com path=example.com 
-    - a/b path=example.com/a/b 
-      - c/d path=example.com/a/b/c/d 
-        - e.js path=example.com/a/b/c/d/e.js source_id=actor2 
-      - c.js path=example.com/a/b/c.js source_id=actor1 
+  - Main Thread path=Main Thread 
+    - example.com path=Main Thread/example.com 
+      - a/b path=Main Thread/example.com/a/b 
+        - c/d path=Main Thread/example.com/a/b/c/d 
+          - e.js path=Main Thread/example.com/a/b/c/d/e.js source_id=actor2 
+        - c.js path=Main Thread/example.com/a/b/c.js source_id=actor1 
 "
 `;
deleted file mode 100644
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/sortTree.spec.js.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`sources-tree sortEntireTree alphabetically sorts children 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - foo path=example.com/foo 
-      - a_source3.js path=example.com/foo/a_source3.js source_id=actor3 
-      - b_source2.js path=example.com/foo/b_source2.js source_id=actor2 
-    - source1.js path=example.com/source1.js source_id=actor1 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts folder at the top of the sort 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - folder path=example.com/folder 
-      - b path=example.com/folder/b 
-        - b.js path=example.com/folder/b/b.js source_id=actor2 
-      - c path=example.com/folder/c 
-        - (index) path=http://example.com/folder/c/ source_id=actor3 
-      - a.js path=example.com/folder/a.js source_id=actor1 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts root debugee url at the top of the sort 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - b.js path=example.com/b.js source_id=actor2 
-  - api.example.com path=api.example.com 
-    - a.js path=api.example.com/a.js source_id=actor1 
-  - demo.com path=demo.com 
-    - c.js path=demo.com/c.js source_id=actor3 
-"
-`;
-
-exports[`sources-tree sortEntireTree puts root debugee url at the top of the sort 2`] = `
-" - root path= 
-  - demo.com path=demo.com 
-    - c.js path=demo.com/c.js source_id=actor3 
-  - api.example.com path=api.example.com 
-    - a.js path=api.example.com/a.js source_id=actor1 
-  - example.com path=example.com 
-    - b.js path=example.com/b.js source_id=actor2 
-"
-`;
-
-exports[`sources-tree sortEntireTree sorts folders first 1`] = `
-" - root path= 
-  - example.com path=example.com 
-    - (index) path=http://example.com source_id=actor4 
-    - b.js path=example.com/b.js 
-      - b_source.js path=example.com/b.js/b_source.js source_id=actor2 
-    - d path=example.com/d 
-      - d_source.js path=example.com/d/d_source.js source_id=actor5 
-    - a.js path=example.com/a.js source_id=actor1 
-    - b2 path=example.com/b2 source_id=actor6 
-    - c.js path=example.com/c.js source_id=actor3 
-"
-`;
-
-exports[`sources-tree sortEntireTree sorts folders first 2`] = `
-" - root path= 
-  - example.com path=example.com 
-    - (index) path=http://example.com source_id=actor4 
-    - b.js path=example.com/b.js 
-      - b_source.js path=example.com/b.js/b_source.js source_id=actor2 
-    - d path=example.com/d 
-      - d_source.js path=example.com/d/d_source.js source_id=actor5 
-    - a.js path=example.com/a.js source_id=actor1 
-    - b2 path=example.com/b2 source_id=actor6 
-    - c.js path=example.com/c.js source_id=actor3 
-"
-`;
--- a/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/updateTree.spec.js.snap
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/__snapshots__/updateTree.spec.js.snap
@@ -3,155 +3,161 @@
 exports[`calls updateTree.js adds one source 1`] = `
 "{
   \\"type\\": \\"directory\\",
   \\"name\\": \\"root\\",
   \\"path\\": \\"\\",
   \\"contents\\": [
     {
       \\"type\\": \\"directory\\",
-      \\"name\\": \\"davidwalsh.name\\",
-      \\"path\\": \\"davidwalsh.name\\",
+      \\"name\\": \\"FakeThread\\",
+      \\"path\\": \\"FakeThread\\",
       \\"contents\\": [
         {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"(index)\\",
-          \\"path\\": \\"https://davidwalsh.name/\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/39\\",
-            \\"url\\": \\"https://davidwalsh.name/\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
-        },
-        {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"source1.js\\",
-          \\"path\\": \\"davidwalsh.name/source1.js\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/37\\",
-            \\"url\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
+          \\"type\\": \\"directory\\",
+          \\"name\\": \\"davidwalsh.name\\",
+          \\"path\\": \\"FakeThread/davidwalsh.name\\",
+          \\"contents\\": [
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"(index)\\",
+              \\"path\\": \\"https://davidwalsh.name/\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/39\\",
+                \\"url\\": \\"https://davidwalsh.name/\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            },
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"source1.js\\",
+              \\"path\\": \\"FakeThread/davidwalsh.name/source1.js\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/37\\",
+                \\"url\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            }
+          ]
         }
       ]
     }
   ]
 }"
 `;
 
 exports[`calls updateTree.js adds two sources 1`] = `
 "{
   \\"type\\": \\"directory\\",
   \\"name\\": \\"root\\",
   \\"path\\": \\"\\",
   \\"contents\\": [
     {
       \\"type\\": \\"directory\\",
-      \\"name\\": \\"davidwalsh.name\\",
-      \\"path\\": \\"davidwalsh.name\\",
+      \\"name\\": \\"FakeThread\\",
+      \\"path\\": \\"FakeThread\\",
       \\"contents\\": [
         {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"(index)\\",
-          \\"path\\": \\"https://davidwalsh.name/\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/39\\",
-            \\"url\\": \\"https://davidwalsh.name/\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
-        },
-        {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"source1.js\\",
-          \\"path\\": \\"davidwalsh.name/source1.js\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/37\\",
-            \\"url\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
-        },
-        {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"source2.js\\",
-          \\"path\\": \\"davidwalsh.name/source2.js\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/40\\",
-            \\"url\\": \\"https://davidwalsh.name/source2.js\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/source2.js\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
+          \\"type\\": \\"directory\\",
+          \\"name\\": \\"davidwalsh.name\\",
+          \\"path\\": \\"FakeThread/davidwalsh.name\\",
+          \\"contents\\": [
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"(index)\\",
+              \\"path\\": \\"https://davidwalsh.name/\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/39\\",
+                \\"url\\": \\"https://davidwalsh.name/\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            },
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"source1.js\\",
+              \\"path\\": \\"FakeThread/davidwalsh.name/source1.js\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/37\\",
+                \\"url\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            },
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"source2.js\\",
+              \\"path\\": \\"FakeThread/davidwalsh.name/source2.js\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/40\\",
+                \\"url\\": \\"https://davidwalsh.name/source2.js\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/source2.js\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            }
+          ]
         }
       ]
     }
   ]
 }"
 `;
 
 exports[`calls updateTree.js shows all the sources 1`] = `
 "{
   \\"type\\": \\"directory\\",
   \\"name\\": \\"root\\",
   \\"path\\": \\"\\",
   \\"contents\\": [
     {
       \\"type\\": \\"directory\\",
-      \\"name\\": \\"davidwalsh.name\\",
-      \\"path\\": \\"davidwalsh.name\\",
+      \\"name\\": \\"FakeThread\\",
+      \\"path\\": \\"FakeThread\\",
       \\"contents\\": [
         {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"(index)\\",
-          \\"path\\": \\"https://davidwalsh.name/\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/39\\",
-            \\"url\\": \\"https://davidwalsh.name/\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
-        },
-        {
-          \\"type\\": \\"source\\",
-          \\"name\\": \\"source1.js\\",
-          \\"path\\": \\"davidwalsh.name/source1.js\\",
-          \\"contents\\": {
-            \\"id\\": \\"server1.conn13.child1/37\\",
-            \\"url\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"isBlackBoxed\\": false,
-            \\"isPrettyPrinted\\": false,
-            \\"relativeUrl\\": \\"https://davidwalsh.name/source1.js\\",
-            \\"introductionUrl\\": null,
-            \\"isWasm\\": false,
-            \\"isExtension\\": false
-          }
+          \\"type\\": \\"directory\\",
+          \\"name\\": \\"davidwalsh.name\\",
+          \\"path\\": \\"FakeThread/davidwalsh.name\\",
+          \\"contents\\": [
+            {
+              \\"type\\": \\"source\\",
+              \\"name\\": \\"(index)\\",
+              \\"path\\": \\"https://davidwalsh.name/\\",
+              \\"contents\\": {
+                \\"id\\": \\"server1.conn13.child1/39\\",
+                \\"url\\": \\"https://davidwalsh.name/\\",
+                \\"isBlackBoxed\\": false,
+                \\"isPrettyPrinted\\": false,
+                \\"relativeUrl\\": \\"https://davidwalsh.name/\\",
+                \\"introductionUrl\\": null,
+                \\"isWasm\\": false,
+                \\"isExtension\\": false
+              }
+            }
+          ]
         }
       ]
     }
   ]
 }"
 `;
--- a/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/addToTree.spec.js
@@ -12,17 +12,17 @@ import {
   addToTree,
   createDirectoryNode,
   createSourceNode,
   createTree,
   formatTree,
   nodeHasChildren
 } from "../index";
 
-type RawSource = {| url: string, id: string |};
+type RawSource = {| url: string, id: string, actors?: any |};
 
 function createSourcesMap(sources: RawSource[]) {
   const sourcesMap = sources.reduce((map, source) => {
     map[source.id] = makeMockSource(source.url, source.id);
     return map;
   }, {});
 
   return sourcesMap;
@@ -58,20 +58,20 @@ describe("sources-tree", () => {
 
     it("builds a path-based tree", () => {
       const source1 = makeMockSource(
         "http://example.com/foo/source1.js",
         "actor1"
       );
       const tree = createDirectoryNode("root", "", []);
 
-      addToTree(tree, source1, "http://example.com/");
+      addToTree(tree, source1, "http://example.com/", "FakeThread");
       expect(tree.contents).toHaveLength(1);
 
-      const base = tree.contents[0];
+      const base = tree.contents[0].contents[0];
       expect(base.name).toBe("example.com");
       expect(base.contents).toHaveLength(1);
 
       const fooNode = base.contents[0];
       expect(fooNode.name).toBe("foo");
       expect(fooNode.contents).toHaveLength(1);
 
       const source1Node = fooNode.contents[0];
@@ -84,133 +84,168 @@ describe("sources-tree", () => {
 
       const source1 = makeMockSource(
         `https://example.com/foo/${sourceName}`,
         "actor1"
       );
 
       const tree = createDirectoryNode("root", "", []);
 
-      addToTree(tree, source1, "http://example.com/");
-      const childNode = getChildNode(tree, 0, 0, 0);
+      addToTree(tree, source1, "http://example.com/", "FakeThread");
+      const childNode = getChildNode(tree, 0, 0, 0, 0);
       expect(childNode.name).toEqual(sourceName);
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("name does not include query params", () => {
       const sourceName = "name.js?bar=3";
 
       const source1 = makeMockSource(
         `https://example.com/foo/${sourceName}`,
         "actor1"
       );
 
       const tree = createDirectoryNode("root", "", []);
 
-      addToTree(tree, source1, "http://example.com/");
+      addToTree(tree, source1, "http://example.com/", "FakeThread");
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("does not attempt to add two of the same directory", () => {
       const sources = [
         {
           id: "server1.conn13.child1/39",
           url: "https://davidwalsh.name/wp-content/prism.js"
         },
         {
           id: "server1.conn13.child1/37",
           url: "https://davidwalsh.name/"
         }
       ];
 
-      const sourceMap = createSourcesMap(sources);
+      const sourceMap = { FakeThread: createSourcesMap(sources) };
       const tree = createTree({
         sources: sourceMap,
         debuggeeUrl: "",
-        projectRoot: ""
+        threads: [
+          {
+            actor: "FakeThread",
+            name: "FakeThread",
+            url: "https://davidwalsh.name",
+            type: 1
+          }
+        ]
       }).sourceTree;
-      // expect(tree.contents).toHaveLength(1);
-      const subtree = tree.contents[0];
+
+      expect(tree.contents[0].contents).toHaveLength(1);
+      const subtree = tree.contents[0].contents[0];
       expect(subtree.contents).toHaveLength(2);
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("supports data URLs", () => {
       const sources = [
         {
           id: "server1.conn13.child1/39",
           url: "data:text/html,<script>console.log(123)</script>"
         }
       ];
 
-      const sourceMap = createSourcesMap(sources);
+      const sourceMap = { FakeThread: createSourcesMap(sources) };
       const tree = createTree({
         sources: sourceMap,
         debuggeeUrl: "",
-        projectRoot: ""
+        threads: [
+          {
+            actor: "FakeThread",
+            url: "https://davidwalsh.name",
+            type: 1,
+            name: "FakeThread"
+          }
+        ]
       }).sourceTree;
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("does not attempt to add two of the same file", () => {
       const sources = [
         {
           id: "server1.conn13.child1/39",
           url: "https://davidwalsh.name/"
         },
         {
           id: "server1.conn13.child1/37",
-          url: "https://davidwalsh.name/"
+          url: "https://davidwalsh.name/util.js"
         }
       ];
 
-      const sourceMap = createSourcesMap(sources);
+      const sourceMap = {
+        FakeThread: createSourcesMap(sources),
+        FakeThread2: createSourcesMap([sources[1]])
+      };
+
       const tree = createTree({
         sources: sourceMap,
-        debuggeeUrl: "",
-        projectRoot: ""
+        debuggeeUrl: "https://davidwalsh.name",
+        threads: [
+          {
+            actor: "FakeThread",
+            name: "FakeThread",
+            url: "https://davidwalsh.name",
+            type: 1
+          },
+          {
+            actor: "FakeThread2",
+            name: "FakeThread2",
+            url: "https://davidwalsh.name/WorkerA.js",
+            type: 2
+          }
+        ]
       }).sourceTree;
-      expect(tree.contents).toHaveLength(1);
-      const subtree = tree.contents[0];
-      expect(subtree.contents).toHaveLength(1);
+
+      expect(tree.contents[0].contents).toHaveLength(1);
+      const subtree = tree.contents[0].contents[0];
+      expect(subtree.contents).toHaveLength(2);
+      const subtree2 = tree.contents[1].contents[0];
+      expect(subtree2.contents).toHaveLength(1);
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("excludes javascript: URLs from the tree", () => {
       const source1 = makeMockSource(
         "javascript:alert('Hello World')",
         "actor1"
       );
       const source2 = makeMockSource("http://example.com/source1.js", "actor2");
       const source3 = makeMockSource(
         "javascript:let i = 10; while (i > 0) i--; console.log(i);",
         "actor3"
       );
       const tree = createDirectoryNode("root", "", []);
 
-      addToTree(tree, source1, "http://example.com/");
-      addToTree(tree, source2, "http://example.com/");
-      addToTree(tree, source3, "http://example.com/");
+      addToTree(tree, source1, "http://example.com/", "FakeThread");
+      addToTree(tree, source2, "http://example.com/", "FakeThread");
+      addToTree(tree, source3, "http://example.com/", "FakeThread");
 
-      const base = tree.contents[0];
+      const base = tree.contents[0].contents[0];
       expect(tree.contents).toHaveLength(1);
 
       const source1Node = base.contents[0];
       expect(source1Node.name).toBe("source1.js");
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("correctly parses file sources", () => {
       const source = makeMockSource("file:///a/b.js", "actor1");
       const tree = createDirectoryNode("root", "", []);
 
-      addToTree(tree, source, "file:///a/index.html");
+      addToTree(tree, source, "file:///a/index.html", "FakeThread");
       expect(tree.contents).toHaveLength(1);
 
-      const base = tree.contents[0];
+      const base = tree.contents[0].contents[0];
       expect(base.name).toBe("file://");
       expect(base.contents).toHaveLength(1);
 
       const aNode = base.contents[0];
       expect(aNode.name).toBe("a");
       expect(aNode.contents).toHaveLength(1);
 
       const bNode = aNode.contents[0];
@@ -227,17 +262,19 @@ describe("sources-tree", () => {
         {
           id: "server1.conn13.child1/37",
           url: "https://unpkg.com/codemirror@5.1"
         }
       ];
 
       const sources = createSourcesList(testData);
       const tree = createDirectoryNode("root", "", []);
-      sources.forEach(source => addToTree(tree, source, "https://unpkg.com/"));
+      sources.forEach(source =>
+        addToTree(tree, source, "https://unpkg.com/", "FakeThread")
+      );
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("replaces a file with a directory", () => {
       const testData = [
         {
           id: "server1.conn13.child1/37",
           url: "https://unpkg.com/codemirror@5.1"
@@ -246,17 +283,19 @@ describe("sources-tree", () => {
         {
           id: "server1.conn13.child1/39",
           url: "https://unpkg.com/codemirror@5.1/mode/xml/xml.js"
         }
       ];
 
       const sources = createSourcesList(testData);
       const tree = createDirectoryNode("root", "", []);
-      sources.forEach(source => addToTree(tree, source, "https://unpkg.com/"));
+      sources.forEach(source =>
+        addToTree(tree, source, "https://unpkg.com/", "FakeThread")
+      );
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     xit("uses debuggeeUrl as default", () => {
       const testData = [
         {
           url: "components/TodoTextInput.js"
         },
@@ -279,13 +318,13 @@ describe("sources-tree", () => {
         {
           url: "index.js"
         }
       ];
 
       const domain = "http://localhost:4242";
       const sources = createSourcesList(testData);
       const tree = createDirectoryNode("root", "", []);
-      sources.forEach(source => addToTree(tree, source, domain));
+      sources.forEach(source => addToTree(tree, source, domain, "FakeThread"));
       expect(formatTree(tree)).toMatchSnapshot();
     });
   });
 });
--- a/devtools/client/debugger/src/utils/sources-tree/tests/collapseTree.spec.js
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/collapseTree.spec.js
@@ -3,120 +3,116 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import { makeMockSource } from "../../../utils/test-mockup";
 
 import {
   collapseTree,
-  sortEntireTree,
   formatTree,
   addToTree,
   createDirectoryNode
 } from "../index";
 
 const abcSource = makeMockSource("http://example.com/a/b/c.js", "actor1");
 const abcdeSource = makeMockSource("http://example.com/a/b/c/d/e.js", "actor2");
 const abxSource = makeMockSource("http://example.com/a/b/x.js", "actor3");
 
 describe("sources tree", () => {
   describe("collapseTree", () => {
     it("can collapse a single source", () => {
       const fullTree = createDirectoryNode("root", "", []);
-      addToTree(fullTree, abcSource, "http://example.com/");
+      addToTree(fullTree, abcSource, "http://example.com/", "Main Thread");
       expect(fullTree.contents).toHaveLength(1);
       const tree = collapseTree(fullTree);
 
-      const host = tree.contents[0];
+      const host = tree.contents[0].contents[0];
       expect(host.name).toBe("example.com");
       expect(host.contents).toHaveLength(1);
 
       const abFolder = host.contents[0];
       expect(abFolder.name).toBe("a/b");
       expect(abFolder.contents).toHaveLength(1);
 
       const abcNode = abFolder.contents[0];
       expect(abcNode.name).toBe("c.js");
-      expect(abcNode.path).toBe("example.com/a/b/c.js");
+      expect(abcNode.path).toBe("Main Thread/example.com/a/b/c.js");
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("correctly merges in a collapsed source with a deeper level", () => {
       const fullTree = createDirectoryNode("root", "", []);
-      addToTree(fullTree, abcSource, "http://example.com/");
-      addToTree(fullTree, abcdeSource, "http://example.com/");
+      addToTree(fullTree, abcSource, "http://example.com/", "Main Thread");
+      addToTree(fullTree, abcdeSource, "http://example.com/", "Main Thread");
       const tree = collapseTree(fullTree);
 
-      sortEntireTree(tree);
-      expect(tree.contents).toHaveLength(1);
-
-      const host = tree.contents[0];
+      const host = tree.contents[0].contents[0];
       expect(host.name).toBe("example.com");
       expect(host.contents).toHaveLength(1);
 
       const abFolder = host.contents[0];
       expect(abFolder.name).toBe("a/b");
       expect(abFolder.contents).toHaveLength(2);
 
       const [cdFolder, abcNode] = abFolder.contents;
       expect(abcNode.name).toBe("c.js");
-      expect(abcNode.path).toBe("example.com/a/b/c.js");
+      expect(abcNode.path).toBe("Main Thread/example.com/a/b/c.js");
       expect(cdFolder.name).toBe("c/d");
 
       const [abcdeNode] = cdFolder.contents;
       expect(abcdeNode.name).toBe("e.js");
-      expect(abcdeNode.path).toBe("example.com/a/b/c/d/e.js");
+      expect(abcdeNode.path).toBe("Main Thread/example.com/a/b/c/d/e.js");
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("correctly merges in a collapsed source with a shallower level", () => {
       const fullTree = createDirectoryNode("root", "", []);
-      addToTree(fullTree, abcSource, "http://example.com/");
-      addToTree(fullTree, abxSource, "http://example.com/");
+      addToTree(fullTree, abcSource, "http://example.com/", "Main Thread");
+      addToTree(fullTree, abxSource, "http://example.com/", "Main Thread");
       const tree = collapseTree(fullTree);
 
       expect(tree.contents).toHaveLength(1);
 
-      const host = tree.contents[0];
+      const host = tree.contents[0].contents[0];
       expect(host.name).toBe("example.com");
       expect(host.contents).toHaveLength(1);
 
       const abFolder = host.contents[0];
       expect(abFolder.name).toBe("a/b");
       expect(abFolder.contents).toHaveLength(2);
 
       const [abcNode, abxNode] = abFolder.contents;
       expect(abcNode.name).toBe("c.js");
-      expect(abcNode.path).toBe("example.com/a/b/c.js");
+      expect(abcNode.path).toBe("Main Thread/example.com/a/b/c.js");
       expect(abxNode.name).toBe("x.js");
-      expect(abxNode.path).toBe("example.com/a/b/x.js");
+      expect(abxNode.path).toBe("Main Thread/example.com/a/b/x.js");
       expect(formatTree(tree)).toMatchSnapshot();
     });
 
     it("correctly merges in a collapsed source with the same level", () => {
       const fullTree = createDirectoryNode("root", "", []);
-      addToTree(fullTree, abcdeSource, "http://example.com/");
-      addToTree(fullTree, abcSource, "http://example.com/");
+      addToTree(fullTree, abcdeSource, "http://example.com/", "Main Thread");
+      addToTree(fullTree, abcSource, "http://example.com/", "Main Thread");
       const tree = collapseTree(fullTree);
 
       expect(tree.contents).toHaveLength(1);
 
-      const host = tree.contents[0];
+      const host = tree.contents[0].contents[0];
       expect(host.name).toBe("example.com");
       expect(host.contents).toHaveLength(1);
 
       const abFolder = host.contents[0];
       expect(abFolder.name).toBe("a/b");
       expect(abFolder.contents).toHaveLength(2);
 
       const [cdFolder, abcNode] = abFolder.contents;
       expect(abcNode.name).toBe("c.js");
-      expect(abcNode.path).toBe("example.com/a/b/c.js");
+      expect(abcNode.path).toBe("Main Thread/example.com/a/b/c.js");
       expect(cdFolder.name).toBe("c/d");
 
       const [abcdeNode] = cdFolder.contents;
       expect(abcdeNode.name).toBe("e.js");
-      expect(abcdeNode.path).toBe("example.com/a/b/c/d/e.js");
+      expect(abcdeNode.path).toBe("Main Thread/example.com/a/b/c/d/e.js");
       expect(formatTree(tree)).toMatchSnapshot();
     });
   });
 });
--- a/devtools/client/debugger/src/utils/sources-tree/tests/getDirectories.spec.js
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/getDirectories.spec.js
@@ -9,34 +9,58 @@ import { makeMockSource } from "../../..
 import { getDirectories, createTree } from "../index";
 
 function formatDirectories(source, tree) {
   const paths: any = getDirectories(source, tree);
   return paths.map(node => node.path);
 }
 
 function createSources(urls) {
-  return urls.reduce((sources, url, index) => {
-    const id = `a${index}`;
-    sources[id] = makeMockSource(url, id);
-    return sources;
-  }, {});
+  return {
+    FakeThread: urls.reduce((sources, url, index) => {
+      const id = `a${index}`;
+      sources[id] = makeMockSource(url, id);
+      return sources;
+    }, {})
+  };
 }
 
 describe("getDirectories", () => {
   it("gets a source's ancestor directories", function() {
     const sources = createSources([
       "http://a/b.js",
       "http://a/c.js",
       "http://b/c.js"
     ]);
 
+    const threads = [
+      {
+        actor: "FakeThread",
+        url: "http://a",
+        type: 1,
+        name: "FakeThread"
+      }
+    ];
+
     const debuggeeUrl = "http://a/";
     const { sourceTree } = createTree({
       sources,
       debuggeeUrl,
-      projectRoot: ""
+      threads
     });
-    expect(formatDirectories(sources.a0, sourceTree)).toEqual(["a/b.js", "a"]);
-    expect(formatDirectories(sources.a1, sourceTree)).toEqual(["a/c.js", "a"]);
-    expect(formatDirectories(sources.a2, sourceTree)).toEqual(["b/c.js", "b"]);
+
+    expect(formatDirectories(sources.FakeThread.a0, sourceTree)).toEqual([
+      "FakeThread/a/b.js",
+      "FakeThread/a",
+      "FakeThread"
+    ]);
+    expect(formatDirectories(sources.FakeThread.a1, sourceTree)).toEqual([
+      "FakeThread/a/c.js",
+      "FakeThread/a",
+      "FakeThread"
+    ]);
+    expect(formatDirectories(sources.FakeThread.a2, sourceTree)).toEqual([
+      "FakeThread/b/c.js",
+      "FakeThread/b",
+      "FakeThread"
+    ]);
   });
 });
deleted file mode 100644
--- a/devtools/client/debugger/src/utils/sources-tree/tests/sortTree.spec.js
+++ /dev/null
@@ -1,153 +0,0 @@
-/* eslint max-nested-callbacks: ["error", 4]*/
-/* 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 { makeMockSource } from "../../../utils/test-mockup";
-
-import {
-  addToTree,
-  sortEntireTree,
-  createDirectoryNode,
-  formatTree
-} from "../index";
-
-describe("sources-tree", () => {
-  describe("sortEntireTree", () => {
-    it("alphabetically sorts children", () => {
-      const source1 = makeMockSource("http://example.com/source1.js", "actor1");
-      const source2 = makeMockSource(
-        "http://example.com/foo/b_source2.js",
-        "actor2"
-      );
-      const source3 = makeMockSource(
-        "http://example.com/foo/a_source3.js",
-        "actor3"
-      );
-      const _tree = createDirectoryNode("root", "", []);
-
-      addToTree(_tree, source1, "http://example.com/");
-      addToTree(_tree, source2, "http://example.com/");
-      addToTree(_tree, source3, "http://example.com/");
-      const tree = sortEntireTree(_tree);
-
-      const base = tree.contents[0];
-      const fooNode = base.contents[0];
-      expect(fooNode.name).toBe("foo");
-      expect(fooNode.contents).toHaveLength(2);
-
-      const source1Node = base.contents[1];
-      expect(source1Node.name).toBe("source1.js");
-
-      // source2 should be after source1 alphabetically
-      const source2Node = fooNode.contents[1];
-      const source3Node = fooNode.contents[0];
-      expect(source2Node.name).toBe("b_source2.js");
-      expect(source3Node.name).toBe("a_source3.js");
-      expect(formatTree(tree)).toMatchSnapshot();
-    });
-
-    it("sorts folders first", () => {
-      const sources = [
-        makeMockSource("http://example.com/a.js", "actor1"),
-        makeMockSource("http://example.com/b.js/b_source.js", "actor2"),
-        makeMockSource("http://example.com/c.js", "actor3"),
-        makeMockSource("http://example.com", "actor4"),
-        makeMockSource("http://example.com/d/d_source.js", "actor5"),
-        makeMockSource("http://example.com/b2", "actor6")
-      ];
-
-      const _tree = createDirectoryNode("root", "", []);
-      sources.forEach(source =>
-        addToTree(_tree, source, "http://example.com/")
-      );
-      const tree = sortEntireTree(_tree);
-      const domain = tree.contents[0];
-
-      const [
-        indexNode,
-        bFolderNode,
-        dFolderNode,
-        aFileNode,
-        b2FileNode,
-        cFileNode
-      ] = domain.contents;
-
-      expect(formatTree(tree)).toMatchSnapshot();
-      expect(indexNode.name).toBe("(index)");
-      expect(bFolderNode.name).toBe("b.js");
-      expect(bFolderNode.contents).toHaveLength(1);
-      expect(bFolderNode.contents[0].name).toBe("b_source.js");
-
-      expect(b2FileNode.name).toBe("b2");
-
-      expect(dFolderNode.name).toBe("d");
-      expect(dFolderNode.contents).toHaveLength(1);
-      expect(dFolderNode.contents[0].name).toBe("d_source.js");
-
-      expect(aFileNode.name).toBe("a.js");
-
-      expect(cFileNode.name).toBe("c.js");
-      expect(formatTree(tree)).toMatchSnapshot();
-    });
-
-    it("puts folder at the top of the sort", () => {
-      const sources = [
-        makeMockSource("http://example.com/folder/a.js", "actor1"),
-        makeMockSource("http://example.com/folder/b/b.js", "actor2"),
-        makeMockSource("http://example.com/folder/c/", "actor3")
-      ];
-
-      const _tree = createDirectoryNode("root", "", []);
-      sources.forEach(source =>
-        addToTree(_tree, source, "http://example.com/")
-      );
-      const tree = sortEntireTree(_tree);
-      const [
-        bFolderNode,
-        cFolderNode,
-        aFileNode
-      ] = tree.contents[0].contents[0].contents;
-
-      expect(bFolderNode.name).toBe("b");
-      expect(bFolderNode.contents).toHaveLength(1);
-      expect(bFolderNode.contents[0].name).toBe("b.js");
-
-      expect(cFolderNode.name).toBe("c");
-      expect(cFolderNode.contents).toHaveLength(1);
-      expect(cFolderNode.contents[0].name).toBe("(index)");
-
-      expect(aFileNode.name).toBe("a.js");
-      expect(formatTree(tree)).toMatchSnapshot();
-    });
-
-    it("puts root debugee url at the top of the sort", () => {
-      const sources = [
-        makeMockSource("http://api.example.com/a.js", "actor1"),
-        makeMockSource("http://example.com/b.js", "actor2"),
-        makeMockSource("http://demo.com/c.js", "actor3")
-      ];
-
-      const rootA = "http://example.com/path/to/file.html";
-      const rootB = "https://www.demo.com/index.html";
-      const _treeA = createDirectoryNode("root", "", []);
-      const _treeB = createDirectoryNode("root", "", []);
-      sources.forEach(source => {
-        addToTree(_treeA, source, rootA);
-        addToTree(_treeB, source, rootB);
-      });
-      const treeA = sortEntireTree(_treeA, rootA);
-      const treeB = sortEntireTree(_treeB, rootB);
-
-      expect(treeA.contents[0].contents[0].name).toBe("b.js");
-      expect(treeA.contents[1].contents[0].name).toBe("a.js");
-
-      expect(treeB.contents[0].contents[0].name).toBe("c.js");
-      expect(treeB.contents[1].contents[0].name).toBe("a.js");
-      expect(formatTree(treeA)).toMatchSnapshot();
-      expect(formatTree(treeB)).toMatchSnapshot();
-    });
-  });
-});
--- a/devtools/client/debugger/src/utils/sources-tree/tests/updateTree.spec.js
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/updateTree.spec.js
@@ -2,25 +2,25 @@
  * 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 { makeMockSource } from "../../../utils/test-mockup";
 import { updateTree, createTree } from "../index";
 
-type RawSource = {| url: string, id: string |};
+type RawSource = {| url: string, id: string, actors?: any |};
 
 function createSourcesMap(sources: RawSource[]) {
   const sourcesMap = sources.reduce((map, source) => {
     map[source.id] = makeMockSource(source.url, source.id);
     return map;
   }, {});
 
-  return sourcesMap;
+  return { FakeThread: sourcesMap };
 }
 
 function formatTree(tree) {
   return JSON.stringify(tree.uncollapsedTree, null, 2);
 }
 
 const sources = [
   {
@@ -32,76 +32,85 @@ const sources = [
     url: "https://davidwalsh.name/source1.js"
   },
   {
     id: "server1.conn13.child1/40",
     url: "https://davidwalsh.name/source2.js"
   }
 ];
 
+const threads = [
+  {
+    actor: "FakeThread",
+    url: "https://davidwalsh.name",
+    type: 1,
+    name: "FakeThread"
+  }
+];
+
 const debuggeeUrl = "blah";
 
 describe("calls updateTree.js", () => {
   it("adds one source", () => {
     const prevSources = createSourcesMap([sources[0]]);
-
     const { sourceTree, uncollapsedTree } = createTree({
       debuggeeUrl,
       sources: prevSources,
-      projectRoot: ""
+      threads
     });
-
     const newTree = updateTree({
       debuggeeUrl,
       prevSources,
       newSources: createSourcesMap([sources[0], sources[1]]),
       uncollapsedTree,
       sourceTree,
-      projectRoot: ""
+      threads
     });
 
     expect(formatTree(newTree)).toMatchSnapshot();
   });
 
   it("adds two sources", () => {
     const prevSources = createSourcesMap([sources[0]]);
 
     const { sourceTree, uncollapsedTree } = createTree({
       debuggeeUrl,
       sources: prevSources,
-      projectRoot: ""
+      threads
     });
 
     const newTree = updateTree({
       debuggeeUrl,
       prevSources,
       newSources: createSourcesMap([sources[0], sources[1], sources[2]]),
       uncollapsedTree,
       sourceTree,
-      projectRoot: ""
+      projectRoot: "",
+      threads
     });
 
     expect(formatTree(newTree)).toMatchSnapshot();
   });
 
   // NOTE: we currently only add sources to the tree and clear the tree
   // on navigate.
   it("shows all the sources", () => {
     const prevSources = createSourcesMap([sources[0]]);
 
     const { sourceTree, uncollapsedTree } = createTree({
       debuggeeUrl,
       sources: prevSources,
-      projectRoot: ""
+      threads
     });
 
     const newTree = updateTree({
       debuggeeUrl,
       prevSources,
       newSources: createSourcesMap([sources[1]]),
       uncollapsedTree,
       sourceTree,
-      projectRoot: ""
+      projectRoot: "",
+      threads
     });
 
     expect(formatTree(newTree)).toMatchSnapshot();
   });
 });
--- a/devtools/client/debugger/src/utils/sources-tree/tests/utils.spec.js
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/utils.spec.js
@@ -8,17 +8,16 @@
 import { makeMockSource } from "../../test-mockup";
 
 import {
   createDirectoryNode,
   getRelativePath,
   isExactUrlMatch,
   isDirectory,
   addToTree,
-  sortEntireTree,
   isNotJavaScript
 } from "../index";
 
 describe("sources tree", () => {
   describe("isExactUrlMatch", () => {
     it("recognizes root url match", () => {
       const rootA = "http://example.com/path/to/file.html";
       const rootB = "https://www.demo.com/index.html";
@@ -42,19 +41,20 @@ describe("sources tree", () => {
   describe("isDirectory", () => {
     it("identifies directories correctly", () => {
       const sources = [
         makeMockSource("http://example.com/a.js", "actor1"),
         makeMockSource("http://example.com/b/c/d.js", "actor2")
       ];
 
       const tree = createDirectoryNode("root", "", []);
-      sources.forEach(source => addToTree(tree, source, "http://example.com/"));
-      sortEntireTree(tree);
-      const [bFolderNode, aFileNode] = tree.contents[0].contents;
+      sources.forEach(source =>
+        addToTree(tree, source, "http://example.com/", "Main Thread")
+      );
+      const [bFolderNode, aFileNode] = tree.contents[0].contents[0].contents;
       const [cFolderNode] = bFolderNode.contents;
       const [dFileNode] = cFolderNode.contents;
 
       expect(isDirectory(bFolderNode)).toBe(true);
       expect(isDirectory(aFileNode)).toBe(false);
       expect(isDirectory(cFolderNode)).toBe(true);
       expect(isDirectory(dFileNode)).toBe(false);
     });
--- a/devtools/client/debugger/src/utils/sources-tree/updateTree.js
+++ b/devtools/client/debugger/src/utils/sources-tree/updateTree.js
@@ -1,52 +1,92 @@
 /* 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 { addToTree } from "./addToTree";
 import { collapseTree } from "./collapseTree";
-import { createParentMap } from "./utils";
-import { difference } from "lodash";
+import { createDirectoryNode, createParentMap } from "./utils";
 import { getDomain } from "./treeOrder";
-import type { SourcesMap } from "../../reducers/types";
+
+import type { SourcesMapByThread } from "../../reducers/types";
+import type { Thread, Source } from "../../types";
 import type { TreeDirectory } from "./types";
 
-function newSourcesSet(newSources, prevSources) {
-  const newSourceIds = difference(
-    Object.keys(newSources),
-    Object.keys(prevSources)
-  );
-  const uniqSources = newSourceIds.map(id => newSources[id]);
-  return uniqSources;
+function getSourcesToAdd(newSources, prevSources): Source[] {
+  const sourcesToAdd = [];
+
+  for (const sourceId in newSources) {
+    const newSource = newSources[sourceId];
+    const prevSource = prevSources ? prevSources[sourceId] : null;
+    if (!prevSource) {
+      sourcesToAdd.push(newSource);
+    }
+  }
+
+  return sourcesToAdd;
 }
 
-type Params = {
-  newSources: SourcesMap,
-  prevSources: SourcesMap,
+type UpdateTreeParams = {
+  newSources: SourcesMapByThread,
+  prevSources: SourcesMapByThread,
   uncollapsedTree: TreeDirectory,
-  sourceTree: TreeDirectory,
-  debuggeeUrl: string
+  debuggeeUrl: string,
+  threads: Thread[]
+};
+
+type CreateTreeParams = {
+  sources: SourcesMapByThread,
+  debuggeeUrl: string,
+  threads: Thread[]
 };
 
+export function createTree({
+  debuggeeUrl,
+  sources,
+  threads
+}: CreateTreeParams) {
+  const uncollapsedTree = createDirectoryNode("root", "", []);
+
+  return updateTree({
+    debuggeeUrl,
+    newSources: sources,
+    prevSources: {},
+    threads,
+    uncollapsedTree
+  });
+}
+
 export function updateTree({
   newSources,
   prevSources,
   debuggeeUrl,
   uncollapsedTree,
-  sourceTree
-}: Params) {
-  const newSet = newSourcesSet(newSources, prevSources);
+  threads
+}: UpdateTreeParams) {
   const debuggeeHost = getDomain(debuggeeUrl);
+  const contexts = (Object.keys(newSources): any);
 
-  for (const source of newSet) {
-    addToTree(uncollapsedTree, source, debuggeeHost);
-  }
+  contexts.forEach(context => {
+    const thread = threads.find(t => t.actor === context);
+    if (!thread) {
+      return;
+    }
+
+    const sourcesToAdd = getSourcesToAdd(
+      (Object.values(newSources[context]): any),
+      prevSources[context] ? (Object.values(prevSources[context]): any) : null
+    );
+
+    for (const source of sourcesToAdd) {
+      addToTree(uncollapsedTree, source, debuggeeHost, thread.actor);
+    }
+  });
 
   const newSourceTree = collapseTree(uncollapsedTree);
 
   return {
     uncollapsedTree,
     sourceTree: newSourceTree,
     parentMap: createParentMap(newSourceTree)
   };
--- a/devtools/client/debugger/src/utils/sources-tree/utils.js
+++ b/devtools/client/debugger/src/utils/sources-tree/utils.js
@@ -8,17 +8,17 @@ import { parse } from "../../utils/url";
 
 import type { TreeNode, TreeSource, TreeDirectory, ParentMap } from "./types";
 import type { Source } from "../../types";
 import { isPretty } from "../source";
 import { getURL } from "./getURL";
 const IGNORED_URLS = ["debugger eval code", "XStringBundle"];
 
 export function nodeHasChildren(item: TreeNode): boolean {
-  return Array.isArray(item.contents) && item.type === "directory";
+  return Array.isArray(item.contents) && item.type == "directory";
 }
 
 export function isExactUrlMatch(pathPart: string, debuggeeUrl: string) {
   // compare to hostname with an optional 'www.' prefix
   const { host } = parse(debuggeeUrl);
   if (!host) {
     return false;
   }
--- a/devtools/client/debugger/test/mochitest/browser_dbg-asm.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-asm.js
@@ -5,17 +5,17 @@ add_task(async function() {
   // After reload() we are getting getSources notifiction for old sources,
   // using the debugger statement to really stop are reloaded page.
   await waitForPaused(dbg);
   await resume(dbg);
 
   await waitForSources(dbg, "doc-asm.html", "asm.js");
 
   // Make sure sources appear.
-  is(findAllElements(dbg, "sourceNodes").length, 2);
+  is(findAllElements(dbg, "sourceNodes").length, 3);
 
   await selectSource(dbg, "asm.js");
 
   await addBreakpoint(dbg, "asm.js", 7);
   invokeInTab("runAsm");
 
   await waitForPaused(dbg);
   assertPausedLocation(dbg, "asm.js", 7);
--- a/devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-content-script-sources.js
@@ -44,24 +44,30 @@ async function installAndStartExtension(
   return extension;
 }
 
 add_task(async function() {
   await pushPref("devtools.chrome.enabled", true);
   const extension = await installAndStartExtension();
 
   let dbg = await initDebugger("doc-content-script-sources.html");
+  await clickElement(dbg, "sourceDirectoryLabel", 2);
+
+
   await selectContentScriptSources(dbg);
   await closeTab(dbg, "content_script.js");
 
   // Destroy the toolbox and repeat the test in a new toolbox
   // and ensures that the content script is still listed.
   await dbg.toolbox.destroy();
   const toolbox = await openToolboxForTab(gBrowser.selectedTab, "jsdebugger");
   dbg = createDebuggerContext(toolbox);
+
+  await clickElement(dbg, "sourceDirectoryLabel", 2);
+
   await selectContentScriptSources(dbg);
 
   await addBreakpoint(dbg, "content_script.js", 2);
 
   for (let i = 1; i < 3; i++) {
     info(`Reloading tab (${i} time)`);
     gBrowser.reloadTab(gBrowser.selectedTab);
     await waitForPaused(dbg);
--- a/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-sourcemaps.js
@@ -61,18 +61,18 @@ add_task(async function() {
     selectors: { getBreakpointCount },
     getState
   } = dbg;
 
   ok(true, "Original sources exist");
   const bundleSrc = findSource(dbg, "bundle.js");
 
   // Check that the original sources appear in the source tree
-  await clickElement(dbg, "sourceDirectoryLabel", 3);
-  await assertSourceCount(dbg, 8);
+  await clickElement(dbg, "sourceDirectoryLabel", 4);
+  await assertSourceCount(dbg, 9);
 
   await selectSource(dbg, bundleSrc);
 
   await clickGutter(dbg, 70);
   await waitForBreakpointCount(dbg, 1);
   await assertEditorBreakpoint(dbg, 70, true);
 
   await clickGutter(dbg, 70);
--- a/devtools/client/debugger/test/mochitest/browser_dbg-sources-arrow-keys.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-sources-arrow-keys.js
@@ -1,59 +1,60 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test keyboard arrow behaviour
 add_task(async function() {
   const dbg = await initDebugger("doc-sources.html", "simple1", "simple2", "nested-source", "long.js");
 
-  await clickElement(dbg, "sourceDirectoryLabel", 2);
-  await assertSourceCount(dbg, 7);
+  await clickElement(dbg, "sourceDirectoryLabel", 3);
+  await assertSourceCount(dbg, 8);
 
   // Right key on open dir
   await pressKey(dbg, "Right");
   await assertNodeIsFocused(dbg, 3);
 
   // Right key on closed dir
   await pressKey(dbg, "Right");
   await assertSourceCount(dbg, 8);
-  await assertNodeIsFocused(dbg, 3);
+  await assertNodeIsFocused(dbg, 4);
 
   // Left key on a open dir
   await pressKey(dbg, "Left");
-  await assertSourceCount(dbg, 7);
-  await assertNodeIsFocused(dbg, 3);
+  await assertSourceCount(dbg, 8);
+  await assertNodeIsFocused(dbg, 4);
 
   // Down key on a closed dir
   await pressKey(dbg, "Down");
   await assertNodeIsFocused(dbg, 4);
 
   // Right key on a source
   await pressKey(dbg, "Right");
-  await assertNodeIsFocused(dbg, 5);
+  await assertNodeIsFocused(dbg, 4);
 
   // Down key on a source
+  await waitForSourceCount(dbg, 9);
   await pressKey(dbg, "Down");
-  await assertNodeIsFocused(dbg, 6);
+  await assertNodeIsFocused(dbg, 5);
 
   // Go to bottom of tree and press down key
   await pressKey(dbg, "Down");
   await pressKey(dbg, "Down");
-  await assertNodeIsFocused(dbg, 7);
+  await assertNodeIsFocused(dbg, 6);
 
   // Up key on a source
   await pressKey(dbg, "Up");
-  await assertNodeIsFocused(dbg, 6);
+  await assertNodeIsFocused(dbg, 5);
 
   // Left key on a source
   await pressKey(dbg, "Left");
-  await assertNodeIsFocused(dbg, 2);
+  await assertNodeIsFocused(dbg, 4);
 
   // Left key on a closed dir
   await pressKey(dbg, "Left");
-  await assertSourceCount(dbg, 2);
+  await assertSourceCount(dbg, 8);
   await pressKey(dbg, "Left");
-  await assertNodeIsFocused(dbg, 1);
+  await assertNodeIsFocused(dbg, 3);
 
   // Up Key at the top of the source tree
   await pressKey(dbg, "Up");
-  await assertNodeIsFocused(dbg, 1);
+  await assertNodeIsFocused(dbg, 2);
 });
--- a/devtools/client/debugger/test/mochitest/browser_dbg-sources.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-sources.js
@@ -13,47 +13,47 @@ add_task(async function() {
     "long.js"
   );
   const {
     selectors: { getSelectedSource },
     getState
   } = dbg;
 
   // Expand nodes and make sure more sources appear.
-  await assertSourceCount(dbg, 2);
-  await clickElement(dbg, "sourceDirectoryLabel", 2);
+  await assertSourceCount(dbg, 3);
+  await clickElement(dbg, "sourceDirectoryLabel", 3);
 
-  await assertSourceCount(dbg, 7);
-  await clickElement(dbg, "sourceDirectoryLabel", 3);
   await assertSourceCount(dbg, 8);
+  await clickElement(dbg, "sourceDirectoryLabel", 4);
+  await assertSourceCount(dbg, 9);
 
   const selected = waitForDispatch(dbg, "SET_SELECTED_LOCATION");
-  await clickElement(dbg, "sourceNode", 4);
+  await clickElement(dbg, "sourceNode", 5);
   await selected;
   await waitForSelectedSource(dbg);
 
   // Ensure the source file clicked is now focused
   await waitForElementWithSelector(dbg, ".sources-list .focused");
 
   const focusedNode = findElementWithSelector(dbg, ".sources-list .focused");
-  const fourthNode = findElement(dbg, "sourceNode", 4);
+  const fourthNode = findElement(dbg, "sourceNode", 5);
   const selectedSource = getSelectedSource().url;
 
   ok(fourthNode.classList.contains("focused"), "4th node is focused");
   ok(selectedSource.includes("nested-source.js"), "nested-source is selected");
-  await assertNodeIsFocused(dbg, 4);
+  await assertNodeIsFocused(dbg, 5);
   await waitForSelectedSource(dbg, "nested-source");
 
   // Make sure new sources appear in the list.
   ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
     const script = content.document.createElement("script");
     script.src = "math.min.js";
     content.document.body.appendChild(script);
   });
 
-  await waitForSourceCount(dbg, 9);
-  await assertNodeIsFocused(dbg, 4);
+  await waitForSourceCount(dbg, 10);
+  await assertNodeIsFocused(dbg, 5);
   is(
-    getSourceNodeLabel(dbg, 7),
+    getSourceNodeLabel(dbg, 8),
     "math.min.js",
     "math.min.js - The dynamic script exists"
   );
 });
--- a/devtools/client/framework/test/test_browser_toolbox_debugger.js
+++ b/devtools/client/framework/test/test_browser_toolbox_debugger.js
@@ -10,22 +10,21 @@ info(`START: ${new Error().lineNumber}`)
 
   info("Waiting for debugger load");
   await toolbox.selectTool("jsdebugger");
   const dbg = createDebuggerContext(toolbox);
   const window = dbg.win;
   const document = window.document;
 
   await waitForSources(dbg, testUrl);
-//  yield waitForSourceCount(dbg, 6);
 
   info("Loaded, selecting the test script to debug");
   // First expand the domain
   const domain = [...document.querySelectorAll(".tree-node")].find(node => {
-    return node.textContent.trim() == "mozilla.org";
+    return node.querySelector(".label").textContent.trim() == "mozilla.org";
   });
   const arrow = domain.querySelector(".arrow");
   arrow.click();
 
   const fileName = testUrl.match(/browser-toolbox-test.*\.js/)[0];
 
   let script = [...document.querySelectorAll(".tree-node")].find(node => {
     return node.textContent.includes(fileName);