Backed out 5 changesets (bug 1520957) failing browser_webconsole... tests CLOSED TREE
authorCiure Andrei <aciure@mozilla.com>
Sat, 19 Jan 2019 00:49:12 +0200
changeset 514545 ef836f7dfc086551403866e9dd5ca81feb4cdabc
parent 514544 57aacc322be1c01f82b987a41f6e577260778d0e
child 514546 dd45dce4d3a9e37af40b36e631ffc9841817a3a0
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1520957
milestone66.0a1
backs out6371d4b4ff1ec1d79446f3c05898903a17ff41f7
862c4fc3b6abcf5094a00993f17331f04383bba0
86defe96c1bbc5142c11a650107efea59ed33c3f
4655b2d86e56dc3bd0c9e645f740e3ecee5a2d35
67db08315e4b2346cd7ffb11aefd64620726acc6
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
Backed out 5 changesets (bug 1520957) failing browser_webconsole... tests CLOSED TREE Backed out changeset 6371d4b4ff1e (bug 1520957) Backed out changeset 862c4fc3b6ab (bug 1520957) Backed out changeset 86defe96c1bb (bug 1520957) Backed out changeset 4655b2d86e56 (bug 1520957) Backed out changeset 67db08315e4b (bug 1520957)
devtools/client/debugger/new/dist/debugger.css
devtools/client/debugger/new/dist/vendors.js
devtools/client/debugger/new/src/actions/README.md
devtools/client/debugger/new/src/actions/project-text-search.js
devtools/client/debugger/new/src/actions/sources/newSources.js
devtools/client/debugger/new/src/actions/sources/prettyPrint.js
devtools/client/debugger/new/src/actions/sources/tests/__snapshots__/prettyPrint.spec.js.snap
devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
devtools/client/debugger/new/src/actions/types/index.js
devtools/client/debugger/new/src/client/README.md
devtools/client/debugger/new/src/client/firefox/tests/__snapshots__/commands.spec.js.snap
devtools/client/debugger/new/src/components/Editor/Footer.css
devtools/client/debugger/new/src/components/Editor/Footer.js
devtools/client/debugger/new/src/components/Editor/Tabs.css
devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/SearchBar.spec.js.snap
devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
devtools/client/debugger/new/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/__snapshots__/Breakpoint.spec.js.snap
devtools/client/debugger/new/src/components/shared/Button/styles/CloseButton.css
devtools/client/debugger/new/src/components/shared/Button/styles/CommandBarButton.css
devtools/client/debugger/new/src/components/shared/Button/styles/PaneToggleButton.css
devtools/client/debugger/new/src/components/shared/Dropdown.css
devtools/client/debugger/new/src/components/shared/ManagedTree.js
devtools/client/debugger/new/src/reducers/project-text-search.js
devtools/client/debugger/new/src/test/fixtures/README.md
devtools/client/debugger/new/src/types.js
devtools/client/debugger/new/src/utils/pause/mapScopes/README.md
devtools/client/debugger/new/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
devtools/client/debugger/new/test/mochitest/helpers.js
devtools/client/shared/components/reps/reps.js
--- a/devtools/client/debugger/new/dist/debugger.css
+++ b/devtools/client/debugger/new/dist/debugger.css
@@ -2375,17 +2375,17 @@ menuseparator {
 .source-footer > .commands > .blackboxed > .img.blackBox {
   background: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary,
 .source-footer .mapped-source,
 .source-footer .cursor-position {
   color: var(--theme-body-color);
-  padding-right: 2.5px;
+  padding-left: 2.5px;
 }
 
 .source-footer .mapped-source {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
--- a/devtools/client/debugger/new/dist/vendors.js
+++ b/devtools/client/debugger/new/dist/vendors.js
@@ -5021,17 +5021,17 @@ class Tree extends Component {
           // We can stop the propagation since click handler on the node can be
           // created in `renderItem`.
           e.stopPropagation();
 
           // Since the user just clicked the node, there's no need to check if
           // it should be scrolled into view.
           this._focus(item, { preventAutoScroll: true });
           if (this.props.isExpanded(item)) {
-            this.props.onCollapse(item, e.altKey);
+            this.props.onCollapse(item);
           } else {
             this.props.onExpand(item, e.altKey);
           }
         }
       });
     });
 
     const style = Object.assign({}, this.props.style || {}, {
deleted file mode 100644
--- a/devtools/client/debugger/new/src/actions/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
-## Actions
-
-### Best Practices
-
-#### Scheduling Async Actions
-
-There are several use-cases with async actions that involve scheduling:
-
-* we do one action and cancel subsequent actions
-* we do one action and subsequent calls wait on the initial call
-* we start an action and show a loading state
-
-If you want to wait on subsequent calls you need to store action promises.
-[ex][req]
-
-If you just want to cancel subsequent calls, you can keep track of a pending
-state in the store. [ex][state]
-
-The advantage of adding the pending state to the store is that we can use that
-in the UI:
-
-* disable/hide the pretty print button
-* show a progress ui
-
-[req]: https://github.com/devtools-html/debugger.html/blob/master/src/actions/sources/loadSourceText.js
-[state]: https://github.com/devtools-html/debugger.html/blob/master/src/reducers/sources.js
--- a/devtools/client/debugger/new/src/actions/project-text-search.js
+++ b/devtools/client/debugger/new/src/actions/project-text-search.js
@@ -25,16 +25,20 @@ import type { SearchOperation } from "..
 export function addSearchQuery(query: string): Action {
   return { type: "ADD_QUERY", query };
 }
 
 export function addOngoingSearch(ongoingSearch: SearchOperation): Action {
   return { type: "ADD_ONGOING_SEARCH", ongoingSearch };
 }
 
+export function clearSearchQuery(): Action {
+  return { type: "CLEAR_QUERY" };
+}
+
 export function addSearchResult(
   sourceId: string,
   filepath: string,
   matches: Object[]
 ): Action {
   return {
     type: "ADD_SEARCH_RESULT",
     result: { sourceId, filepath, matches }
--- a/devtools/client/debugger/new/src/actions/sources/newSources.js
+++ b/devtools/client/debugger/new/src/actions/sources/newSources.js
@@ -39,18 +39,17 @@ function createOriginalSource(
   return {
     url: originalUrl,
     relativeUrl: originalUrl,
     id: generatedToOriginalId(generatedSource.id, originalUrl),
     thread: generatedSource.thread,
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
-    loadedState: "unloaded",
-    introductionUrl: null
+    loadedState: "unloaded"
   };
 }
 
 function loadSourceMaps(sources: Source[]) {
   return async function({
     dispatch,
     sourceMaps
   }: ThunkArgs): Promise<Promise<Source>[]> {
--- a/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
+++ b/devtools/client/debugger/new/src/actions/sources/prettyPrint.js
@@ -37,18 +37,17 @@ export function createPrettySource(sourc
       url,
       relativeUrl: url,
       id,
       thread: "",
       isBlackBoxed: false,
       isPrettyPrinted: true,
       isWasm: false,
       contentType: "text/javascript",
-      loadedState: "loading",
-      introductionUrl: null
+      loadedState: "loading"
     };
 
     dispatch(({ type: "ADD_SOURCE", source: prettySource }: Action));
     dispatch(selectSource(prettySource.id));
 
     const { code, mappings } = await prettyPrint({ source, url });
     await sourceMaps.applySourceMap(source.id, url, code, mappings);
 
--- a/devtools/client/debugger/new/src/actions/sources/tests/__snapshots__/prettyPrint.spec.js.snap
+++ b/devtools/client/debugger/new/src/actions/sources/tests/__snapshots__/prettyPrint.spec.js.snap
@@ -1,16 +1,15 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`sources - pretty print returns a pretty source for a minified file 1`] = `
 Object {
   "contentType": "text/javascript",
   "error": undefined,
   "id": "base.js/originalSource-36c718d4bde9a75edb388ff7733efe7f",
-  "introductionUrl": null,
   "isBlackBoxed": false,
   "isPrettyPrinted": true,
   "isWasm": false,
   "loadedState": "loaded",
   "relativeUrl": "http://localhost:8000/examples/base.js:formatted",
   "sourceMapURL": undefined,
   "text": "undefined
 ",
--- a/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
+++ b/devtools/client/debugger/new/src/actions/tests/project-text-search.spec.js
@@ -56,16 +56,29 @@ describe("project text search", () => {
     const { dispatch, getState } = createStore();
     const mockQuery = "foo";
 
     dispatch(actions.addSearchQuery(mockQuery));
 
     expect(getTextSearchQuery(getState())).toEqual(mockQuery);
   });
 
+  it("should remove the  project text search query", () => {
+    const { dispatch, getState } = createStore();
+    const mockQuery = "foo";
+
+    dispatch(actions.addSearchQuery(mockQuery));
+    expect(getTextSearchQuery(getState())).toEqual(mockQuery);
+    dispatch(actions.updateSearchStatus("DONE"));
+    dispatch(actions.clearSearchQuery());
+    expect(getTextSearchQuery(getState())).toEqual("");
+    const status = getTextSearchStatus(getState());
+    expect(status).toEqual("INITIAL");
+  });
+
   it("should search all the loaded sources based on the query", async () => {
     const { dispatch, getState } = createStore(threadClient);
     const mockQuery = "foo";
     const source1 = makeSource("foo1");
     const source2 = makeSource("foo2");
 
     await dispatch(actions.newSource(source1));
     await dispatch(actions.newSource(source2));
--- a/devtools/client/debugger/new/src/actions/types/index.js
+++ b/devtools/client/debugger/new/src/actions/types/index.js
@@ -106,16 +106,17 @@ export type SourceTreeAction =
   | {| +type: "SET_FOCUSED_SOURCE_ITEM", item: FocusItem |};
 
 export type ProjectTextSearchAction =
   | {| +type: "ADD_QUERY", +query: string |}
   | {|
       +type: "ADD_SEARCH_RESULT",
       +result: ProjectTextSearchResult
     |}
+  | {| +type: "CLEAR_QUERY" |}
   | {| +type: "UPDATE_STATUS", +status: string |}
   | {| +type: "CLEAR_SEARCH_RESULTS" |}
   | {| +type: "ADD_ONGOING_SEARCH", +ongoingSearch: SearchOperation |}
   | {| +type: "CLEAR_SEARCH" |};
 
 export type FileTextSearchModifier =
   | "caseSensitive"
   | "wholeWord"
deleted file mode 100644
--- a/devtools/client/debugger/new/src/client/README.md
+++ /dev/null
@@ -1,53 +0,0 @@
-# Debugger Client
-
-The Debugger client is responsible for managing the communication between the
-client application and JS server.
-
-* When the server sends a notification to the client, the client receives an
-  "event" and notifies the application via redux actions.
-* When the application, wants to send a command to the server, it invokes
-  "commands" in the client.
-
-The Debugger supports a Firefox and a Chrome client, which lets it attach and
-debug Firefox, Chrome, and Node contexts. The clients are defined in
-`src/client` and have an `onConnect` function, and a `commands` and `events`
-module.
-
-Both clients implement client adapters for translating commands and events into
-JSON packets. The chrome client debugger adapter is defined in
-[chrome-remote-interface][chrome-remote-interface]. The Firefox client adapters
-are defined in two places:
-
-* The launchpad client adapter is maintained in the package
-  [devtools-connection][dt-connect].
-* The panel client adapter is maintained in
-  [devtools-client.js][devtools-client.js].
-
-## Firefox
-
-### Remote Debugger Protocol
-
-The [Remote Debugger Protocol][protocol] specifies the client / server API.
-
-### Interrupt
-
-When the client wants to add a breakpoint, it avoids race conditions by doing
-temporary pauses called interrupts.
-
-We want to do these interrupts transparently, so we've decided that the client
-should not notify the application that the thread has been paused or resumed.
-
-[protocol]: https://searchfox.org/mozilla-central/source/devtools/docs/backend/protocol.md
-[dt-connect]: https://github.com/devtools-html/devtools-core/tree/master/packages/devtools-connection
-[devtools-client.js]: https://searchfox.org/mozilla-central/source/devtools/shared/client/debugger-client.js
-
-## Chrome
-
-### Chrome Debugger Protocol
-
-The chrome debugger protocol is available [here][devtools-protocol-viewer]. And
-is maintained in the devtools-protocol [repo][devtools-protocol-gh].
-
-[chrome-remote-interface]: https://github.com/cyrus-and/chrome-remote-interface
-[devtools-protocol-viewer]: https://chromedevtools.github.io/devtools-protocol/
-[devtools-protocol-gh]: https://github.com/ChromeDevTools/devtools-protocol
deleted file mode 100644
--- a/devtools/client/debugger/new/src/client/firefox/tests/__snapshots__/commands.spec.js.snap
+++ /dev/null
@@ -1,66 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`firefox commands getProperties empty response 1`] = `
-Object {
-  "ownProperties": Object {},
-  "safeGetterValues": Object {},
-}
-`;
-
-exports[`firefox commands getProperties getter values 1`] = `
-Object {
-  "ownProperties": Object {
-    "foo": Object {
-      "value": "foo",
-    },
-    "obj": Object {
-      "enumerable": true,
-      "value": "getter",
-      "writable": false,
-    },
-  },
-  "safeGetterValues": Object {
-    "obj": Object {
-      "enumerable": true,
-      "getterValue": "getter",
-      "writable": false,
-    },
-  },
-}
-`;
-
-exports[`firefox commands getProperties new getter values 1`] = `
-Object {
-  "ownProperties": Object {
-    "foo": Object {
-      "value": "foo",
-    },
-    "obj": Object {
-      "enumerable": true,
-      "value": "getter",
-      "writable": false,
-    },
-  },
-  "safeGetterValues": Object {
-    "obj": Object {
-      "enumerable": true,
-      "getterValue": "getter",
-      "writable": false,
-    },
-  },
-}
-`;
-
-exports[`firefox commands getProperties simple properties 1`] = `
-Object {
-  "ownProperties": Object {
-    "foo": Object {
-      "value": "foo",
-    },
-    "obj": Object {
-      "value": "obj",
-    },
-  },
-  "safeGetterValues": Object {},
-}
-`;
--- a/devtools/client/debugger/new/src/components/Editor/Footer.css
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.css
@@ -81,17 +81,17 @@
 .source-footer > .commands > .blackboxed > .img.blackBox {
   background: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary,
 .source-footer .mapped-source,
 .source-footer .cursor-position {
   color: var(--theme-body-color);
-  padding-right: 2.5px;
+  padding-left: 2.5px;
 }
 
 .source-footer .mapped-source {
   white-space: nowrap;
   overflow: hidden;
   text-overflow: ellipsis;
 }
 
--- a/devtools/client/debugger/new/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/new/src/components/Editor/Footer.js
@@ -234,18 +234,18 @@ class SourceFooter extends PureComponent
     if (!shouldShowFooter(selectedSource, horizontal)) {
       return null;
     }
 
     return (
       <div className="source-footer">
         {this.renderCommands()}
         {this.renderSourceSummary()}
+        {this.renderToggleButton()}
         {this.renderCursorPosition()}
-        {this.renderToggleButton()}
       </div>
     );
   }
 }
 
 const mapStateToProps = state => {
   const selectedSource = getSelectedSource(state);
 
--- a/devtools/client/debugger/new/src/components/Editor/Tabs.css
+++ b/devtools/client/debugger/new/src/components/Editor/Tabs.css
@@ -33,27 +33,29 @@
 }
 
 .source-tabs {
   max-width: calc(100% - 80px);
   align-self: flex-start;
 }
 
 .source-tab {
+  border-left: 1px solid transparent;
+  border-right: 1px solid transparent;
   display: inline-flex;
   align-items: center;
   position: relative;
+  transition: all 0.15s ease;
   min-width: 40px;
   max-width: 100%;
   overflow: hidden;
   padding: 5px;
   cursor: default;
   height: 30px;
   font-size: 12px;
-  background-color: transparent;
 }
 
 .source-tab::before {
   content: "";
   position: absolute;
   top: 0;
   left: 0;
   width: 100%;
@@ -76,16 +78,19 @@
   background: var(--tab-line-hover-color);
   opacity: 1;
   transform: scaleX(1);
 }
 
 .source-tab.active {
   color: var(--theme-toolbar-selected-color);
   border-bottom-color: transparent;
+  border-left: 1px solid var(--theme-splitter-color);
+  border-right: 1px solid var(--theme-splitter-color);
+  background-color: var(--theme-body-background);
 }
 
 .source-tab.active::before {
   background: var(--tab-line-selected-color);
   opacity: 1;
   transform: scaleX(1);
 }
 
@@ -125,16 +130,31 @@
 .source-tab .blackBox path {
   fill: var(--theme-textbox-box-shadow);
 }
 
 .theme-dark .source-tab .blackBox circle {
   fill: var(--theme-body-color);
 }
 
+img.moreTabs {
+  mask: url(/images/command-chevron.svg) no-repeat;
+  mask-size: 100%;
+  width: 12px;
+  height: 12px;
+  display: block;
+  background: var(--theme-body-color);
+  margin-left: 6px;
+}
+
+html[dir="rtl"] .img.moreTabs {
+  transform: rotate(180deg);
+  margin-right: 6px;
+}
+
 .source-tab .filename {
   white-space: nowrap;
   text-overflow: ellipsis;
   overflow: hidden;
   padding: 0 4px;
   align-self: center;
   margin-bottom: 1px;
 }
@@ -148,16 +168,11 @@
   visibility: hidden;
   line-height: 0;
 }
 
 .source-tab.active .close-btn {
   visibility: visible;
 }
 
-.source-tab.active .close {
-  visibility: visible;
-  background-color: var(--theme-toolbar-selected-color);
-}
-
 .source-tab:hover .close-btn {
   visibility: visible;
 }
--- a/devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/Footer.spec.js.snap
@@ -1,39 +1,39 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`SourceFooter Component default case should render 1`] = `
 <div
   className="source-footer"
 >
+  <PaneToggleButton
+    collapsed={true}
+    horizontal={false}
+    key="toggle"
+    position="end"
+  />
   <span
     className="cursor-position"
     title="(Line 2, column 2)"
   >
     (2, 2)
   </span>
-  <PaneToggleButton
-    collapsed={true}
-    horizontal={false}
-    key="toggle"
-    position="end"
-  />
 </div>
 `;
 
 exports[`SourceFooter Component move cursor should render new cursor position 1`] = `
 <div
   className="source-footer"
 >
+  <PaneToggleButton
+    collapsed={true}
+    horizontal={false}
+    key="toggle"
+    position="end"
+  />
   <span
     className="cursor-position"
     title="(Line 6, column 11)"
   >
     (6, 11)
   </span>
-  <PaneToggleButton
-    collapsed={true}
-    horizontal={false}
-    key="toggle"
-    position="end"
-  />
 </div>
 `;
--- a/devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/SearchBar.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/Editor/tests/__snapshots__/SearchBar.spec.js.snap
@@ -51,25 +51,23 @@ exports[`SearchBar should render 1`] = `
         tooltip="Case sensitive"
       />
       <SearchModBtn
         className="whole-word-btn"
         modVal="wholeWord"
         svgName="whole-word-match"
         tooltip="Whole word"
       />
-      <React.Fragment>
-        <span
-          className="pipe-divider"
-        />
-        <CloseButton
-          buttonClass="big"
-          handleClick={[Function]}
-        />
-      </React.Fragment>
+      <span
+        className="pipe-divider"
+      />
+      <CloseButton
+        buttonClass="big"
+        handleClick={[Function]}
+      />
     </div>
   </div>
 </div>
 `;
 
 exports[`doSearch should complete a search 1`] = `"query"`;
 
 exports[`showErrorEmoji false if no query + no results 1`] = `
@@ -124,25 +122,23 @@ exports[`showErrorEmoji false if no quer
         tooltip="Case sensitive"
       />
       <SearchModBtn
         className="whole-word-btn"
         modVal="wholeWord"
         svgName="whole-word-match"
         tooltip="Whole word"
       />
-      <React.Fragment>
-        <span
-          className="pipe-divider"
-        />
-        <CloseButton
-          buttonClass="big"
-          handleClick={[Function]}
-        />
-      </React.Fragment>
+      <span
+        className="pipe-divider"
+      />
+      <CloseButton
+        buttonClass="big"
+        handleClick={[Function]}
+      />
     </div>
   </div>
 </div>
 `;
 
 exports[`showErrorEmoji false if query + results 1`] = `
 <div
   className="search-bar"
@@ -195,25 +191,23 @@ exports[`showErrorEmoji false if query +
         tooltip="Case sensitive"
       />
       <SearchModBtn
         className="whole-word-btn"
         modVal="wholeWord"
         svgName="whole-word-match"
         tooltip="Whole word"
       />
-      <React.Fragment>
-        <span
-          className="pipe-divider"
-        />
-        <CloseButton
-          buttonClass="big"
-          handleClick={[Function]}
-        />
-      </React.Fragment>
+      <span
+        className="pipe-divider"
+      />
+      <CloseButton
+        buttonClass="big"
+        handleClick={[Function]}
+      />
     </div>
   </div>
 </div>
 `;
 
 exports[`showErrorEmoji true if query + no results 1`] = `
 <div
   className="search-bar"
@@ -266,21 +260,19 @@ exports[`showErrorEmoji true if query + 
         tooltip="Case sensitive"
       />
       <SearchModBtn
         className="whole-word-btn"
         modVal="wholeWord"
         svgName="whole-word-match"
         tooltip="Whole word"
       />
-      <React.Fragment>
-        <span
-          className="pipe-divider"
-        />
-        <CloseButton
-          buttonClass="big"
-          handleClick={[Function]}
-        />
-      </React.Fragment>
+      <span
+        className="pipe-divider"
+      />
+      <CloseButton
+        buttonClass="big"
+        handleClick={[Function]}
+      />
     </div>
   </div>
 </div>
 `;
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTree.js
@@ -78,16 +78,18 @@ type Props = {
 type State = {
   parentMap: ParentMap,
   sourceTree: TreeDirectory,
   uncollapsedTree: TreeDirectory,
   listItems?: any,
   highlightItems?: any
 };
 
+type SetExpanded = (item: TreeNode, expanded: boolean, altKey: boolean) => void;
+
 class SourcesTree extends Component<Props, State> {
   mounted: boolean;
 
   constructor(props: Props) {
     super(props);
     const { debuggeeUrl, sources, projectRoot } = this.props;
 
     this.state = createTree({
@@ -259,31 +261,33 @@ class SourcesTree extends Component<Prop
     return sourceTree.contents;
   };
 
   renderItem = (
     item: TreeNode,
     depth: number,
     focused: boolean,
     _,
-    expanded: boolean
+    expanded: boolean,
+    { setExpanded }: { setExpanded: SetExpanded }
   ) => {
     const { debuggeeUrl, projectRoot } = this.props;
 
     return (
       <SourcesTreeItem
         item={item}
         depth={depth}
         focused={focused}
         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;
 
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/SourcesTreeItem.js
@@ -35,16 +35,17 @@ type Props = {
   projectRoot: string,
   source: ?Source,
   item: TreeNode,
   depth: number,
   focused: boolean,
   expanded: boolean,
   hasMatchingGeneratedSource: boolean,
   hasSiblingOfSameName: boolean,
+  setExpanded: (TreeNode, boolean, boolean) => void,
   focusItem: TreeNode => void,
   selectItem: TreeNode => void,
   clearProjectDirectoryRoot: typeof actions.clearProjectDirectoryRoot,
   setProjectDirectoryRoot: typeof actions.setProjectDirectoryRoot
 };
 
 type State = {};
 
@@ -77,21 +78,23 @@ class SourceTreeItem extends Component<P
     if (source) {
       return <SourceIcon source={source} />;
     }
 
     return null;
   }
 
   onClick = (e: MouseEvent) => {
-    const { item, focusItem, selectItem } = this.props;
+    const { expanded, item, focusItem, setExpanded, selectItem } = this.props;
 
     focusItem(item);
 
-    if (!isDirectory(item)) {
+    if (isDirectory(item)) {
+      setExpanded(item, !!expanded, e.altKey);
+    } else {
       selectItem(item);
     }
   };
 
   onContextMenu = (event: Event, item: TreeNode) => {
     const copySourceUri2Label = L10N.getStr("copySourceUri2");
     const copySourceUri2Key = L10N.getStr("copySourceUri2.accesskey");
     const setDirectoryRootLabel = L10N.getStr("setDirectoryRoot.label");
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/tests/SourcesTreeItem.spec.js
@@ -231,43 +231,50 @@ describe("SourceTreeItem", () => {
       const { node } = render({
         projectRoot: "root/"
       });
       expect(node).toMatchSnapshot();
     });
 
     it("should focus on and select item on click", async () => {
       const event = { event: "click" };
+      const setExpanded = jest.fn();
       const selectItem = jest.fn();
       const { component, instance, props } = render({
         depth: 1,
         focused: true,
         expanded: false,
+        setExpanded,
         selectItem
       });
 
       const { item } = instance.props;
       component.simulate("click", event);
       await component.simulate("keydown", { keyCode: 13 });
       expect(props.selectItem).toHaveBeenCalledWith(item);
+      expect(setExpanded).not.toHaveBeenCalled();
     });
 
-    it("should focus on directory on click", async () => {
+    it("should focus on and expand directory on click", async () => {
+      const setExpanded = jest.fn();
       const selectItem = jest.fn();
 
-      const { component, props } = render({
+      const { component, instance, props } = render({
         item: createMockDirectory(),
         source: null,
         depth: 1,
         focused: true,
         expanded: false,
+        setExpanded,
         selectItem
       });
 
+      const { item } = instance.props;
       component.simulate("click", { event: "click" });
+      expect(setExpanded).toHaveBeenCalledWith(item, false, undefined);
       expect(props.selectItem).not.toHaveBeenCalled();
     });
   });
 });
 
 function generateDefaults(overrides) {
   const source = createSource({
     id: "server1.conn13.child1/39",
@@ -283,16 +290,17 @@ function generateDefaults(overrides) {
   return {
     expanded: false,
     item,
     source,
     debuggeeUrl: "http://mdn.com",
     projectRoot: "",
     clearProjectDirectoryRoot: jest.fn(),
     setProjectDirectoryRoot: jest.fn(),
+    setExpanded: jest.fn(),
     selectItem: jest.fn(),
     focusItem: jest.fn(),
     ...overrides
   };
 }
 
 function render(overrides = {}) {
   const props = generateDefaults(overrides);
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTree.spec.js.snap
@@ -1,62 +1,255 @@
 // 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"
   key="pane"
 >
   <div
-    className="no-sources-message"
-    key="empty"
+    className="sources-list"
+    key="tree"
+    onKeyDown={[Function]}
   >
-    This page has no sources
+    <ManagedTree
+      autoExpandAll={false}
+      autoExpandDepth={0}
+      expanded={
+        Array [
+          "four.js",
+          "five.js",
+          "six.js",
+        ]
+      }
+      getChildren={[Function]}
+      getParent={[Function]}
+      getPath={[Function]}
+      getRoots={[Function]}
+      itemHeight={21}
+      key="full"
+      onCollapse={[Function]}
+      onExpand={[Function]}
+      onFocus={[Function]}
+      preventBlur={true}
+      renderItem={[Function]}
+    />
   </div>
 </div>
 `;
 
 exports[`SourcesTree Should show the tree with nothing expanded 1`] = `
 <div
   className="sources-pane"
   key="pane"
 >
   <div
-    className="no-sources-message"
-    key="empty"
+    className="sources-list"
+    key="tree"
+    onKeyDown={[Function]}
   >
-    This page has no sources
+    <ManagedTree
+      autoExpandAll={false}
+      autoExpandDepth={1}
+      getChildren={[Function]}
+      getParent={[Function]}
+      getPath={[Function]}
+      getRoots={[Function]}
+      itemHeight={21}
+      key="full"
+      onCollapse={[Function]}
+      onExpand={[Function]}
+      onFocus={[Function]}
+      preventBlur={true}
+      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"
   key="pane"
 >
   <div
-    className="no-sources-message"
-    key="empty"
+    className="sources-list"
+    key="tree"
+    onKeyDown={[Function]}
   >
-    This page has no sources
+    <ManagedTree
+      autoExpandAll={false}
+      autoExpandDepth={0}
+      expanded={
+        Array [
+          "one.js",
+          "two.js",
+          "three.js",
+        ]
+      }
+      getChildren={[Function]}
+      getParent={[Function]}
+      getPath={[Function]}
+      getRoots={[Function]}
+      itemHeight={21}
+      key="full"
+      onCollapse={[Function]}
+      onExpand={[Function]}
+      onFocus={[Function]}
+      preventBlur={true}
+      renderItem={[Function]}
+    />
   </div>
 </div>
 `;
 
 exports[`SourcesTree on receiving new props updates highlighted items updates highlightItems if selectedSource changes 1`] = `
 <div
   className="sources-pane"
   key="pane"
 >
   <div
-    className="no-sources-message"
-    key="empty"
+    className="sources-list"
+    key="tree"
+    onKeyDown={[Function]}
   >
-    This page has no sources
+    <ManagedTree
+      autoExpandAll={false}
+      autoExpandDepth={1}
+      getChildren={[Function]}
+      getParent={[Function]}
+      getPath={[Function]}
+      getRoots={[Function]}
+      highlightItems={
+        Array [
+          Object {
+            "contents": Object {
+              "contentType": "",
+              "error": undefined,
+              "id": "server1.conn13.child1/41",
+              "isBlackBoxed": false,
+              "isPrettyPrinted": false,
+              "isWasm": false,
+              "loadedState": "unloaded",
+              "sourceMapURL": null,
+              "text": undefined,
+              "thread": "",
+              "url": "http://mdn.com/three.js",
+            },
+            "name": "three.js",
+            "path": "mdn.com/three.js",
+            "type": "source",
+          },
+          Object {
+            "contents": Array [
+              Object {
+                "contents": Object {
+                  "contentType": "",
+                  "error": undefined,
+                  "id": "server1.conn13.child1/42",
+                  "isBlackBoxed": false,
+                  "isPrettyPrinted": false,
+                  "isWasm": false,
+                  "loadedState": "unloaded",
+                  "sourceMapURL": "data:application/json?charset=utf?dsffewrsf",
+                  "text": undefined,
+                  "thread": "",
+                  "url": "http://mdn.com/four.js",
+                },
+                "name": "four.js",
+                "path": "mdn.com/four.js",
+                "type": "source",
+              },
+              Object {
+                "contents": Object {
+                  "contentType": "",
+                  "error": undefined,
+                  "id": "server1.conn13.child1/42/originalSource-sha",
+                  "isBlackBoxed": false,
+                  "isPrettyPrinted": false,
+                  "isWasm": false,
+                  "loadedState": "unloaded",
+                  "sourceMapURL": null,
+                  "text": undefined,
+                  "thread": "",
+                  "url": "http://mdn.com/four.js",
+                },
+                "name": "four.js",
+                "path": "mdn.com/four.js",
+                "type": "source",
+              },
+              Object {
+                "contents": Object {
+                  "contentType": "",
+                  "error": undefined,
+                  "id": "server1.conn13.child1/39",
+                  "isBlackBoxed": false,
+                  "isPrettyPrinted": false,
+                  "isWasm": false,
+                  "loadedState": "unloaded",
+                  "sourceMapURL": null,
+                  "text": undefined,
+                  "thread": "",
+                  "url": "http://mdn.com/one.js",
+                },
+                "name": "one.js",
+                "path": "mdn.com/one.js",
+                "type": "source",
+              },
+              Object {
+                "contents": Object {
+                  "contentType": "",
+                  "error": undefined,
+                  "id": "server1.conn13.child1/41",
+                  "isBlackBoxed": false,
+                  "isPrettyPrinted": false,
+                  "isWasm": false,
+                  "loadedState": "unloaded",
+                  "sourceMapURL": null,
+                  "text": undefined,
+                  "thread": "",
+                  "url": "http://mdn.com/three.js",
+                },
+                "name": "three.js",
+                "path": "mdn.com/three.js",
+                "type": "source",
+              },
+              Object {
+                "contents": Object {
+                  "contentType": "",
+                  "error": undefined,
+                  "id": "server1.conn13.child1/40",
+                  "isBlackBoxed": false,
+                  "isPrettyPrinted": false,
+                  "isWasm": false,
+                  "loadedState": "unloaded",
+                  "sourceMapURL": null,
+                  "text": undefined,
+                  "thread": "",
+                  "url": "http://mdn.com/two.js",
+                },
+                "name": "two.js",
+                "path": "mdn.com/two.js",
+                "type": "source",
+              },
+            ],
+            "name": "mdn.com",
+            "path": "mdn.com",
+            "type": "directory",
+          },
+        ]
+      }
+      itemHeight={21}
+      key="full"
+      onCollapse={[Function]}
+      onExpand={[Function]}
+      onFocus={[Function]}
+      preventBlur={true}
+      renderItem={[Function]}
+    />
   </div>
 </div>
 `;
 
 exports[`SourcesTree with custom root renders custom root source list 1`] = `
 <div
   className="sources-pane sources-list-custom-root"
   key="pane"
@@ -79,20 +272,35 @@ exports[`SourcesTree with custom root re
       <span
         className="sources-clear-root-label"
       >
         mdn.com
       </span>
     </button>
   </div>
   <div
-    className="no-sources-message"
-    key="empty"
+    className="sources-list"
+    key="tree"
+    onKeyDown={[Function]}
   >
-    This directory root has no sources
+    <ManagedTree
+      autoExpandAll={false}
+      autoExpandDepth={1}
+      getChildren={[Function]}
+      getParent={[Function]}
+      getPath={[Function]}
+      getRoots={[Function]}
+      itemHeight={21}
+      key="full"
+      onCollapse={[Function]}
+      onExpand={[Function]}
+      onFocus={[Function]}
+      preventBlur={true}
+      renderItem={[Function]}
+    />
   </div>
 </div>
 `;
 
 exports[`SourcesTree with custom root renders empty custom root source list 1`] = `
 <div
   className="sources-pane sources-list-custom-root"
   key="pane"
--- a/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/PrimaryPanes/tests/__snapshots__/SourcesTreeItem.spec.js.snap
@@ -67,31 +67,33 @@ Object {
           "url": undefined,
         },
         "name": "one.js",
         "path": "http://mdn.com/one.js",
         "type": "source",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -114,16 +116,17 @@ Object {
               },
               "name": "one.js",
               "path": "http://mdn.com/one.js",
               "type": "source",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -174,16 +177,17 @@ Object {
               (mapped)
             </span>
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "hasMatchingGeneratedSource": true,
     "item": Object {
@@ -200,16 +204,17 @@ Object {
         "url": undefined,
       },
       "name": "one.js",
       "path": "http://mdn.com/one.js",
       "type": "source",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -269,31 +274,33 @@ Object {
           "url": undefined,
         },
         "name": "root",
         "path": "root",
         "type": "source",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -316,16 +323,17 @@ Object {
               },
               "name": "root",
               "path": "root",
               "type": "source",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -358,16 +366,17 @@ Object {
             root
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 0,
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
@@ -384,16 +393,17 @@ Object {
         "url": undefined,
       },
       "name": "root",
       "path": "root",
       "type": "source",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -442,31 +452,33 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "http://mdn.com",
         "path": "root",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -478,16 +490,17 @@ Object {
               "contents": Array [],
               "name": "http://mdn.com",
               "path": "root",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -520,31 +533,33 @@ Object {
             http://mdn.com
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 0,
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "http://mdn.com",
       "path": "root",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -594,31 +609,33 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "http://mdn.com",
         "path": "root",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -631,16 +648,17 @@ Object {
               "contents": Array [],
               "name": "http://mdn.com",
               "path": "root",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -673,32 +691,34 @@ Object {
             http://mdn.com
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 0,
     "expanded": false,
     "focusItem": [MockFunction],
     "focused": true,
     "item": Object {
       "contents": Array [],
       "name": "http://mdn.com",
       "path": "root",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -748,20 +768,22 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "folder",
         "path": "folder/",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -774,16 +796,17 @@ Object {
               "contents": Array [],
               "name": "folder",
               "path": "folder/",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node focused"
@@ -803,32 +826,34 @@ Object {
             folder
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": true,
     "item": Object {
       "contents": Array [],
       "name": "folder",
       "path": "folder/",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for angular item 1`] = `
 Object {
@@ -865,31 +890,33 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "ng://",
         "path": "ng://",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -900,16 +927,17 @@ Object {
               "contents": Array [],
               "name": "ng://",
               "path": "ng://",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -942,30 +970,32 @@ Object {
             Angular
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "ng://",
       "path": "ng://",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -1013,20 +1043,22 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "folder",
         "path": "folder/",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1037,16 +1069,17 @@ Object {
               "contents": Array [],
               "name": "folder",
               "path": "folder/",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
@@ -1066,30 +1099,32 @@ Object {
             folder
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "folder",
       "path": "folder/",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for folder with expanded arrow 1`] = `
 Object {
@@ -1128,20 +1163,22 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "folder",
         "path": "folder/",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": null,
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1154,16 +1191,17 @@ Object {
               "contents": Array [],
               "name": "folder",
               "path": "folder/",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={null}
         />,
         "_forcedUpdate": false,
         "_instance": [Circular],
         "_newState": null,
         "_rendered": <div
           className="node"
@@ -1183,32 +1221,34 @@ Object {
             folder
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": true,
     "focusItem": [MockFunction],
     "focused": false,
     "item": Object {
       "contents": Array [],
       "name": "folder",
       "path": "folder/",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": null,
   },
 }
 `;
 
 exports[`SourceTreeItem renderItem should show icon for moz-extension item 1`] = `
 Object {
@@ -1246,31 +1286,33 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
         "path": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1282,16 +1324,17 @@ Object {
               "contents": Array [],
               "name": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
               "path": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -1324,31 +1367,33 @@ Object {
             moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 0,
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
       "path": "moz-extension://e37c3c08-beac-a04b-8032-c4f699a1a856",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -1396,31 +1441,33 @@ Object {
       "item": Object {
         "contents": Array [],
         "name": "webpack://",
         "path": "webpack://",
         "type": "directory",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1431,16 +1478,17 @@ Object {
               "contents": Array [],
               "name": "webpack://",
               "path": "webpack://",
               "type": "directory",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -1473,30 +1521,32 @@ Object {
             Webpack
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Array [],
       "name": "webpack://",
       "path": "webpack://",
       "type": "directory",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -1568,31 +1618,33 @@ Object {
           "url": undefined,
         },
         "name": "one.js",
         "path": "http://mdn.com/one.js",
         "type": "source",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1614,16 +1666,17 @@ Object {
               },
               "name": "one.js",
               "path": "http://mdn.com/one.js",
               "type": "source",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -1669,16 +1722,17 @@ Object {
             one.js
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "expanded": false,
     "focusItem": [MockFunction],
     "item": Object {
       "contents": Object {
@@ -1694,16 +1748,17 @@ Object {
         "url": undefined,
       },
       "name": "one.js",
       "path": "http://mdn.com/one.js",
       "type": "source",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
@@ -1776,31 +1831,33 @@ Object {
           "text": undefined,
           "url": "http://mdn.com/one.js",
         },
         "name": "one.js",
         "path": "mdn.com/one.js",
       },
       "projectRoot": "",
       "selectItem": [MockFunction],
+      "setExpanded": [MockFunction],
       "setProjectDirectoryRoot": [MockFunction],
       "source": Object {
         "contentType": "",
         "error": undefined,
         "id": "server1.conn13.child1/39",
         "isBlackBoxed": false,
         "isPrettyPrinted": false,
         "isWasm": false,
         "loadedState": "unloaded",
         "sourceMapURL": undefined,
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
     },
     "refs": Object {},
+    "setState": [Function],
     "state": null,
     "updater": Updater {
       "_callbacks": Array [],
       "_renderer": ReactShallowRenderer {
         "_context": Object {},
         "_element": <SourceTreeItem
           clearProjectDirectoryRoot={[MockFunction]}
           debuggeeUrl="http://mdn.com"
@@ -1823,16 +1880,17 @@ Object {
                 "url": "http://mdn.com/one.js",
               },
               "name": "one.js",
               "path": "mdn.com/one.js",
             }
           }
           projectRoot=""
           selectItem={[MockFunction]}
+          setExpanded={[MockFunction]}
           setProjectDirectoryRoot={[MockFunction]}
           source={
             Object {
               "contentType": "",
               "error": undefined,
               "id": "server1.conn13.child1/39",
               "isBlackBoxed": false,
               "isPrettyPrinted": false,
@@ -1878,16 +1936,17 @@ Object {
             one.js
              
           </span>
         </div>,
         "_rendering": false,
         "_updater": [Circular],
       },
     },
+    Symbol(enzyme.__setState__): [Function],
   },
   "props": Object {
     "clearProjectDirectoryRoot": [MockFunction],
     "debuggeeUrl": "http://mdn.com",
     "depth": 1,
     "expanded": false,
     "focusItem": [MockFunction],
     "focused": true,
@@ -1904,16 +1963,17 @@ Object {
         "text": undefined,
         "url": "http://mdn.com/one.js",
       },
       "name": "one.js",
       "path": "mdn.com/one.js",
     },
     "projectRoot": "",
     "selectItem": [MockFunction],
+    "setExpanded": [MockFunction],
     "setProjectDirectoryRoot": [MockFunction],
     "source": Object {
       "contentType": "",
       "error": undefined,
       "id": "server1.conn13.child1/39",
       "isBlackBoxed": false,
       "isPrettyPrinted": false,
       "isWasm": false,
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -63,20 +63,18 @@ class Breakpoint extends PureComponent<P
 
   onDoubleClick = () => {
     const { breakpoint, openConditionalPanel } = this.props;
     if (breakpoint.condition) {
       openConditionalPanel(breakpoint.selectedLocation);
     }
   };
 
-  selectBreakpoint = event => {
+  selectBreakpoint = () => {
     const { breakpoint, selectSpecificLocation } = this.props;
-
-    event.preventDefault();
     selectSpecificLocation(breakpoint.selectedLocation);
   };
 
   removeBreakpoint = event => {
     const { breakpoint, removeBreakpoint } = this.props;
 
     event.stopPropagation();
     removeBreakpoint(breakpoint.selectedLocation);
@@ -156,17 +154,16 @@ class Breakpoint extends PureComponent<P
           className="breakpoint-checkbox"
           checked={!breakpoint.disabled}
           onChange={this.handleBreakpointCheckbox}
           onClick={ev => ev.stopPropagation()}
         />
         <label
           htmlFor={breakpoint.id}
           className="breakpoint-label cm-s-mozilla"
-          onClick={this.selectBreakpoint}
           title={this.getBreakpointText()}
         >
           <span dangerouslySetInnerHTML={this.highlightText()} />
         </label>
         <div className="breakpoint-line-close">
           <div className="breakpoint-line">{this.getBreakpointLocation()}</div>
           <CloseButton
             handleClick={e => this.removeBreakpoint(e)}
--- a/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/__snapshots__/Breakpoint.spec.js.snap
+++ b/devtools/client/debugger/new/src/components/SecondaryPanes/Breakpoints/tests/__snapshots__/Breakpoint.spec.js.snap
@@ -11,17 +11,16 @@ exports[`Breakpoint disabled 1`] = `
     checked={false}
     className="breakpoint-checkbox"
     onChange={[Function]}
     onClick={[Function]}
     type="checkbox"
   />
   <label
     className="breakpoint-label cm-s-mozilla"
-    onClick={[Function]}
   >
     <span
       dangerouslySetInnerHTML={
         Object {
           "__html": "",
         }
       }
     />
@@ -53,17 +52,16 @@ exports[`Breakpoint paused at a differen
     checked={true}
     className="breakpoint-checkbox"
     onChange={[Function]}
     onClick={[Function]}
     type="checkbox"
   />
   <label
     className="breakpoint-label cm-s-mozilla"
-    onClick={[Function]}
   >
     <span
       dangerouslySetInnerHTML={
         Object {
           "__html": "",
         }
       }
     />
@@ -95,17 +93,16 @@ exports[`Breakpoint paused at a generate
     checked={true}
     className="breakpoint-checkbox"
     onChange={[Function]}
     onClick={[Function]}
     type="checkbox"
   />
   <label
     className="breakpoint-label cm-s-mozilla"
-    onClick={[Function]}
   >
     <span
       dangerouslySetInnerHTML={
         Object {
           "__html": "",
         }
       }
     />
@@ -137,17 +134,16 @@ exports[`Breakpoint paused at an origina
     checked={true}
     className="breakpoint-checkbox"
     onChange={[Function]}
     onClick={[Function]}
     type="checkbox"
   />
   <label
     className="breakpoint-label cm-s-mozilla"
-    onClick={[Function]}
   >
     <span
       dangerouslySetInnerHTML={
         Object {
           "__html": "",
         }
       }
     />
@@ -179,17 +175,16 @@ exports[`Breakpoint simple 1`] = `
     checked={true}
     className="breakpoint-checkbox"
     onChange={[Function]}
     onClick={[Function]}
     type="checkbox"
   />
   <label
     className="breakpoint-label cm-s-mozilla"
-    onClick={[Function]}
   >
     <span
       dangerouslySetInnerHTML={
         Object {
           "__html": "",
         }
       }
     />
--- a/devtools/client/debugger/new/src/components/shared/Button/styles/CloseButton.css
+++ b/devtools/client/debugger/new/src/components/shared/Button/styles/CloseButton.css
@@ -15,24 +15,21 @@
 }
 
 .close-btn .close {
   mask: url(/images/close.svg) no-repeat;
   mask-size: 100%;
   background-color: var(--theme-comment);
   width: 8px;
   height: 8px;
+  transition: all 0.15s ease-in-out;
   padding: 0;
   margin-top: 0;
 }
 
-.close-btn .close:hover {
-  background-color: var(--theme-comment);
-}
-
 .close-btn:hover .img.close {
   background-color: white;
 }
 
 .close-btn:hover {
   background-color: var(--theme-selection-background);
 }
 
--- a/devtools/client/debugger/new/src/components/shared/Button/styles/CommandBarButton.css
+++ b/devtools/client/debugger/new/src/components/shared/Button/styles/CommandBarButton.css
@@ -6,17 +6,16 @@
   appearance: none;
   background: transparent;
   border: none;
   display: inline-block;
   text-align: center;
   position: relative;
   padding: 0px 5px;
   fill: currentColor;
-  min-width: 30px;
 }
 
 .command-bar-button:disabled {
   opacity: 0.8;
   cursor: default;
 }
 
 .command-bar-button:not(.disabled):hover {
--- a/devtools/client/debugger/new/src/components/shared/Button/styles/PaneToggleButton.css
+++ b/devtools/client/debugger/new/src/components/shared/Button/styles/PaneToggleButton.css
@@ -1,20 +1,20 @@
 /* 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/>. */
 
 .toggle-button {
   transform: translate(0, 0px);
   transition: transform 0.25s ease-in-out;
   padding: 5px;
-  -webkit-border-start: 1px solid;
-  border-inline-start: 1px solid;
-  border-image: var(--separator-border-image) 1 1;
-  height: inherit;
+}
+
+.toggle-button .togglePanes {
+  vertical-align: -2px;
 }
 
 .toggle-button svg {
   fill: var(--theme-comment);
   vertical-align: 0;
 }
 
 :root.theme-dark .toggle-button svg {
--- a/devtools/client/debugger/new/src/components/shared/Dropdown.css
+++ b/devtools/client/debugger/new/src/components/shared/Dropdown.css
@@ -34,30 +34,16 @@ html[dir="rtl"] .dropdown {
   border: none;
   padding: 0;
   font-weight: 100;
   font-size: 14px;
   height: 100%;
   width: 24px;
 }
 
-.more-tabs {
-  mask: url(/images/command-chevron.svg) no-repeat;
-  mask-size: 100%;
-  width: 12px;
-  display: block;
-  background: var(--theme-body-color);
-  margin-left: 6px;
-}
-
-html[dir="rtl"] .img.more-tabs {
-  transform: rotate(180deg);
-  margin-right: 6px;
-}
-
 .dropdown li {
   transition: all 0.25s ease;
   padding: 2px 10px 10px 5px;
   overflow: hidden;
   height: 30px;
   text-overflow: ellipsis;
   white-space: nowrap;
   display: block;
--- a/devtools/client/debugger/new/src/components/shared/ManagedTree.js
+++ b/devtools/client/debugger/new/src/components/shared/ManagedTree.js
@@ -136,22 +136,18 @@ class ManagedTree extends Component<Prop
     const { expanded } = this.state;
     return (
       <div className="managed-tree">
         <Tree
           {...this.props}
           isExpanded={item => expanded.has(this.props.getPath(item))}
           focused={this.props.focused}
           getKey={this.props.getPath}
-          onExpand={(item, shouldIncludeChildren) =>
-            this.setExpanded(item, true, shouldIncludeChildren)
-          }
-          onCollapse={(item, shouldIncludeChildren) =>
-            this.setExpanded(item, false, shouldIncludeChildren)
-          }
+          onExpand={item => this.setExpanded(item, true, false)}
+          onCollapse={item => this.setExpanded(item, false, false)}
           onFocus={this.props.onFocus}
           renderItem={(...args) =>
             this.props.renderItem(...args, {
               setExpanded: this.setExpanded
             })
           }
         />
       </div>
--- a/devtools/client/debugger/new/src/reducers/project-text-search.js
+++ b/devtools/client/debugger/new/src/reducers/project-text-search.js
@@ -55,16 +55,23 @@ export function initialProjectTextSearch
 function update(
   state: ProjectTextSearchState = initialProjectTextSearchState(),
   action: Action
 ): ProjectTextSearchState {
   switch (action.type) {
     case "ADD_QUERY":
       return { ...state, query: action.query };
 
+    case "CLEAR_QUERY":
+      return {
+        ...state,
+        query: "",
+        status: statusType.initial
+      };
+
     case "ADD_SEARCH_RESULT":
       const results = state.results;
       if (action.result.matches.length === 0) {
         return state;
       }
 
       const result = {
         type: "RESULT",
deleted file mode 100644
--- a/devtools/client/debugger/new/src/test/fixtures/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## Fixtures
-
-Fixtures used for unit tests
--- a/devtools/client/debugger/new/src/types.js
+++ b/devtools/client/debugger/new/src/types.js
@@ -306,18 +306,17 @@ type BaseSource = {|
   +url: string,
   +thread: string,
   +sourceMapURL?: string,
   +isBlackBoxed: boolean,
   +isPrettyPrinted: boolean,
   +contentType?: string,
   +error?: string,
   +loadedState: "unloaded" | "loading" | "loaded",
-  +relativeUrl: string,
-  introductionUrl: ?string
+  +relativeUrl: string
 |};
 
 /**
  * JsSource
  *
  * @memberof types
  * @static
  */
deleted file mode 100644
--- a/devtools/client/debugger/new/src/utils/pause/mapScopes/README.md
+++ /dev/null
@@ -1,191 +0,0 @@
-# mapScopes
-
-The files in this directory manage the Devtools logic for taking the original
-JS file from the sourcemap and using the content, combined with source
-mappings, to provide an enhanced user experience for debugging the code.
-
-In this document, we'll refer to the files as either:
-
-* `original` - The code that the user wrote originally.
-* `generated` - The code actually executing in the engine, which may have been
-  transformed from the `original`, and if a bundler was used, may contain the
-  final output for several different `original` files.
-
-The enhancements implemented here break down into as two primary improvements:
-
-* Rendering the scopes as the user expects them, using the position in the
-  original file, rather than the content of the generated file.
-* Allowing users to interact with the code in the console as if it were the
-  original code, rather than the generated file.
-
-
-## Overall Approach
-
-The core goal of scope mapping is to parse the original and generated files,
-and then infer a correlation between the bindings in the original file,
-and the bindings in the generated file. This is correlation is done via the
-source mappings provided alongside the original code in the sourcemap.
-
-The overall steps break down into:
-
-
-### 1. Parsing
-
-First the generated and original files are parsed into ASTs, and then the ASTs
-are each traversed in order to generate a full picture of the scopes that are
-present in the file. This covers information for each binding in each scope,
-including all metadata about the declarations themselves, and all references
-to each of the bindings. The exact location of each binding reference is the
-most important factor because these will be used when working with sourcemaps.
-
-Importantly, this scope tree's structure also mirrors the structure of the
-scope data that the engine itself returns when paused.
-
-
-### 2. Generated Binding -> Grip Correlation
-
-When the engine pauses, we get back a full description of the current scope,
-as well as `grip` objects that can be used as proxy-like objects in order to
-access the actual values in the engine itself.
-
-With this data, along with the location where the engine is paused, we can
-use the AST-based scope information from step 1 to attach a line/column
-range to each `grip`. Using those mappings we have enough information to get
-the real engine value of any variable based on its position in the generated
-code, as long as it is in scope at the paused location in the generated file.
-
-The generated and engine scopes are correlated depth-first, because in some
-cases the scope data from the engine will be deeper that the data from the
-parsed code, meaning there are additional unknown parent scopes. This can
-happen, for instance, if the executing code is running inside of an `eval`
-since the parser only knows about the scopes inside of `eval`, and cannot
-know what context it is executed inside of.
-
-
-### 3. Original Binding -> Generated Binding Correlation
-
-Now that we can get the value of a generated location, we need to decide which
-generated location correlates with which original location. For each of
-the original bindings in each original scope, we iterate through the
-individual references to the binding in the file and:
-
-1. Use the sourcemap to convert the original location into a range on the
-   generated code.
-2. Filter the available bindings in the generated file down to those that
-   overlap with this generated range.
-3. Perform heuristics to decide if any of the bindings in the range appear
-   to be a valid and safe mapping.
-
-These steps allow us to build a datastructure that describes the scope
-as it is declared in the original file, while rendering the value using the
-`grip` objects, as returned from the engine itself.
-
-There is additional complexity here in the exact details of the heuristics
-mentioned above. See the later Heuristics sections diving into these details.
-
-During this phase, one additional task is performed, which is the construction
-of expressions that relate back to the original binding. After matching each
-binding in a range, we know the name of the binding in the original scope,
-and we _also_ know the name of the generated binding, or more generally the
-expression to evaluate in order to get the value of the original binding.
-
-These expression values are important for ensuring that the developer console
-is able to allow users to interact with the original code. If a user types
-a variable into the console, it can be translated into the expression
-correlated with that original variable, allowing the code to behave as it
-would have, were it actually part of the original file.
-
-
-### 4. Generate a Scope Tree
-
-The structure generated in step 3 is converted into a structure compatible
-with the standard scope data returned from the engine itself in order to allow
-for consistent usage of scope data, irrespective of the scope data source.
-This stage also re-attaches the global scope data, which is otherwise ignored
-during the correlation process.
-
-
-### 5. Validation
-
-As a simple form of validation, we ensure that a large percentage of bindings
-were actually resolved to a `grip` object. This offers some amount of
-protection, if the sourcemap itself turned out to be somewhat low-quality.
-
-This happens often with tooling that generates maps that simply map an original
-line to a generated line. While line-to-line mappings still enable step
-debugging, they do not provide enough information for original-scope mapping.
-
-On the other hand, generally line-to-line mappings are usually generated by
-tooling that performs minimal transformations on their own, so it is often
-acceptable to fall back to the engine-only generated-file scope data in this
-case.
-
-
-## Range Mapping Heuristics
-
-Since we know lots of information about the original bindings when performing
-matches, it is possible to make educated guesses about how their generated
-code will have likely been transformed. This allows us to perform more
-aggressive matching logic what would normally be too false-positive-heavy
-for generic use, when it is deemed safe enough.
-
-
-### Standard Matching
-
-In the general case, we iterate through the bindings that were found in the
-generated range, and consider it a match if the binding overlaps with the
-start of the range. One extra case here is that ranges at the start of
-a line often point to the first binding in a range and do not overlap the
-first binding, so there is special-casing for the first binding in each range.
-
-
-### ES6 Import Reference Special Case
-
-References to ES6 imports are often transformed into property accesses on objects
-in order to emulate the "live-binding" behavior defined by ES6. The standard
-logic here would end up always resolving the imported value to be the import
-namespace object itself, rather than reading the value of the property.
-
-To support this, specifically for ES6 imports, we walk outward from the matched
-binding itself, taking property accesses into account, as long as the
-property access itself is also within the mapped range.
-
-While decently effective, there are currently two downsides to this approach:
-
-* The "live" behavior of imports is often implemented using accessor
-  properties, which as of the time of this writing, cannot be evaluated to
-  retreive their real value.
-* The "live" behavior of imports is sometimes implemented with function calls,
-  which also also cannot be evaluated, causing their value to be
-  unknown.
-
-
-### ES6 Import Declaration Special Case
-
-If there are no references to an imported value, or matching based on the
-reference failed, we fall back to a second case.
-
-ES6 import declarations themselves often map back to the location of the
-declaration of the imported module's namespace object. By getting the range for
-the import declaration itself, we can infer which generated binding is the
-namespace object. Alongside that, we already know the name of the property on
-the namespace itself because it is statically knowable by analyzing the
-import declaration.
-
-By combining both of those pieces of information, we can access the namespace's
-property to get the imported value.
-
-
-### Typescript Classes
-
-Typescript have several non-ideal ways in which they output source maps, most
-of which center around classes that are either exported, or decorated. These
-issues are currently tracked in [Typescript issue #22833][1].
-
-To work around this issue, we use a method similar to the import declaration
-case above. While the class name itself often maps to unhelpful locations,
-the class declaration itself generally maps to the class's transformed binding,
-so we make use of the class declaration location instead of the location of
-the class's declared name in these cases.
-
-  [1]: https://github.com/Microsoft/TypeScript/issues/22833
--- a/devtools/client/debugger/new/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
+++ b/devtools/client/debugger/new/src/utils/sources-tree/tests/__snapshots__/addToTree.spec.js.snap
@@ -70,29 +70,8 @@ exports[`sources-tree addToTree replaces
 `;
 
 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 
 "
 `;
-
-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 
-      - TodoItem.js path=localhost:4242/components/TodoItem.js source_id=undefined 
-      - TodoTextInput.js path=localhost:4242/components/TodoTextInput.js source_id=undefined 
-    - reducers path=localhost:4242/reducers 
-      - index.js path=localhost:4242/reducers/index.js source_id=undefined 
-    - index.js path=localhost:4242/index.js source_id=undefined 
-  - resource://gre path=resource://gre 
-    - modules path=resource://gre/modules 
-      - ExtensionContent.jsm path=resource://gre/modules/ExtensionContent.jsm source_id=undefined 
-  - voz37vlg5.codesandbox.io path=voz37vlg5.codesandbox.io 
-    - static path=voz37vlg5.codesandbox.io/static 
-      - js path=voz37vlg5.codesandbox.io/static/js 
-        - components path=voz37vlg5.codesandbox.io/static/js/components 
-          - TodoItem.js path=voz37vlg5.codesandbox.io/static/js/components/TodoItem.js source_id=undefined 
-"
-`;
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -1,22 +1,20 @@
-/* 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/>. */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Helper methods to drive with the debugger during mochitests. This file can be safely
  * required from other panel test files.
  */
 
 // Import helpers for the new debugger
 Services.scriptloader.loadSubScript(
-  "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers/context.js",
-  this
-);
+"chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers/context.js",
+this);
 
 var { Toolbox } = require("devtools/client/framework/toolbox");
 var { Task } = require("devtools/shared/task");
 var asyncStorage = require("devtools/shared/async-storage");
 
 const sourceUtils = {
   isLoaded: source => source.loadedState === "loaded"
 };
@@ -199,17 +197,17 @@ async function waitForSources(dbg, ...so
  */
 function waitForSource(dbg, url) {
   return waitForState(
     dbg,
     state => {
       const sources = dbg.selectors.getSources(state);
       return Object.values(sources).find(s => (s.url || "").includes(url));
     },
-    "source exists"
+    `source exists`
   );
 }
 
 async function waitForElement(dbg, name) {
   await waitUntil(() => findElement(dbg, name));
   return findElement(dbg, name);
 }
 
@@ -337,24 +335,24 @@ function assertDebugLine(dbg, line) {
     1,
     "There is only one line"
   );
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
-    const classMatch =
-      markedSpans.filter(
-        span =>
-          span.marker.className &&
-          span.marker.className.includes("debug-expression")
-      ).length > 0;
+    const classMatch = markedSpans.filter(
+      span => span.marker.className && span.marker.className.includes("debug-expression")
+    ).length > 0;
 
-    ok(classMatch, "expression is highlighted as paused");
+    ok(
+      classMatch,
+      "expression is highlighted as paused"
+    );
   }
 }
 
 /**
  * Assert that the debugger is highlighting the correct location.
  *
  * @memberof mochitest/asserts
  * @param {Object} dbg
@@ -381,17 +379,20 @@ function assertHighlightLocation(dbg, so
     1,
     "Only 1 line is highlighted"
   );
 
   ok(isVisibleInEditor(dbg, lineEl), "Highlighted line is visible");
 
   const cm = getCM(dbg);
   const lineInfo = cm.lineInfo(line - 1);
-  ok(lineInfo.wrapClass.includes("highlight-line"), "Line is highlighted");
+  ok(
+    lineInfo.wrapClass.includes("highlight-line"),
+    "Line is highlighted"
+  );
 }
 
 /**
  * Returns boolean for whether the debugger is paused.
  *
  * @memberof mochitest/asserts
  * @param {Object} dbg
  * @static
@@ -431,17 +432,17 @@ async function getWorkers(dbg) {
 
   return getWorkers(getState());
 }
 
 async function waitForLoadedScopes(dbg) {
   const scopes = await waitForElement(dbg, "scopes");
   // Since scopes auto-expand, we can assume they are loaded when there is a tree node
   // with the aria-level attribute equal to "2".
-  await waitUntil(() => scopes.querySelector('.tree-node[aria-level="2"]'));
+  await waitUntil(() => scopes.querySelector(`.tree-node[aria-level="2"]`));
 }
 
 /**
  * Waits for the debugger to be fully paused.
  *
  * @memberof mochitest/waits
  * @param {Object} dbg
  * @static
@@ -496,17 +497,17 @@ function isSelectedFrameSelected(dbg, st
 
   return source.id == sourceId;
 }
 
 /**
  * Clear all the debugger related preferences.
  */
 function clearDebuggerPreferences() {
-  asyncStorage.clear();
+  asyncStorage.clear()
   Services.prefs.clearUserPref("devtools.recordreplay.enabled");
   Services.prefs.clearUserPref("devtools.debugger.pause-on-exceptions");
   Services.prefs.clearUserPref("devtools.debugger.pause-on-caught-exceptions");
   Services.prefs.clearUserPref("devtools.debugger.ignore-caught-exceptions");
   Services.prefs.clearUserPref("devtools.debugger.tabs");
   Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
   Services.prefs.clearUserPref("devtools.debugger.pending-breakpoints");
   Services.prefs.clearUserPref("devtools.debugger.expressions");
@@ -522,17 +523,17 @@ function clearDebuggerPreferences() {
  * @param {String} url
  * @return {Promise} dbg
  * @static
  */
 async function initDebugger(url, ...sources) {
   clearDebuggerPreferences();
   const toolbox = await openNewTabAndToolbox(EXAMPLE_URL + url, "jsdebugger");
   const dbg = createDebuggerContext(toolbox);
-  await waitForSources(dbg, ...sources);
+  await waitForSources(dbg, ...sources)
   return dbg;
 }
 
 async function initPane(url, pane) {
   clearDebuggerPreferences();
   return openNewTabAndToolbox(EXAMPLE_URL + url, pane);
 }
 
@@ -614,23 +615,21 @@ function waitForLoadedSources(dbg) {
  * @param {Object} dbg
  * @param {String} url
  * @param {Number} line
  * @return {Promise}
  * @static
  */
 async function selectSource(dbg, url, line) {
   const source = findSource(dbg, url);
-  await dbg.actions.selectLocation(
-    { sourceId: source.id, line },
-    { keepContext: false }
-  );
+  await dbg.actions.selectLocation({ sourceId: source.id, line }, {keepContext: false});
   return waitForSelectedSource(dbg, url);
 }
 
+
 async function closeTab(dbg, url) {
   await dbg.actions.closeTab(findSource(dbg, url));
 }
 
 function countTabs(dbg) {
   return findElement(dbg, "sourceTabs").children.length;
 }
 
@@ -759,23 +758,22 @@ function disableBreakpoint(dbg, source, 
 
 function findBreakpoint(dbg, url, line) {
   const {
     selectors: { getBreakpoint },
     getState
   } = dbg;
   const source = findSource(dbg, url);
   let column;
-  if (
+  if(
     Services.prefs.getBoolPref("devtools.debugger.features.column-breakpoints")
-  ) {
-    column = dbg.selectors.getFirstPausePointLocation(dbg.store.getState(), {
-      sourceId: source.id,
-      line
-    }).column;
+    ) {
+    column = dbg.selectors.getFirstPausePointLocation(
+      dbg.store.getState(), { sourceId: source.id, line }
+    ).column;
   }
   return getBreakpoint(getState(), { sourceId: source.id, line, column });
 }
 
 async function loadAndAddBreakpoint(dbg, filename, line, column) {
   const {
     selectors: { getBreakpoint, getBreakpointCount, getBreakpointsMap },
     getState
@@ -793,19 +791,17 @@ async function loadAndAddBreakpoint(dbg,
 
   is(getBreakpointCount(getState()), 1, "One breakpoint exists");
   if (!getBreakpoint(getState(), { sourceId: source.id, line, column })) {
     const breakpoints = getBreakpointsMap(getState());
     const id = Object.keys(breakpoints).pop();
     const loc = breakpoints[id].location;
     ok(
       false,
-      `Breakpoint has correct line ${line}, column ${column}, but was line ${
-        loc.line
-      } column ${loc.column}`
+      `Breakpoint has correct line ${line}, column ${column}, but was line ${loc.line} column ${loc.column}`
     );
   }
 
   return source;
 }
 
 async function invokeWithBreakpoint(
   dbg,
@@ -818,17 +814,17 @@ async function invokeWithBreakpoint(
     selectors: { getBreakpointCount },
     getState
   } = dbg;
 
   const source = await loadAndAddBreakpoint(dbg, filename, line, column);
 
   const invokeResult = invokeInTab(fnName);
 
-  const invokeFailed = await Promise.race([
+  let invokeFailed = await Promise.race([
     waitForPaused(dbg),
     invokeResult.then(() => new Promise(() => {}), () => true)
   ]);
 
   if (invokeFailed) {
     return invokeResult;
   }
 
@@ -844,17 +840,17 @@ async function invokeWithBreakpoint(
 
   // If the invoke errored later somehow, capture here so the error is reported nicely.
   await invokeResult;
 }
 
 async function expandAllScopes(dbg) {
   const scopes = await waitForElement(dbg, "scopes");
   const scopeElements = scopes.querySelectorAll(
-    '.tree-node[aria-level="1"][data-expandable="true"]:not([aria-expanded="true"])'
+    `.tree-node[aria-level="1"][data-expandable="true"]:not([aria-expanded="true"])`
   );
   const indices = Array.from(scopeElements, el => {
     return Array.prototype.indexOf.call(el.parentNode.childNodes, el);
   }).reverse();
 
   for (const index of indices) {
     await toggleScopeNode(dbg, index + 1);
   }
@@ -1079,17 +1075,17 @@ const selectors = {
     `.expressions-list .expression-container:nth-child(${i}) .object-delimiter + *`,
   expressionClose: i =>
     `.expressions-list .expression-container:nth-child(${i}) .close`,
   expressionInput: ".expressions-list  input.input-expression",
   expressionNodes: ".expressions-list .tree-node",
   expressionPlus: ".watch-expressions-pane button.plus",
   scopesHeader: ".scopes-pane ._header",
   breakpointItem: i => `.breakpoints-list div:nth-of-type(${i})`,
-  breakpointItems: ".breakpoints-list .breakpoint",
+  breakpointItems: `.breakpoints-list .breakpoint`,
   breakpointContextMenu: {
     disableSelf: "#node-menu-disable-self",
     disableAll: "#node-menu-disable-all",
     disableOthers: "#node-menu-disable-others",
     enableSelf: "#node-menu-enable-self",
     enableOthers: "#node-menu-enable-others",
     remove: "#node-menu-delete-self",
     removeOthers: "#node-menu-delete-other",
@@ -1099,20 +1095,18 @@ const selectors = {
   scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
   scopeValue: i =>
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
   frame: i => `.frames ul li:nth-child(${i})`,
   frames: ".frames ul li",
   gutter: i => `.CodeMirror-code *:nth-child(${i}) .CodeMirror-linenumber`,
   // These work for bobth the breakpoint listing and gutter marker
   gutterContextMenu: {
-    addConditionalBreakpoint:
-      "#node-menu-add-condition, #node-menu-add-conditional-breakpoint",
-    editConditionalBreakpoint:
-      "#node-menu-edit-condition, #node-menu-edit-conditional-breakpoint"
+    addConditionalBreakpoint: "#node-menu-add-condition, #node-menu-add-conditional-breakpoint",
+    editConditionalBreakpoint: "#node-menu-edit-condition, #node-menu-edit-conditional-breakpoint"
   },
   menuitem: i => `menupopup menuitem:nth-child(${i})`,
   pauseOnExceptions: ".pause-exceptions",
   breakpoint: ".CodeMirror-code > .new-breakpoint",
   highlightLine: ".CodeMirror-code > .highlight-line",
   debugLine: ".new-debug-line",
   debugErrorLine: ".new-debug-line-error",
   codeMirror: ".CodeMirror",
@@ -1140,17 +1134,17 @@ const selectors = {
   previewPopup: ".preview-popup",
   outlineItem: i =>
     `.outline-list__element:nth-child(${i}) .function-signature`,
   outlineItems: ".outline-list__element",
   conditionalPanelInput: ".conditional-breakpoint-panel input",
   searchField: ".search-field",
   blackbox: ".action.black-box",
   projectSearchCollapsed: ".project-text-search .arrow:not(.expanded)",
-  projectSerchExpandedResults: ".project-text-search .result"
+  projectSerchExpandedResults: ".project-text-search .result",
 };
 
 function getSelector(elementName, ...args) {
   let selector = selectors[elementName];
   if (!selector) {
     throw new Error(`The selector ${elementName} is not defined`);
   }
 
@@ -1167,17 +1161,17 @@ function findElement(dbg, elementName, .
 }
 
 function findElementWithSelector(dbg, selector) {
   return dbg.win.document.querySelector(selector);
 }
 
 function findAllElements(dbg, elementName, ...args) {
   const selector = getSelector(elementName, ...args);
-  return findAllElementsWithSelector(dbg, selector);
+  return findAllElementsWithSelector(dbg, selector)
 }
 
 function findAllElementsWithSelector(dbg, selector) {
   return dbg.win.document.querySelectorAll(selector);
 }
 
 /**
  * Simulates a mouse click in the debugger DOM.
@@ -1469,17 +1463,17 @@ async function editExpression(dbg, input
   await evaluated;
 }
 
 async function waitUntilPredicate(predicate) {
   let result;
   await waitUntil(() => {
     result = predicate();
     return result;
-  });
+  })
 
   return result;
 }
 
 // Return a promise with a reference to jsterm, opening the split
 // console if necessary.  This cleans up the split console pref so
 // it won't pollute other tests.
 async function getDebuggerSplitConsole(dbg) {
@@ -1515,31 +1509,28 @@ function hideConsoleContextMenu(hud) {
   const onPopupHidden = once(popup, "popuphidden");
   popup.hidePopup();
   return onPopupHidden;
 }
 
 // Return a promise that resolves with the result of a thread evaluating a
 // string in the topmost frame.
 async function evaluateInTopFrame(threadClient, text) {
-  const { frames } = await threadClient.getFrames(0, 1);
+  const {frames} = await threadClient.getFrames(0, 1);
   ok(frames.length == 1, "Got one frame");
   const response = await threadClient.eval(frames[0].actor, text);
   ok(response.type == "resumed", "Got resume response from eval");
   let rval;
   await threadClient.addOneTimeListener("paused", function(event, packet) {
-    ok(
-      packet.type == "paused" &&
-        packet.why.type == "clientEvaluated" &&
-        "return" in packet.why.frameFinished,
-      "Eval returned a value"
-    );
+    ok(packet.type == "paused" &&
+       packet.why.type == "clientEvaluated" &&
+       "return" in packet.why.frameFinished, "Eval returned a value");
     rval = packet.why.frameFinished.return;
   });
-  return rval.type == "undefined" ? undefined : rval;
+  return (rval.type == "undefined") ? undefined : rval;
 }
 
 // Return a promise that resolves when a thread evaluates a string in the
 // topmost frame, ensuring the result matches the expected value.
 async function checkEvaluateInTopFrame(threadClient, text, expected) {
   const rval = await evaluateInTopFrame(threadClient, text);
-  ok(rval == expected, `Eval returned ${expected}`);
+  ok(rval == expected, "Eval returned " + expected);
 }
--- a/devtools/client/shared/components/reps/reps.js
+++ b/devtools/client/shared/components/reps/reps.js
@@ -3683,23 +3683,36 @@ function getClosestNonBucketNode(item) {
   const parent = getParent(item);
   if (!parent) {
     return null;
   }
 
   return getClosestNonBucketNode(parent);
 }
 
-function getNonPrototypeParentGripValue(item) {
+function getParentGripNode(item) {
   const parentNode = getParent(item);
   if (!parentNode) {
     return null;
   }
 
-  const parentGripNode = getClosestGripNode(parentNode);
+  return getClosestGripNode(parentNode);
+}
+
+function getParentGripValue(item) {
+  const parentGripNode = getParentGripNode(item);
+  if (!parentGripNode) {
+    return null;
+  }
+
+  return getValue(parentGripNode);
+}
+
+function getNonPrototypeParentGripValue(item) {
+  const parentGripNode = getParentGripNode(item);
   if (!parentGripNode) {
     return null;
   }
 
   if (getType(parentGripNode) === NODE_TYPES.PROTOTYPE) {
     return getNonPrototypeParentGripValue(parentGripNode);
   }
 
@@ -3711,16 +3724,17 @@ module.exports = {
   createGetterNode,
   createSetterNode,
   getActor,
   getChildren,
   getChildrenWithEvaluations,
   getClosestGripNode,
   getClosestNonBucketNode,
   getParent,
+  getParentGripValue,
   getNonPrototypeParentGripValue,
   getNumericalPropertiesCount,
   getValue,
   makeNodesForEntries,
   makeNodesForPromiseProperties,
   makeNodesForProperties,
   makeNumericalBuckets,
   nodeHasAccessors,
@@ -4562,17 +4576,17 @@ class Tree extends Component {
           // We can stop the propagation since click handler on the node can be
           // created in `renderItem`.
           e.stopPropagation();
 
           // Since the user just clicked the node, there's no need to check if
           // it should be scrolled into view.
           this._focus(item, { preventAutoScroll: true });
           if (this.props.isExpanded(item)) {
-            this.props.onCollapse(item, e.altKey);
+            this.props.onCollapse(item);
           } else {
             this.props.onExpand(item, e.altKey);
           }
         }
       });
     });
 
     const style = Object.assign({}, this.props.style || {}, {
@@ -6401,21 +6415,21 @@ function rootsChanged(props) {
 
 function releaseActors(state, client) {
   const actors = getActors(state);
   for (const actor of actors) {
     client.releaseActor(actor);
   }
 }
 
-function invokeGetter(node, grip, getterName) {
+function invokeGetter(node, targetGrip, receiverId, getterName) {
   return async ({ dispatch, client, getState }) => {
     try {
-      const objectClient = client.createObjectClient(grip);
-      const result = await objectClient.getPropertyValue(getterName);
+      const objectClient = client.createObjectClient(targetGrip);
+      const result = await objectClient.getPropertyValue(getterName, receiverId);
       dispatch({
         type: "GETTER_INVOKED",
         data: {
           node,
           result
         }
       });
     } catch (e) {
@@ -6999,16 +7013,17 @@ const {
   nodeIsSetter,
   nodeIsUninitializedBinding,
   nodeIsUnmappedBinding,
   nodeIsUnscopedBinding,
   nodeIsWindow,
   nodeIsLongString,
   nodeHasFullText,
   nodeHasGetter,
+  getParentGripValue,
   getNonPrototypeParentGripValue
 } = Utils.node;
 
 class ObjectInspectorItem extends Component {
   // eslint-disable-next-line complexity
   getLabelAndValue() {
     const { item, depth, expanded, mode } = this.props;
 
@@ -7073,20 +7088,21 @@ class ObjectInspectorItem extends Compon
 
       if (nodeIsLongString(item)) {
         repProps.member = {
           open: nodeHasFullText(item) && expanded
         };
       }
 
       if (nodeHasGetter(item)) {
-        const parentGrip = getNonPrototypeParentGripValue(item);
-        if (parentGrip) {
+        const targetGrip = getParentGripValue(item);
+        const receiverGrip = getNonPrototypeParentGripValue(item);
+        if (targetGrip && receiverGrip) {
           Object.assign(repProps, {
-            onInvokeGetterButtonClick: () => this.props.invokeGetter(item, parentGrip, item.name)
+            onInvokeGetterButtonClick: () => this.props.invokeGetter(item, targetGrip, receiverGrip.actor, item.name)
           });
         }
       }
 
       return {
         label,
         value: Utils.renderRep(item, repProps)
       };