Bug 1129187 - Initialize all the overview graphs and detail views lazily, r=jsantell
authorVictor Porof <vporof@mozilla.com>
Fri, 06 Feb 2015 13:20:19 -0500
changeset 227872 1de14614f44e49095562887018bdb6f220dfa98e
parent 227871 a462b35377de6ea9aeeeb4a5220c025b5127a253
child 227873 2b2130aec3cd093fb5362216a597610594fa8e22
push idunknown
push userunknown
push dateunknown
reviewersjsantell
bugs1129187
milestone38.0a1
Bug 1129187 - Initialize all the overview graphs and detail views lazily, r=jsantell
browser/devtools/performance/performance-controller.js
browser/devtools/performance/performance-view.js
browser/devtools/performance/test/browser.ini
browser/devtools/performance/test/browser_perf-allocations-to-samples.js
browser/devtools/performance/test/browser_perf-clear-01.js
browser/devtools/performance/test/browser_perf-details-02.js
browser/devtools/performance/test/browser_perf-details-03.js
browser/devtools/performance/test/browser_perf-details-calltree-render.js
browser/devtools/performance/test/browser_perf-details-flamegraph-render.js
browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
browser/devtools/performance/test/browser_perf-front.js
browser/devtools/performance/test/browser_perf-jump-to-debugger-02.js
browser/devtools/performance/test/browser_perf-options-01.js
browser/devtools/performance/test/browser_perf-options-02.js
browser/devtools/performance/test/browser_perf-options-enable-memory-02.js
browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-01.js
browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-02.js
browser/devtools/performance/test/browser_perf-options-invert-call-tree-01.js
browser/devtools/performance/test/browser_perf-options-invert-call-tree-02.js
browser/devtools/performance/test/browser_perf-options-show-idle-blocks-01.js
browser/devtools/performance/test/browser_perf-options-show-idle-blocks-02.js
browser/devtools/performance/test/browser_perf-options-show-platform-data-01.js
browser/devtools/performance/test/browser_perf-options-show-platform-data-02.js
browser/devtools/performance/test/browser_perf-overview-render-01.js
browser/devtools/performance/test/browser_perf-overview-render-02.js
browser/devtools/performance/test/browser_perf-overview-render-03.js
browser/devtools/performance/test/browser_perf-overview-selection-01.js
browser/devtools/performance/test/browser_perf-overview-selection-02.js
browser/devtools/performance/test/browser_perf-overview-selection-03.js
browser/devtools/performance/test/browser_perf-overview-time-interval.js
browser/devtools/performance/test/browser_perf-range-changed-render.js
browser/devtools/performance/test/browser_perf-recording-selected-04.js
browser/devtools/performance/test/browser_perf-states.js
browser/devtools/performance/test/browser_perf_recordings-io-01.js
browser/devtools/performance/test/browser_perf_recordings-io-04.js
browser/devtools/performance/test/head.js
browser/devtools/performance/views/details-abstract-subview.js
browser/devtools/performance/views/details.js
browser/devtools/performance/views/overview.js
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -55,19 +55,18 @@ devtools.lazyImporter(this, "SideMenuWid
 
 const BRANCH_NAME = "devtools.performance.ui.";
 
 // Events emitted by various objects in the panel.
 const EVENTS = {
   // Fired by the OptionsView when a preference changes.
   PREF_CHANGED: "Performance:PrefChanged",
 
-  // Emitted by the PerformanceController or RecordingView
-  // when a recording model is selected
-  RECORDING_SELECTED: "Performance:RecordingSelected",
+  // Emitted by the PerformanceView when the state (display mode) changes.
+  UI_STATE_CHANGED: "Performance:UI:StateChanged",
 
   // Emitted by the PerformanceView on clear button click
   UI_CLEAR_RECORDINGS: "Performance:UI:ClearRecordings",
 
   // Emitted by the PerformanceView on record button click
   UI_START_RECORDING: "Performance:UI:StartRecording",
   UI_STOP_RECORDING: "Performance:UI:StopRecording",
 
@@ -77,16 +76,20 @@ const EVENTS = {
   UI_EXPORT_RECORDING: "Performance:UI:ExportRecording",
 
   // When a recording is started or stopped via the PerformanceController
   RECORDING_STARTED: "Performance:RecordingStarted",
   RECORDING_STOPPED: "Performance:RecordingStopped",
   RECORDING_WILL_START: "Performance:RecordingWillStart",
   RECORDING_WILL_STOP: "Performance:RecordingWillStop",
 
+  // Emitted by the PerformanceController or RecordingView
+  // when a recording model is selected
+  RECORDING_SELECTED: "Performance:RecordingSelected",
+
   // When recordings have been cleared out
   RECORDINGS_CLEARED: "Performance:RecordingsCleared",
 
   // When a recording is imported or exported via the PerformanceController
   RECORDING_IMPORTED: "Performance:RecordingImported",
   RECORDING_EXPORTED: "Performance:RecordingExported",
 
   // When the PerformanceController has new recording data
@@ -236,17 +239,16 @@ let PerformanceController = {
   /**
    * Stops recording with the PerformanceFront. Emits `EVENTS.RECORDING_STOPPED`
    * when the front has stopped recording.
    */
   stopRecording: Task.async(function *() {
     let recording = this._getLatestRecording();
 
     this.emit(EVENTS.RECORDING_WILL_STOP, recording);
-
     yield recording.stopRecording();
     this.emit(EVENTS.RECORDING_STOPPED, recording);
   }),
 
   /**
    * Saves the given recording to a file. Emits `EVENTS.RECORDING_EXPORTED`
    * when the file was saved.
    *
--- a/browser/devtools/performance/performance-view.js
+++ b/browser/devtools/performance/performance-view.js
@@ -24,17 +24,17 @@ let PerformanceView = {
       { deck: "#performance-view", pane: "#performance-view-content" },
       { deck: "#details-pane-container", pane: "#details-pane" }
     ]
   },
 
   /**
    * Sets up the view with event binding and main subviews.
    */
-  initialize: function () {
+  initialize: Task.async(function* () {
     this._recordButton = $("#main-record-button");
     this._importButton = $("#import-button");
     this._clearButton = $("#clear-button");
 
     this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
     this._onImportButtonClick = this._onImportButtonClick.bind(this);
     this._onClearButtonClick = this._onClearButtonClick.bind(this);
     this._lockRecordButton = this._lockRecordButton.bind(this);
@@ -54,61 +54,60 @@ let PerformanceView = {
     PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
     PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
     PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
     PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
 
     this.setState("empty");
 
-    return promise.all([
-      RecordingsView.initialize(),
-      OverviewView.initialize(),
-      ToolbarView.initialize(),
-      DetailsView.initialize()
-    ]);
-  },
+    // Initialize the ToolbarView first, because other views may need access
+    // to the OptionsView via the controller, to read prefs.
+    yield ToolbarView.initialize();
+    yield RecordingsView.initialize();
+    yield OverviewView.initialize();
+    yield DetailsView.initialize();
+  }),
 
   /**
    * Unbinds events and destroys subviews.
    */
-  destroy: function () {
+  destroy: Task.async(function* () {
     for (let button of $$(".record-button")) {
       button.removeEventListener("click", this._onRecordButtonClick);
     }
     this._importButton.removeEventListener("click", this._onImportButtonClick);
 
     PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
     PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
     PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
     PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
 
-    return promise.all([
-      RecordingsView.destroy(),
-      OverviewView.destroy(),
-      ToolbarView.destroy(),
-      DetailsView.destroy()
-    ]);
-  },
+    yield ToolbarView.destroy();
+    yield RecordingsView.destroy();
+    yield OverviewView.destroy();
+    yield DetailsView.destroy();
+  }),
 
   /**
    * Sets the state of the profiler view. Possible options are "empty",
    * "recording", "recorded".
    */
   setState: function (state) {
     let viewConfig = this.states[state];
     if (!viewConfig) {
       throw new Error(`Invalid state for PerformanceView: ${state}`);
     }
     for (let { deck, pane } of viewConfig) {
       $(deck).selectedPanel = $(pane);
     }
 
     this._state = state;
+    this.emit(EVENTS.UI_STATE_CHANGED, state);
   },
 
   /**
    * Returns the state of the PerformanceView.
    */
   getState: function () {
     return this._state;
   },
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -50,16 +50,17 @@ support-files =
 [browser_perf-overview-render-02.js]
 [browser_perf-overview-render-03.js]
 [browser_perf-overview-selection-01.js]
 [browser_perf-overview-selection-02.js]
 [browser_perf-overview-selection-03.js]
 [browser_perf-overview-time-interval.js]
 [browser_perf-shared-connection-02.js]
 [browser_perf-shared-connection-03.js]
+[browser_perf-states.js]
 [browser_perf-ui-recording.js]
 [browser_perf-recording-notices-01.js]
 [browser_perf-recording-notices-02.js]
 [browser_perf_recordings-io-01.js]
 [browser_perf_recordings-io-02.js]
 [browser_perf_recordings-io-03.js]
 [browser_perf_recordings-io-04.js]
 [browser_perf-range-changed-render.js]
--- a/browser/devtools/performance/test/browser_perf-allocations-to-samples.js
+++ b/browser/devtools/performance/test/browser_perf-allocations-to-samples.js
@@ -15,32 +15,29 @@ function test() {
 
   finish();
 }
 
 let TEST_DATA = {
   sites: [0, 0, 1, 2, 3],
   timestamps: [50, 100, 150, 200, 250],
   frames: [
-    null,
-    {
+    null, {
       source: "A",
       line: 1,
       column: 2,
       functionDisplayName: "x",
       parent: 0
-    },
-    {
+    }, {
       source: "B",
       line: 3,
       column: 4,
       functionDisplayName: "y",
       parent: 1
-    },
-    {
+    }, {
       source: "C",
       line: 5,
       column: 6,
       functionDisplayName: null,
       parent: 2
     }
   ],
   counts: [11, 22, 33, 44]
--- a/browser/devtools/performance/test/browser_perf-clear-01.js
+++ b/browser/devtools/performance/test/browser_perf-clear-01.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that clearing recordings empties out the recordings list and toggles
  * the empty notice state.
  */
 
 let test = Task.async(function*() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, PerformanceController, PerformanceView, RecordingsView } = panel.panelWin;
+  let { EVENTS, PerformanceController, PerformanceView, RecordingsView, OverviewView } = panel.panelWin;
 
   yield startRecording(panel);
   yield stopRecording(panel);
 
   yield startRecording(panel);
   yield stopRecording(panel);
 
   yield PerformanceController.clearRecordings();
--- a/browser/devtools/performance/test/browser_perf-details-02.js
+++ b/browser/devtools/performance/test/browser_perf-details-02.js
@@ -9,33 +9,33 @@ function spawnTest () {
   let { EVENTS, DetailsView } = panel.panelWin;
   let { WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
 
   ok(DetailsView.isViewSelected(WaterfallView),
     "The waterfall view is selected by default in the details view.");
 
   let selected = DetailsView.whenViewSelected(JsCallTreeView);
   let notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
   yield Promise.all([selected, notified]);
 
   ok(DetailsView.isViewSelected(JsCallTreeView),
     "The waterfall view is now selected in the details view.");
 
   selected = DetailsView.whenViewSelected(JsFlameGraphView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
-  DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("js-flamegraph");
   yield Promise.all([selected, notified]);
 
   ok(DetailsView.isViewSelected(JsFlameGraphView),
     "The flamegraph view is now selected in the details view.");
 
   selected = DetailsView.whenViewSelected(WaterfallView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
-  DetailsView.selectView("waterfall");
+  yield DetailsView.selectView("waterfall");
   yield Promise.all([selected, notified]);
 
   ok(DetailsView.isViewSelected(WaterfallView),
     "The waterfall view is now selected in the details view.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-details-03.js
+++ b/browser/devtools/performance/test/browser_perf-details-03.js
@@ -35,16 +35,17 @@ function spawnTest () {
 
   selected = DetailsView.whenViewSelected(WaterfallView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   Services.prefs.setBoolPref(MEMORY_PREF, false);
   yield Promise.all([selected, notified]);
 
   ok(DetailsView.isViewSelected(WaterfallView),
     "The waterfall view is now selected when toggling off enable-memory when a memory panel is selected.");
+
   Services.prefs.setBoolPref(MEMORY_PREF, true);
 
   selected = DetailsView.whenViewSelected(MemoryFlameGraphView);
   notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
   DetailsView.selectView("memory-flamegraph");
   yield Promise.all([selected, notified]);
 
   selected = DetailsView.whenViewSelected(WaterfallView);
--- a/browser/devtools/performance/test/browser_perf-details-calltree-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-calltree-render.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that the call tree view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
 
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
   ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-details-flamegraph-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-flamegraph-render.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that the flamegraph view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
 
-  DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("js-flamegraph");
   ok(DetailsView.isViewSelected(JsFlameGraphView), "The flamegraph is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-memory-calltree-render.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that the memory call tree view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
 
-  DetailsView.selectView("memory-calltree");
+  yield DetailsView.selectView("memory-calltree");
   ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
+++ b/browser/devtools/performance/test/browser_perf-details-memory-flamegraph-render.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that the memory flamegraph view renders content after recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
 
-  DetailsView.selectView("memory-flamegraph");
+  yield DetailsView.selectView("memory-flamegraph");
   ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
@@ -5,29 +5,51 @@
  * Test basic functionality of PerformanceFront
  */
 
 let WAIT_TIME = 1000;
 
 function spawnTest () {
   let { target, front } = yield initBackend(SIMPLE_URL);
 
-  let { profilerStartTime, timelineStartTime } = yield front.startRecording();
+  let startData = yield front.startRecording();
+  let { profilerStartTime, timelineStartTime, memoryStartTime } = startData;
+
+  ok("profilerStartTime" in startData,
+    "A `profilerStartTime` property is properly set in the recording data.");
+  ok("timelineStartTime" in startData,
+    "A `timelineStartTime` property is properly set in the recording data.");
+  ok("memoryStartTime" in startData,
+    "A `memoryStartTime` property is properly set in the recording data.");
 
   ok(profilerStartTime !== undefined,
     "A `profilerStartTime` property exists in the recording data.");
   ok(timelineStartTime !== undefined,
     "A `timelineStartTime` property exists in the recording data.");
+  is(memoryStartTime, 0,
+    "A `memoryStartTime` property exists in the recording data, but it's 0.");
 
   yield busyWait(WAIT_TIME);
 
-  let { profile, profilerEndTime, timelineEndTime } = yield front.stopRecording();
+  let stopData = yield front.stopRecording();
+  let { profile, profilerEndTime, timelineEndTime, memoryEndTime } = stopData;
+
+  ok("profile" in stopData,
+    "A `profile` property is properly set in the recording data.");
+  ok("profilerEndTime" in stopData,
+    "A `profilerEndTime` property is properly set in the recording data.");
+  ok("timelineEndTime" in stopData,
+    "A `timelineEndTime` property is properly set in the recording data.");
+  ok("memoryEndTime" in stopData,
+    "A `memoryEndTime` property is properly set in the recording data.");
 
   ok(profile,
     "A `profile` property exists in the recording data.");
   ok(profilerEndTime !== undefined,
     "A `profilerEndTime` property exists in the recording data.");
   ok(timelineEndTime !== undefined,
     "A `timelineEndTime` property exists in the recording data.");
+  is(memoryEndTime, 0,
+    "A `memoryEndTime` property exists in the recording data, but it's 0.");
 
   yield removeTab(target.tab);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
+++ b/browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
@@ -23,19 +23,17 @@ function spawnTest () {
     ticks: Promise.defer()
   };
 
   front.on("markers", handler);
   front.on("memory", handler);
   front.on("ticks", handler);
 
   yield front.startRecording({ withMemory: true, withTicks: true });
-
   yield Promise.all(Object.keys(deferreds).map(type => deferreds[type].promise));
-
   yield front.stopRecording();
 
   is(counters.markers.length, 1, "one marker event fired.");
   is(counters.memory.length, 3, "three memory events fired.");
   is(counters.ticks.length, 3, "three ticks events fired.");
 
   yield removeTab(target.tab);
   finish();
--- a/browser/devtools/performance/test/browser_perf-front.js
+++ b/browser/devtools/performance/test/browser_perf-front.js
@@ -26,17 +26,19 @@ function spawnTest () {
     "The front.startRecording() emits a memory start time.");
 
   yield busyWait(WAIT_TIME);
 
   let {
     profilerEndTime,
     timelineEndTime,
     memoryEndTime
-  } = yield front.stopRecording();
+  } = yield front.stopRecording({
+    withAllocations: true
+  });
 
   ok(typeof profilerEndTime === "number",
     "The front.stopRecording() emits a profiler end time.");
   ok(typeof timelineEndTime === "number",
     "The front.stopRecording() emits a timeline end time.");
   ok(typeof memoryEndTime === "number",
     "The front.stopRecording() emits a memory end time.");
 
--- a/browser/devtools/performance/test/browser_perf-jump-to-debugger-02.js
+++ b/browser/devtools/performance/test/browser_perf-jump-to-debugger-02.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests if the performance tool can jump to the debugger, when the source was already
- * loaded in that tool.
+ * Tests if the performance tool can jump to the debugger, when the source was
+ * already loaded in that tool.
  */
 
 function spawnTest() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL, "jsdebugger");
   let debuggerWin = panel.panelWin;
   let debuggerEvents = debuggerWin.EVENTS;
   let { DebuggerView } = debuggerWin;
   let Sources = DebuggerView.Sources;
--- a/browser/devtools/performance/test/browser_perf-options-01.js
+++ b/browser/devtools/performance/test/browser_perf-options-01.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that toggling preferences before there are any recordings does not throw.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
 
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
 
   // Manually call the _onPrefChanged function so we can catch an error
   try {
     JsCallTreeView._onPrefChanged(null, "invert-call-tree", true);
     ok(true, "Toggling preferences before there are any recordings should not fail.");
   } catch (e) {
     ok(false, "Toggling preferences before there are any recordings should not fail.");
   }
--- a/browser/devtools/performance/test/browser_perf-options-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-02.js
@@ -3,17 +3,17 @@
 
 /**
  * Tests that toggling preferences during a recording does not throw.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
 
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
 
   yield startRecording(panel);
 
   // Manually call the _onPrefChanged function so we can catch an error
   try {
     JsCallTreeView._onPrefChanged(null, "invert-call-tree", true);
     ok(true, "Toggling preferences during a recording should not fail.");
   } catch (e) {
--- a/browser/devtools/performance/test/browser_perf-options-enable-memory-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-enable-memory-02.js
@@ -20,16 +20,17 @@ function spawnTest () {
     "The recording finished without tracking memory.");
   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations, false,
     "The recording finished without tracking allocations.");
 
   // Test starting with memory, and stopping without it.
   yield startRecording(panel);
   Services.prefs.setBoolPref(MEMORY_PREF, false);
   yield stopRecording(panel);
+
   is(PerformanceController.getCurrentRecording().getConfiguration().withMemory, true,
     "The recording finished with tracking memory.");
   is(PerformanceController.getCurrentRecording().getConfiguration().withAllocations, true,
     "The recording finished with tracking allocations.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-01.js
+++ b/browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-01.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the js Flamegraphs gets rerendered when toggling `flatten-tree-recursion`
+ * Tests that the js flamegraphs get rerendered when toggling `flatten-tree-recursion`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
 
-  DetailsView.selectView("js-flamegraph");
+  Services.prefs.setBoolPref(FLATTEN_PREF, true);
 
-  Services.prefs.setBoolPref(FLATTEN_PREF, true);
+  yield DetailsView.selectView("js-flamegraph");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
--- a/browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-flatten-tree-recursion-02.js
@@ -1,24 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the memory Flamegraphs gets rerendered when toggling `flatten-tree-recursion`
+ * Tests that the memory flamegraphs get rerendered when toggling `flatten-tree-recursion`
  */
 function spawnTest () {
-  // Enable memory to test
-  Services.prefs.setBoolPref(MEMORY_PREF, true);
-
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
 
-  DetailsView.selectView("memory-flamegraph");
+  // Enable memory to test
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+  Services.prefs.setBoolPref(FLATTEN_PREF, true);
 
-  Services.prefs.setBoolPref(FLATTEN_PREF, true);
+  yield DetailsView.selectView("memory-flamegraph");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
--- a/browser/devtools/performance/test/browser_perf-options-invert-call-tree-01.js
+++ b/browser/devtools/performance/test/browser_perf-options-invert-call-tree-01.js
@@ -1,22 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the js call tree view is re-rendered after the
- * "invert-call-tree" pref is changed.
+ * Tests that the js call tree views get rerendered when toggling `invert-call-tree`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
 
   Services.prefs.setBoolPref(INVERT_PREF, true);
 
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
   ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-options-invert-call-tree-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-invert-call-tree-02.js
@@ -1,24 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the memory call tree view is re-rendered after the
- * "invert-call-tree" pref is changed.
+ * Tests that the memory call tree views get rerendered when toggling `invert-call-tree`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
 
   // Enable memory to test
   Services.prefs.setBoolPref(MEMORY_PREF, true);
   Services.prefs.setBoolPref(INVERT_PREF, true);
 
-  DetailsView.selectView("memory-calltree");
+  yield DetailsView.selectView("memory-calltree");
   ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield rendered;
--- a/browser/devtools/performance/test/browser_perf-options-show-idle-blocks-01.js
+++ b/browser/devtools/performance/test/browser_perf-options-show-idle-blocks-01.js
@@ -1,21 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the js Flamegraphs gets rerendered when toggling `show-idle-blocks`
+ * Tests that the js flamegraphs get rerendered when toggling `show-idle-blocks`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
 
-  DetailsView.selectView("js-flamegraph");
+  Services.prefs.setBoolPref(IDLE_PREF, true);
 
-  Services.prefs.setBoolPref(IDLE_PREF, true);
+  yield DetailsView.selectView("js-flamegraph");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
--- a/browser/devtools/performance/test/browser_perf-options-show-idle-blocks-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-show-idle-blocks-02.js
@@ -1,23 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the memory Flamegraphs gets rerendered when toggling `show-idle-blocks`
+ * Tests that the memory flamegraphs get rerendered when toggling `show-idle-blocks`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
 
   // Enable memory to test
   Services.prefs.setBoolPref(MEMORY_PREF, true);
-  DetailsView.selectView("memory-flamegraph");
+  Services.prefs.setBoolPref(IDLE_PREF, true);
 
-  Services.prefs.setBoolPref(IDLE_PREF, true);
+  yield DetailsView.selectView("memory-flamegraph");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
--- a/browser/devtools/performance/test/browser_perf-options-show-platform-data-01.js
+++ b/browser/devtools/performance/test/browser_perf-options-show-platform-data-01.js
@@ -1,22 +1,22 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the JsCallTree get rerendered when toggling `show-platform-data`
+ * Tests that the js call tree views get rerendered when toggling `show-platform-data`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, DetailsView, JsFlameGraphView, JsCallTreeView } = panel.panelWin;
-
-  DetailsView.selectView("js-calltree");
+  let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
 
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
 
+  yield DetailsView.selectView("js-calltree");
+
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
   rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
--- a/browser/devtools/performance/test/browser_perf-options-show-platform-data-02.js
+++ b/browser/devtools/performance/test/browser_perf-options-show-platform-data-02.js
@@ -1,20 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Tests that the JsFlamegraphs get rerendered when toggling `show-platform-data`
+ * Tests that the js flamegraphs get rerendered when toggling `show-platform-data`
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
 
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
-  DetailsView.selectView("js-flamegraph");
+
+  yield DetailsView.selectView("js-flamegraph");
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
   yield stopRecording(panel);
   yield rendered;
 
--- a/browser/devtools/performance/test/browser_perf-overview-render-01.js
+++ b/browser/devtools/performance/test/browser_perf-overview-render-01.js
@@ -3,16 +3,19 @@
 
 /**
  * Tests that the overview continuously renders content when recording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, OverviewView } = panel.panelWin;
 
+  // Enable memory to test all the overview graphs.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
   yield startRecording(panel);
 
   yield Promise.all([
     once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
     once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
     once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
     once(OverviewView, EVENTS.OVERVIEW_RENDERED),
   ]);
--- a/browser/devtools/performance/test/browser_perf-overview-render-02.js
+++ b/browser/devtools/performance/test/browser_perf-overview-render-02.js
@@ -4,18 +4,28 @@
 /**
  * Tests that the overview graphs cannot be selected during recording
  * and that they're cleared upon rerecording.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, OverviewView } = panel.panelWin;
 
+  // Enable memory to test all the overview graphs.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
   yield startRecording(panel);
 
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED),
+  ]);
+
   ok("selectionEnabled" in OverviewView.framerateGraph,
     "The selection should not be enabled for the framerate overview (1).");
   is(OverviewView.framerateGraph.selectionEnabled, false,
     "The selection should not be enabled for the framerate overview (2).");
   is(OverviewView.framerateGraph.hasSelection(), false,
     "The framerate overview shouldn't have a selection before recording.");
 
   ok("selectionEnabled" in OverviewView.markersOverview,
--- a/browser/devtools/performance/test/browser_perf-overview-render-03.js
+++ b/browser/devtools/performance/test/browser_perf-overview-render-03.js
@@ -3,35 +3,32 @@
 
 /**
  * Tests that the overview graphs share the exact same width and scaling.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
 
-  Services.prefs.setBoolPref("devtools.performance.ui.enable-memory", true);
-  Services.prefs.setBoolPref("devtools.performance.ui.enable-framerate", true);
+  // Enable memory to test all the overview graphs.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
 
   yield startRecording(panel);
 
   let updated = 0;
   OverviewView.on(EVENTS.OVERVIEW_RENDERED, () => updated++);
 
   yield busyWait(100);
   yield waitUntil(() => PerformanceController.getCurrentRecording().getMarkers().length);
   yield waitUntil(() => PerformanceController.getCurrentRecording().getMemory().length);
   yield waitUntil(() => PerformanceController.getCurrentRecording().getTicks().length);
   yield waitUntil(() => updated > 10);
 
   yield stopRecording(panel);
 
-  // Wait for the overview graph to be rerendered *after* recording.
-  yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
-
   ok(OverviewView.markersOverview.width > 0,
     "The overview's framerate graph has a width.");
   ok(OverviewView.markersOverview.dataScaleX > 0,
     "The overview's framerate graph has a data scale factor.");
 
   ok(OverviewView.memoryOverview.width > 0,
     "The overview's framerate graph has a width.");
   ok(OverviewView.memoryOverview.dataDuration > 0,
--- a/browser/devtools/performance/test/browser_perf-overview-selection-01.js
+++ b/browser/devtools/performance/test/browser_perf-overview-selection-01.js
@@ -6,24 +6,24 @@
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, PerformanceController, OverviewView } = panel.panelWin;
   let startTime, endTime, params, _;
 
   yield startRecording(panel);
 
-  // Wait for the overview graph to be rendered while recording.
-  yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED)
+  ]);
 
   yield stopRecording(panel);
 
-  // Wait for the overview graph to be rerendered *after* recording.
-  yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
-
   let graph = OverviewView.markersOverview;
   let MAX = graph.width;
 
   // Select the first half of the graph
   let results = onceSpread(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
   dragStart(graph, 0);
   dragStop(graph, MAX / 2);
   [_, { startTime, endTime }] = yield results;
--- a/browser/devtools/performance/test/browser_perf-overview-selection-02.js
+++ b/browser/devtools/performance/test/browser_perf-overview-selection-02.js
@@ -2,21 +2,39 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the graphs' selection is correctly disabled or enabled.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, OverviewView } = panel.panelWin;
-  let framerateGraph = OverviewView.framerateGraph;
+
+  // Enable memory to test all the overview graphs.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
+  yield startRecording(panel);
+
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED),
+  ]);
+
   let markersOverview = OverviewView.markersOverview;
   let memoryOverview = OverviewView.memoryOverview;
+  let framerateGraph = OverviewView.framerateGraph;
 
-  yield startRecording(panel);
+  ok(markersOverview,
+    "The markers graph should have been created now.");
+  ok(memoryOverview,
+    "The memory graph should have been created now.");
+  ok(framerateGraph,
+    "The framerate graph should have been created now.");
 
   ok(!framerateGraph.selectionEnabled,
     "Selection shouldn't be enabled when the first recording started (1).");
   ok(!markersOverview.selectionEnabled,
     "Selection shouldn't be enabled when the first recording started (2).");
   ok(!memoryOverview.selectionEnabled,
     "Selection shouldn't be enabled when the first recording started (3).");
 
@@ -26,16 +44,23 @@ function spawnTest () {
     "Selection should be enabled when the first recording finishes (1).");
   ok(markersOverview.selectionEnabled,
     "Selection should be enabled when the first recording finishes (2).");
   ok(memoryOverview.selectionEnabled,
     "Selection should be enabled when the first recording finishes (3).");
 
   yield startRecording(panel);
 
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED),
+  ]);
+
   ok(!framerateGraph.selectionEnabled,
     "Selection shouldn't be enabled when the second recording started (1).");
   ok(!markersOverview.selectionEnabled,
     "Selection shouldn't be enabled when the second recording started (2).");
   ok(!memoryOverview.selectionEnabled,
     "Selection shouldn't be enabled when the second recording started (3).");
 
   yield stopRecording(panel);
--- a/browser/devtools/performance/test/browser_perf-overview-selection-03.js
+++ b/browser/devtools/performance/test/browser_perf-overview-selection-03.js
@@ -2,25 +2,36 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the graphs' selections are linked.
  */
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, OverviewView } = panel.panelWin;
+
+  // Enable memory to test all the overview graphs.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
+  yield startRecording(panel);
+
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED),
+  ]);
+
+  yield stopRecording(panel);
+
   let framerateGraph = OverviewView.framerateGraph;
   let markersOverview = OverviewView.markersOverview;
   let memoryOverview = OverviewView.memoryOverview;
-
   let MAX = framerateGraph.width;
 
-  yield startRecording(panel);
-  yield stopRecording(panel);
-
   // Perform a selection inside the framerate graph.
 
   let selected = once(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
   dragStart(framerateGraph, 0);
   dragStop(framerateGraph, MAX / 2);
   yield selected;
 
   is(framerateGraph.getSelection().toSource(), "({start:0, end:" + (MAX / 2) + "})",
--- a/browser/devtools/performance/test/browser_perf-overview-time-interval.js
+++ b/browser/devtools/performance/test/browser_perf-overview-time-interval.js
@@ -19,21 +19,24 @@ function spawnTest () {
   try {
     OverviewView.getTimeInterval();
     ok(false, "Getting the time interval shouldn't have worked.");
   } catch (e) {
     ok(true, "Getting the time interval didn't work, as expected.");
   }
 
   yield startRecording(panel);
-  busyWait(100);
 
-  let rendered = once(OverviewView, EVENTS.OVERVIEW_RENDERED);
+  yield Promise.all([
+    once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
+    once(OverviewView, EVENTS.OVERVIEW_RENDERED)
+  ]);
+
   yield stopRecording(panel);
-  yield rendered;
 
   // Get/set the time interval and wait for the event propagation.
 
   let notified = once(OverviewView, EVENTS.OVERVIEW_RANGE_SELECTED);
   OverviewView.setTimeInterval({ startTime: 10, endTime: 20 });
   yield notified;
 
   let firstInterval = OverviewView.getTimeInterval();
--- a/browser/devtools/performance/test/browser_perf-range-changed-render.js
+++ b/browser/devtools/performance/test/browser_perf-range-changed-render.js
@@ -22,37 +22,37 @@ function spawnTest () {
   yield stopRecording(panel);
 
   let rendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
   OverviewView.emit(EVENTS.OVERVIEW_RANGE_SELECTED, { startTime: 0, endTime: 10 });
   yield rendered;
   ok(true, "Waterfall rerenders when a range in the overview graph is selected.");
 
   rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
   yield rendered;
   ok(true, "Call tree rerenders after its corresponding pane is shown.");
 
   rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
-  DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("js-flamegraph");
   yield rendered;
   ok(true, "Flamegraph rerenders after its corresponding pane is shown.");
 
   rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
   OverviewView.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
   yield rendered;
   ok(true, "Flamegraph rerenders when a range in the overview graph is removed.");
 
   rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
-  DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-calltree");
   yield rendered;
   ok(true, "Call tree rerenders after its corresponding pane is shown.");
 
   rendered = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
-  DetailsView.selectView("waterfall");
+  yield DetailsView.selectView("waterfall");
   yield rendered;
   ok(true, "Waterfall rerenders after its corresponding pane is shown.");
 
   is(updatedWaterfall, 3, "WaterfallView rerendered 3 times.");
   is(updatedCallTree, 2, "JsCallTreeView rerendered 2 times.");
   is(updatedFlameGraph, 2, "JsFlameGraphView rerendered 2 times.");
 
   yield teardown(panel);
--- a/browser/devtools/performance/test/browser_perf-recording-selected-04.js
+++ b/browser/devtools/performance/test/browser_perf-recording-selected-04.js
@@ -3,20 +3,33 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that all components can get rerendered for a profile when switching.
  */
 
 let test = Task.async(function*() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
-  let { $, EVENTS, PerformanceController, DetailsSubview, RecordingsView } = panel.panelWin;
+  let { $, EVENTS, PerformanceController, DetailsView, DetailsSubview, RecordingsView } = panel.panelWin;
+
+  // Enable memory to test the memory-calltree and memory-flamegraph.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
 
+  // Need to allow widgets to be updated while hidden, otherwise we can't use
+  // `waitForWidgetsRendered`.
   DetailsSubview.canUpdateWhileHidden = true;
 
+  // Cycle through all the views to initialize them, otherwise we can't use
+  // `waitForWidgetsRendered`. The waterfall is shown by default, but all the
+  // other views are created lazily, so won't emit any events.
+  yield DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("memory-calltree");
+  yield DetailsView.selectView("memory-flamegraph");
+
   yield startRecording(panel);
   yield stopRecording(panel);
 
   yield startRecording(panel);
   yield stopRecording(panel);
 
   let rerender = waitForWidgetsRendered(panel);
   RecordingsView.selectedIndex = 0;
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-states.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that view states and lazy component intialization works.
+ */
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, PerformanceView, OverviewView, DetailsView } = panel.panelWin;
+
+  is(PerformanceView.getState(), "empty",
+    "The intial state of the performance panel view is correct.");
+
+  ok(!("markersOverview" in OverviewView),
+    "The markers graph should not have been created yet.");
+  ok(!("memoryOverview" in OverviewView),
+    "The memory graph should not have been created yet.");
+  ok(!("framerateGraph" in OverviewView),
+    "The framerate graph should not have been created yet.");
+
+  ok(DetailsView.components["waterfall"].initialized,
+    "The waterfall detail view should have been created by default.");
+  ok(!DetailsView.components["js-calltree"].initialized,
+    "The js-calltree detail view should not have been created yet.");
+  ok(!DetailsView.components["js-flamegraph"].initialized,
+    "The js-flamegraph detail view should not have been created yet.");
+  ok(!DetailsView.components["memory-calltree"].initialized,
+    "The memory-calltree detail view should not have been created yet.");
+  ok(!DetailsView.components["memory-flamegraph"].initialized,
+    "The memory-flamegraph detail view should not have been created yet.");
+
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
+
+  ok(!("markersOverview" in OverviewView),
+    "The markers graph should still not have been created yet.");
+  ok(!("memoryOverview" in OverviewView),
+    "The memory graph should still not have been created yet.");
+  ok(!("framerateGraph" in OverviewView),
+    "The framerate graph should still not have been created yet.");
+
+  let stateChanged = once(PerformanceView, EVENTS.UI_STATE_CHANGED);
+  yield startRecording(panel);
+  yield stateChanged;
+
+  is(PerformanceView.getState(), "recording",
+    "The current state of the performance panel view is 'recording'.");
+  ok(OverviewView.memoryOverview,
+    "The memory graph should have been created now.");
+  ok(OverviewView.framerateGraph,
+    "The framerate graph should have been created now.");
+
+  stateChanged = once(PerformanceView, EVENTS.UI_STATE_CHANGED);
+  yield stopRecording(panel);
+  yield stateChanged;
+
+  is(PerformanceView.getState(), "recorded",
+    "The current state of the performance panel view is 'recorded'.");
+  ok(!DetailsView.components["js-calltree"].initialized,
+    "The js-calltree detail view should still not have been created yet.");
+  ok(!DetailsView.components["js-flamegraph"].initialized,
+    "The js-flamegraph detail view should still not have been created yet.");
+  ok(!DetailsView.components["memory-calltree"].initialized,
+    "The memory-calltree detail view should still not have been created yet.");
+  ok(!DetailsView.components["memory-flamegraph"].initialized,
+    "The memory-flamegraph detail view should still not have been created yet.");
+
+  yield DetailsView.selectView("js-calltree");
+
+  is(PerformanceView.getState(), "recorded",
+    "The current state of the performance panel view is still 'recorded'.");
+  ok(DetailsView.components["js-calltree"].initialized,
+    "The js-calltree detail view should still have been created now.");
+  ok(!DetailsView.components["js-flamegraph"].initialized,
+    "The js-flamegraph detail view should still not have been created yet.");
+  ok(!DetailsView.components["memory-calltree"].initialized,
+    "The memory-calltree detail view should still not have been created yet.");
+  ok(!DetailsView.components["memory-flamegraph"].initialized,
+    "The memory-flamegraph detail view should still not have been created yet.");
+
+  yield DetailsView.selectView("memory-calltree");
+
+  is(PerformanceView.getState(), "recorded",
+    "The current state of the performance panel view is still 'recorded'.");
+  ok(DetailsView.components["js-calltree"].initialized,
+    "The js-calltree detail view should still register as being created.");
+  ok(!DetailsView.components["js-flamegraph"].initialized,
+    "The js-flamegraph detail view should still not have been created yet.");
+  ok(DetailsView.components["memory-calltree"].initialized,
+    "The memory-calltree detail view should still have been created now.");
+  ok(!DetailsView.components["memory-flamegraph"].initialized,
+    "The memory-flamegraph detail view should still not have been created yet.");
+
+  yield teardown(panel);
+  finish();
+}
--- a/browser/devtools/performance/test/browser_perf_recordings-io-01.js
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-01.js
@@ -2,18 +2,31 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests if the performance tool is able to save and load recordings.
  */
 
 let test = Task.async(function*() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, PerformanceController, DetailsSubview } = panel.panelWin;
+  let { EVENTS, PerformanceController, DetailsView, DetailsSubview } = panel.panelWin;
+
+  // Enable memory to test the memory-calltree and memory-flamegraph.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
 
+  // Cycle through all the views to initialize them, otherwise we can't use
+  // `waitForWidgetsRendered`. The waterfall is shown by default, but all the
+  // other views are created lazily, so won't emit any events.
+  yield DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("memory-calltree");
+  yield DetailsView.selectView("memory-flamegraph");
+
+  // Need to allow widgets to be updated while hidden, otherwise we can't use
+  // `waitForWidgetsRendered`.
   DetailsSubview.canUpdateWhileHidden = true;
 
   yield startRecording(panel);
   yield stopRecording(panel);
 
   // Verify original recording.
 
   let originalData = PerformanceController.getCurrentRecording().getAllData();
--- a/browser/devtools/performance/test/browser_perf_recordings-io-04.js
+++ b/browser/devtools/performance/test/browser_perf_recordings-io-04.js
@@ -3,18 +3,31 @@
 
 /**
  * Tests if the performance tool can import profiler data from the
  * original profiler tool.
  */
 
 let test = Task.async(function*() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, PerformanceController, DetailsSubview } = panel.panelWin;
+  let { EVENTS, PerformanceController, DetailsView, DetailsSubview } = panel.panelWin;
+
+  // Enable memory to test the memory-calltree and memory-flamegraph.
+  Services.prefs.setBoolPref(MEMORY_PREF, true);
 
+  // Cycle through all the views to initialize them, otherwise we can't use
+  // `waitForWidgetsRendered`. The waterfall is shown by default, but all the
+  // other views are created lazily, so won't emit any events.
+  yield DetailsView.selectView("js-calltree");
+  yield DetailsView.selectView("js-flamegraph");
+  yield DetailsView.selectView("memory-calltree");
+  yield DetailsView.selectView("memory-flamegraph");
+
+  // Need to allow widgets to be updated while hidden, otherwise we can't use
+  // `waitForWidgetsRendered`.
   DetailsSubview.canUpdateWhileHidden = true;
 
   yield startRecording(panel);
   yield stopRecording(panel);
 
   // Get data from the current profiler
   let data = PerformanceController.getCurrentRecording().getAllData();
 
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -250,31 +250,38 @@ function* startRecording(panel) {
   let win = panel.panelWin;
   let clicked = panel.panelWin.PerformanceView.once(win.EVENTS.UI_START_RECORDING);
   let willStart = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_WILL_START);
   let hasStarted = panel.panelWin.PerformanceController.once(win.EVENTS.RECORDING_STARTED);
   let button = win.$("#main-record-button");
 
   ok(!button.hasAttribute("checked"),
     "The record button should not be checked yet.");
-
   ok(!button.hasAttribute("locked"),
     "The record button should not be locked yet.");
 
   click(win, button);
-
   yield clicked;
 
   ok(button.hasAttribute("checked"),
     "The record button should now be checked.");
   ok(button.hasAttribute("locked"),
     "The record button should be locked.");
 
   yield willStart;
+  let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
+
   yield hasStarted;
+  let overviewRendered = once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED);
+
+  yield stateChanged;
+  yield overviewRendered;
+
+  is(win.PerformanceView.getState(), "recording",
+    "The current state is 'recording'.");
 
   ok(button.hasAttribute("checked"),
     "The record button should still be checked.");
   ok(!button.hasAttribute("locked"),
     "The record button should not be locked.");
 }
 
 function* stopRecording(panel) {
@@ -285,50 +292,62 @@ function* stopRecording(panel) {
   let button = win.$("#main-record-button");
 
   ok(button.hasAttribute("checked"),
     "The record button should already be checked.");
   ok(!button.hasAttribute("locked"),
     "The record button should not be locked yet.");
 
   click(win, button);
-
   yield clicked;
 
   ok(!button.hasAttribute("checked"),
     "The record button should not be checked.");
   ok(button.hasAttribute("locked"),
     "The record button should be locked.");
 
   yield willStop;
+  let stateChanged = once(win.PerformanceView, win.EVENTS.UI_STATE_CHANGED);
+
   yield hasStopped;
+  let overviewRendered = once(win.OverviewView, win.EVENTS.OVERVIEW_RENDERED);
+
+  yield stateChanged;
+  yield overviewRendered;
+
+  is(win.PerformanceView.getState(), "recorded",
+    "The current state is 'recorded'.");
 
   ok(!button.hasAttribute("checked"),
     "The record button should not be checked.");
   ok(!button.hasAttribute("locked"),
     "The record button should not be locked.");
 }
 
 function waitForWidgetsRendered(panel) {
   let {
     EVENTS,
     OverviewView,
     WaterfallView,
     JsCallTreeView,
-    JsFlameGraphView
+    JsFlameGraphView,
+    MemoryCallTreeView,
+    MemoryFlameGraphView,
   } = panel.panelWin;
 
   return Promise.all([
     once(OverviewView, EVENTS.MARKERS_GRAPH_RENDERED),
     once(OverviewView, EVENTS.MEMORY_GRAPH_RENDERED),
     once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
     once(OverviewView, EVENTS.OVERVIEW_RENDERED),
     once(WaterfallView, EVENTS.WATERFALL_RENDERED),
     once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED),
-    once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED)
+    once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED),
+    once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED),
+    once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED),
   ]);
 }
 
 /**
  * Waits until a predicate returns true.
  *
  * @param function predicate
  *        Invoked once in a while until it returns true.
--- a/browser/devtools/performance/views/details-abstract-subview.js
+++ b/browser/devtools/performance/views/details-abstract-subview.js
@@ -97,21 +97,20 @@ let DetailsSubview = {
       this.render(OverviewView.getTimeInterval());
       this.shouldUpdateWhenShown = false;
     }
   },
 
   /**
    * Fired when a preference in `devtools.performance.ui.` is changed.
    */
-  _onPrefChanged: function (_, prefName, value) {
+  _onPrefChanged: function (_, prefName) {
     // All detail views require a recording to be complete, so do not
     // attempt to render if recording is in progress or does not exist.
     let recording = PerformanceController.getCurrentRecording();
-
     if (!recording || recording.isRecording()) {
       return;
     }
 
     if (!~this.rerenderPrefs.indexOf(prefName)) {
       return;
     }
 
--- a/browser/devtools/performance/views/details.js
+++ b/browser/devtools/performance/views/details.js
@@ -30,79 +30,81 @@ let DetailsView = {
 
     this._onViewToggle = this._onViewToggle.bind(this);
     this.setAvailableViews = this.setAvailableViews.bind(this);
 
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.addEventListener("command", this._onViewToggle);
     }
 
-    for (let [_, { view }] of Iterator(this.components)) {
-      yield view.initialize();
-    }
+    yield this.selectView(DEFAULT_DETAILS_SUBVIEW);
+    this.setAvailableViews();
 
-    this.selectView(DEFAULT_DETAILS_SUBVIEW);
-    this.setAvailableViews();
     PerformanceController.on(EVENTS.PREF_CHANGED, this.setAvailableViews);
   }),
 
   /**
    * Unbinds events, destroys subviews.
    */
   destroy: Task.async(function *() {
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       button.removeEventListener("command", this._onViewToggle);
     }
 
-    for (let [_, { view }] of Iterator(this.components)) {
-      yield view.destroy();
+    for (let [_, component] of Iterator(this.components)) {
+      component.initialized && (yield component.view.destroy());
     }
+
     PerformanceController.off(EVENTS.PREF_CHANGED, this.setAvailableViews);
   }),
 
   /**
    * Sets the possible views based off of prefs by hiding/showing the
    * buttons that select them and going to default view if currently selected.
    * Called when a preference changes in `devtools.performance.ui.`.
    */
   setAvailableViews: function () {
     for (let [name, { view, pref }] of Iterator(this.components)) {
       if (!pref) {
         continue;
       }
       let value = PerformanceController.getPref(pref);
       $(`toolbarbutton[data-view=${name}]`).hidden = !value;
 
-      // If the view is currently selected and not enabled, go back to the default view
+      // If the view is currently selected and not enabled, go back to the
+      // default view.
       if (!value && this.isViewSelected(view)) {
         this.selectView(DEFAULT_DETAILS_SUBVIEW);
       }
     }
   },
 
   /**
    * Select one of the DetailView's subviews to be rendered,
    * hiding the others.
    *
    * @param String viewName
    *        Name of the view to be shown.
    */
-  selectView: function (viewName) {
-    this.el.selectedPanel = $("#" + this.components[viewName].id);
+  selectView: Task.async(function *(viewName) {
+    let component = this.components[viewName];
+    this.el.selectedPanel = $("#" + component.id);
+
+    yield this._whenViewInitialized(component);
 
     for (let button of $$("toolbarbutton[data-view]", this.toolbar)) {
       if (button.getAttribute("data-view") === viewName) {
         button.setAttribute("checked", true);
       } else {
         button.removeAttribute("checked");
       }
     }
 
     this.emit(EVENTS.DETAILS_VIEW_SELECTED, viewName);
-  },
+  }),
 
   /**
    * Checks if the provided view is currently selected.
    *
    * @param object viewObject
    * @return boolean
    */
   isViewSelected: function(viewObject) {
@@ -129,16 +131,40 @@ let DetailsView = {
     if (this.isViewSelected(viewObject)) {
       return promise.resolve();
     }
     yield this.once(EVENTS.DETAILS_VIEW_SELECTED);
     return this.whenViewSelected(viewObject);
   }),
 
   /**
+   * Initializes a subview if it wasn't already set up, and makes sure
+   * it's populated with recording data if there is some available.
+   *
+   * @param object component
+   *        A component descriptor from DetailsView.components
+   */
+  _whenViewInitialized: Task.async(function *(component) {
+    if (component.initialized) {
+      return;
+    }
+    component.initialized = true;
+    yield component.view.initialize();
+
+    // If this view is initialized *after* a recording is shown, it won't display
+    // any data. Make sure it's populated by setting `shouldUpdateWhenShown`.
+    // All detail views require a recording to be complete, so do not
+    // attempt to render if recording is in progress or does not exist.
+    let recording = PerformanceController.getCurrentRecording();
+    if (recording && !recording.isRecording()) {
+      component.view.shouldUpdateWhenShown = true;
+    }
+  }),
+
+  /**
    * Called when a view button is clicked.
    */
   _onViewToggle: function (e) {
     this.selectView(e.target.getAttribute("data-view"));
   }
 };
 
 /**
--- a/browser/devtools/performance/views/overview.js
+++ b/browser/devtools/performance/views/overview.js
@@ -20,59 +20,45 @@ const MEMORY_GRAPH_HEIGHT = 30; // px
 /**
  * View handler for the overview panel's time view, displaying
  * framerate, markers and memory over time.
  */
 let OverviewView = {
   /**
    * Sets up the view with event binding.
    */
-  initialize: Task.async(function *() {
+  initialize: function () {
     this._onRecordingWillStart = this._onRecordingWillStart.bind(this);
     this._onRecordingStarted = this._onRecordingStarted.bind(this);
     this._onRecordingWillStop = this._onRecordingWillStop.bind(this);
     this._onRecordingStopped = this._onRecordingStopped.bind(this);
     this._onRecordingSelected = this._onRecordingSelected.bind(this);
     this._onRecordingTick = this._onRecordingTick.bind(this);
     this._onGraphSelecting = this._onGraphSelecting.bind(this);
     this._onPrefChanged = this._onPrefChanged.bind(this);
 
-    yield this._showMarkersGraph();
-    yield this._showMemoryGraph();
-    yield this._showFramerateGraph();
-
-    this.markersOverview.on("selecting", this._onGraphSelecting);
-
-    PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
-
-    // Toggle the initial state of memory and framerate graph based off of
-    // the prefs.
+    // Toggle the initial visibility of memory and framerate graph containers
+    // based off of prefs.
     $("#memory-overview").hidden = !PerformanceController.getPref("enable-memory");
     $("#time-framerate").hidden = !PerformanceController.getPref("enable-framerate");
 
     PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
+    PerformanceController.on(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
     PerformanceController.on(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
     PerformanceController.on(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
     PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
-
-    // Populate this overview with some dummy initial data.
-    this.markersOverview.setData({ duration: 1000, markers: [] });
-    this.memoryOverview.setData([]);
-    this.framerateGraph.setData([]);
-  }),
+  },
 
   /**
    * Unbinds events.
    */
   destroy: function () {
-    this.markersOverview.off("selecting", this._onGraphSelecting);
-
+    PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceController.off(EVENTS.RECORDING_WILL_START, this._onRecordingWillStart);
-    PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
     PerformanceController.off(EVENTS.RECORDING_WILL_STOP, this._onRecordingWillStop);
     PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
   },
 
   /**
    * Sets the time interval selection for all graphs in this overview.
@@ -106,86 +92,108 @@ let OverviewView = {
     }
     let mapStart = () => 0;
     let mapEnd = () => recording.getDuration();
     let selection = this.markersOverview.getMappedSelection({ mapStart, mapEnd });
     return { startTime: selection.min, endTime: selection.max };
   },
 
   /**
-   * Sets up the framerate graph.
+   * Sets up the markers overivew graph, if needed.
+   *
+   * @return object
+   *         A promise resolved to `true` when the graph was initialized.
    */
-  _showFramerateGraph: Task.async(function *() {
-    this.framerateGraph = new LineGraphWidget($("#time-framerate"), {
-      metric: L10N.getStr("graphs.fps")
-    });
-    this.framerateGraph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
-    yield this.framerateGraph.ready();
-  }),
-
-  /**
-   * Sets up the markers overivew graph.
-   */
-  _showMarkersGraph: Task.async(function *() {
+  _markersGraphAvailable: Task.async(function *() {
+    if (this.markersOverview) {
+      yield this.markersOverview.ready();
+      return true;
+    }
     this.markersOverview = new MarkersOverview($("#markers-overview"), TIMELINE_BLUEPRINT);
     this.markersOverview.headerHeight = MARKERS_GRAPH_HEADER_HEIGHT;
     this.markersOverview.rowHeight = MARKERS_GRAPH_ROW_HEIGHT;
     this.markersOverview.groupPadding = MARKERS_GROUP_VERTICAL_PADDING;
+    this.markersOverview.on("selecting", this._onGraphSelecting);
     yield this.markersOverview.ready();
+    return true;
   }),
 
   /**
-   * Sets up the memory overview graph.
+   * Sets up the memory overview graph, if allowed and needed.
+   *
+   * @return object
+   *         A promise resolved to `true` if the graph was initialized and is
+   *         ready to use, `false` if the graph is disabled.
    */
-  _showMemoryGraph: Task.async(function *() {
+  _memoryGraphAvailable: Task.async(function *() {
+    if (!PerformanceController.getPref("enable-memory")) {
+      return false;
+    }
+    if (this.memoryOverview) {
+      yield this.memoryOverview.ready();
+      return true;
+    }
     this.memoryOverview = new MemoryOverview($("#memory-overview"));
     this.memoryOverview.fixedHeight = MEMORY_GRAPH_HEIGHT;
     yield this.memoryOverview.ready();
 
     CanvasGraphUtils.linkAnimation(this.markersOverview, this.memoryOverview);
     CanvasGraphUtils.linkSelection(this.markersOverview, this.memoryOverview);
+    return true;
   }),
 
   /**
-   * Sets up the framerate graph.
+   * Sets up the framerate graph, if allowed and needed.
+   *
+   * @return object
+   *         A promise resolved to `true` if the graph was initialized and is
+   *         ready to use, `false` if the graph is disabled.
    */
-  _showFramerateGraph: Task.async(function *() {
+  _framerateGraphAvailable: Task.async(function *() {
+    if (!PerformanceController.getPref("enable-framerate")) {
+      return false;
+    }
+    if (this.framerateGraph) {
+      yield this.framerateGraph.ready();
+      return true;
+    }
     let metric = L10N.getStr("graphs.fps");
     this.framerateGraph = new LineGraphWidget($("#time-framerate"), { metric });
     this.framerateGraph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
     yield this.framerateGraph.ready();
 
     CanvasGraphUtils.linkAnimation(this.markersOverview, this.framerateGraph);
     CanvasGraphUtils.linkSelection(this.markersOverview, this.framerateGraph);
+    return true;
   }),
 
   /**
    * Method for handling all the set up for rendering the overview graphs.
    *
    * @param number resolution
    *        The fps graph resolution. @see Graphs.jsm
    */
   render: Task.async(function *(resolution) {
     let recording = PerformanceController.getCurrentRecording();
     let duration = recording.getDuration();
     let markers = recording.getMarkers();
     let memory = recording.getMemory();
     let timestamps = recording.getTicks();
 
     // Empty or older recordings might yield no markers, memory or timestamps.
-    if (markers) {
+    if (markers && (yield this._markersGraphAvailable())) {
       this.markersOverview.setData({ markers, duration });
       this.emit(EVENTS.MARKERS_GRAPH_RENDERED);
     }
-    if (memory) {
+    if (memory && (yield this._memoryGraphAvailable())) {
       this.memoryOverview.dataDuration = duration;
       this.memoryOverview.setData(memory);
       this.emit(EVENTS.MEMORY_GRAPH_RENDERED);
     }
-    if (timestamps) {
+    if (timestamps && (yield this._framerateGraphAvailable())) {
       this.framerateGraph.dataDuration = duration;
       yield this.framerateGraph.setDataFromTimestamps(timestamps, resolution);
       this.emit(EVENTS.FRAMERATE_GRAPH_RENDERED);
     }
 
     // Finished rendering all graphs in this overview.
     this.emit(EVENTS.OVERVIEW_RENDERED);
   }),
@@ -196,16 +204,27 @@ let OverviewView = {
    * data into all the corresponding overview graphs.
    */
   _onRecordingTick: Task.async(function *() {
     yield this.render(FRAMERATE_GRAPH_LOW_RES_INTERVAL);
     this._prepareNextTick();
   }),
 
   /**
+   * Called to refresh the timer to keep firing _onRecordingTick.
+   */
+  _prepareNextTick: function () {
+    // Check here to see if there's still a _timeoutId, incase
+    // `stop` was called before the _prepareNextTick call was executed.
+    if (this._timeoutId) {
+      this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
+    }
+  },
+
+  /**
    * Fired when the graph selection has changed. Called by
    * mouseup and scroll events.
    */
   _onGraphSelecting: function () {
     let recording = PerformanceController.getCurrentRecording();
     if (recording == null || this._stopSelectionChangeEventPropagation) {
       return;
     }
@@ -215,33 +234,22 @@ let OverviewView = {
     if (interval.endTime - interval.startTime < 1) {
       this.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
     } else {
       this.emit(EVENTS.OVERVIEW_RANGE_SELECTED, interval);
     }
   },
 
   /**
-   * Called to refresh the timer to keep firing _onRecordingTick.
-   */
-  _prepareNextTick: function () {
-    // Check here to see if there's still a _timeoutId, incase
-    // `stop` was called before the _prepareNextTick call was executed.
-    if (this._timeoutId) {
-      this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
-    }
-  },
-
-  /**
    * Called when recording will start.
    */
-  _onRecordingWillStart: function (_, recording) {
-    this._checkSelection(recording);
-    this.framerateGraph.dropSelection();
-  },
+  _onRecordingWillStart: Task.async(function* (_, recording) {
+    yield this._checkSelection(recording);
+    this.markersOverview.dropSelection();
+  }),
 
   /**
    * Called when recording actually starts.
    */
   _onRecordingStarted: function (_, recording) {
     this._timeoutId = setTimeout(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
   },
 
@@ -251,50 +259,60 @@ let OverviewView = {
   _onRecordingWillStop: function(_, recording) {
     clearTimeout(this._timeoutId);
     this._timeoutId = null;
   },
 
   /**
    * Called when recording actually stops.
    */
-  _onRecordingStopped: function (_, recording) {
-    this._checkSelection(recording);
+  _onRecordingStopped: Task.async(function* (_, recording) {
     this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
-  },
+    yield this._checkSelection(recording);
+  }),
 
   /**
    * Called when a new recording is selected.
    */
-  _onRecordingSelected: function (_, recording) {
+  _onRecordingSelected: Task.async(function* (_, recording) {
     if (!recording) {
       return;
     }
-    this.markersOverview.dropSelection();
-    this._checkSelection(recording);
-
     // If timeout exists, we have something recording, so
     // this will still tick away at rendering. Otherwise, force a render.
     if (!this._timeoutId) {
-      this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
+      yield this.render(FRAMERATE_GRAPH_HIGH_RES_INTERVAL);
     }
-  },
+    yield this._checkSelection(recording);
+    this.markersOverview.dropSelection();
+  }),
 
-  _checkSelection: function (recording) {
+  /**
+   * Makes sure the selection is enabled or disabled in all the graphs,
+   * based on whether a recording currently exists and is not in progress.
+   */
+  _checkSelection: Task.async(function* (recording) {
     let selectionEnabled = !recording.isRecording();
-    this.markersOverview.selectionEnabled = selectionEnabled;
-    this.memoryOverview.selectionEnabled = selectionEnabled;
-    this.framerateGraph.selectionEnabled = selectionEnabled;
-  },
+
+    if (yield this._markersGraphAvailable()) {
+      this.markersOverview.selectionEnabled = selectionEnabled;
+    }
+    if (yield this._memoryGraphAvailable()) {
+      this.memoryOverview.selectionEnabled = selectionEnabled;
+    }
+    if (yield this._framerateGraphAvailable()) {
+      this.framerateGraph.selectionEnabled = selectionEnabled;
+    }
+  }),
 
   /**
    * Called whenever a preference in `devtools.performance.ui.` changes. Used
    * to toggle the visibility of memory and framerate graphs.
    */
-  _onPrefChanged: function (_, prefName, value) {
+  _onPrefChanged: function (_, prefName) {
     if (prefName === "enable-memory") {
       $("#memory-overview").hidden = !PerformanceController.getPref("enable-memory");
     }
     if (prefName === "enable-framerate") {
       $("#time-framerate").hidden = !PerformanceController.getPref("enable-framerate");
     }
   }
 };