Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 22 May 2015 11:11:20 -0400
changeset 266182 acb065aa387e86c8214fe047b9cac7fddc421601
parent 266181 b6a1b130eb1b08d29a2a79ee3160c59b8e53e54d (current diff)
parent 266108 0b01db3f7238c0848a0d18046602b0f8bc908dcb (diff)
child 266264 a69094e0f2a47486a85c9d65a6ef3cd7693c1789
push id2231
push usermichael.l.comella@gmail.com
push dateFri, 22 May 2015 20:04:59 +0000
reviewersmerge
milestone41.0a1
Merge fx-team to m-c. a=merge
browser/devtools/performance/test/browser_perf-aaa-run-first-leaktest.js
browser/devtools/profiler/moz.build
browser/devtools/profiler/panel.js
browser/devtools/profiler/profiler.js
browser/devtools/profiler/profiler.xul
browser/devtools/profiler/test/browser.ini
browser/devtools/profiler/test/browser_profiler_aaa_run_first_leaktest.js
browser/devtools/profiler/test/browser_profiler_console-record-01.js
browser/devtools/profiler/test/browser_profiler_console-record-02.js
browser/devtools/profiler/test/browser_profiler_console-record-03.js
browser/devtools/profiler/test/browser_profiler_console-record-04.js
browser/devtools/profiler/test/browser_profiler_console-record-05.js
browser/devtools/profiler/test/browser_profiler_console-record-06.js
browser/devtools/profiler/test/browser_profiler_console-record-07.js
browser/devtools/profiler/test/browser_profiler_console-record-08.js
browser/devtools/profiler/test/browser_profiler_console-record-09.js
browser/devtools/profiler/test/browser_profiler_data-massaging-01.js
browser/devtools/profiler/test/browser_profiler_data-massaging-02.js
browser/devtools/profiler/test/browser_profiler_data-samples.js
browser/devtools/profiler/test/browser_profiler_gecko-pref-changed.js
browser/devtools/profiler/test/browser_profiler_jump-to-debugger-01.js
browser/devtools/profiler/test/browser_profiler_jump-to-debugger-02.js
browser/devtools/profiler/test/browser_profiler_profile-deselection.js
browser/devtools/profiler/test/browser_profiler_profile-view-events.js
browser/devtools/profiler/test/browser_profiler_recording-cancelled.js
browser/devtools/profiler/test/browser_profiler_recording-selected-01.js
browser/devtools/profiler/test/browser_profiler_recording-selected-02.js
browser/devtools/profiler/test/browser_profiler_recording-selected-03.js
browser/devtools/profiler/test/browser_profiler_recording-utils.js
browser/devtools/profiler/test/browser_profiler_recordings-clear.js
browser/devtools/profiler/test/browser_profiler_recordings-io-01.js
browser/devtools/profiler/test/browser_profiler_recordings-io-02.js
browser/devtools/profiler/test/browser_profiler_recordings-io-03.js
browser/devtools/profiler/test/browser_profiler_shared-connection-01.js
browser/devtools/profiler/test/browser_profiler_shared-connection-02.js
browser/devtools/profiler/test/browser_profiler_shared-connection-03.js
browser/devtools/profiler/test/browser_profiler_shared-connection-04.js
browser/devtools/profiler/test/browser_profiler_shared-front-01.js
browser/devtools/profiler/test/browser_profiler_shared-front-02.js
browser/devtools/profiler/test/browser_profiler_shared-front-03.js
browser/devtools/profiler/test/browser_profiler_shared-front-04.js
browser/devtools/profiler/test/browser_profiler_shared-front-05.js
browser/devtools/profiler/test/browser_profiler_shared-front-06.js
browser/devtools/profiler/test/browser_profiler_simple-record-01.js
browser/devtools/profiler/test/browser_profiler_simple-record-02.js
browser/devtools/profiler/test/browser_profiler_simple-record-03.js
browser/devtools/profiler/test/browser_profiler_sudden-deactivation-01.js
browser/devtools/profiler/test/browser_profiler_sudden-deactivation-02.js
browser/devtools/profiler/test/browser_profiler_tabbed-browser-01.js
browser/devtools/profiler/test/browser_profiler_tabbed-browser-02.js
browser/devtools/profiler/test/browser_profiler_tabbed-browser-03.js
browser/devtools/profiler/test/browser_profiler_tabbed-browser-add-remove-01.js
browser/devtools/profiler/test/browser_profiler_tabbed-browser-add-remove-02.js
browser/devtools/profiler/test/doc_simple-test.html
browser/devtools/profiler/test/head.js
browser/devtools/profiler/ui-profile.js
browser/devtools/profiler/ui-recordings.js
browser/devtools/profiler/utils/shared.js
browser/themes/linux/devtools/profiler.css
browser/themes/osx/devtools/profiler.css
browser/themes/shared/devtools/profiler.inc.css
browser/themes/windows/devtools/profiler.css
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -302,21 +302,29 @@ function onSearchSubmit(aEvent)
 {
   let searchText = document.getElementById("searchText");
   let searchTerms = searchText.value;
   let engineName = document.documentElement.getAttribute("searchEngineName");
 
   if (engineName && searchTerms.length > 0) {
     // Send an event that will perform a search and Firefox Health Report will
     // record that a search from about:home has occurred.
-    let useNewTab = aEvent && aEvent.button == 1;
     let eventData = {
       engineName: engineName,
       searchTerms: searchTerms,
-      useNewTab: useNewTab,
+      originalEvent: {
+        target: {
+          ownerDocument: null
+        },
+        shiftKey: aEvent.shiftKey,
+        ctrlKey: aEvent.ctrlKey,
+        metaKey: aEvent.metaKey,
+        altKey: aEvent.altKey,
+        button: aEvent.button,
+      },
     };
 
     if (searchText.hasAttribute("selection-index")) {
       eventData.selection = {
         index: searchText.getAttribute("selection-index"),
         kind: searchText.getAttribute("selection-kind")
       };
     }
--- a/browser/base/content/newtab/intro.js
+++ b/browser/base/content/newtab/intro.js
@@ -187,17 +187,18 @@ let gIntro = {
       "4": [customizeIcon, this._bold(newTabString("intro.controls"))],
       "6": [this._bold(newTabString("intro.paragraph6.remove")), this._bold(newTabString("intro.paragraph6.pin"))],
       "7": [this._link(TILES_INTRO_LINK, newTabString("learn.link"))],
       "8": [this._brandShortName, this._link(TILES_INTRO_LINK, newTabString("learn.link"))]
     }
 
     for (let i = 1; i <= MAX_PARAGRAPH_ID; i++) {
       try {
-        this._paragraphs.push(newTabString("intro.paragraph" + i, substringMappings[i]));
+        let name = "intro.paragraph" + i + (i == 4 ? ".2" : "");
+        this._paragraphs.push(newTabString(name, substringMappings[i]));
       } catch (ex) {
         // Paragraph with this ID doesn't exist so continue
       }
     }
   },
 
   showIfNecessary: function() {
     if (!Services.prefs.getBoolPref(PREF_INTRO_SHOWN)) {
--- a/browser/base/content/newtab/search.js
+++ b/browser/base/content/newtab/search.js
@@ -49,17 +49,26 @@ let gSearch = {
     let searchText = this._nodes.text;
     let searchStr = searchText.value;
     if (this.currentEngineName && searchStr.length) {
       let useNewTab = event && event.button == 1;
       let eventData = {
         engineName: this.currentEngineName,
         searchString: searchStr,
         whence: "newtab",
-        useNewTab: useNewTab,
+        originalEvent: {
+          target: {
+            ownerDocument: null
+          },
+          shiftKey: event.shiftKey,
+          ctrlKey: event.ctrlKey,
+          metaKey: event.metaKey,
+          altKey: event.altKey,
+          button: event.button,
+        },
       }
 
       if (searchText.hasAttribute("selection-index")) {
         eventData.selection = {
           index: searchText.getAttribute("selection-index"),
           kind: searchText.getAttribute("selection-kind")
         };
       }
--- a/browser/components/loop/.eslintrc
+++ b/browser/components/loop/.eslintrc
@@ -14,49 +14,53 @@
   },
   "globals": {
     "_": false,
     "$": false,
     "Backbone": false,
     "chai": false,
     "console": false,
     "jQuery": false,
-    "loop": false,
+    "loop": true,
     "MozActivity": false,
+    "mozRTCSessionDescription": false,
     "OT": false,
+    "performance": false,
     "Promise": false,
     "React": false,
     "sinon": false
   },
   "rules": {
     // turn off all kinds of stuff that we actually do want, because
     // right now, we're bootstrapping the linting infrastructure.  We'll
     // want to audit these rules, and start turning them on and fixing the
     // problems they find, one at a time.
 
     // Eslint built-in rules are documented at <http://eslint.org/docs/rules/>
     "camelcase": 0,               // TODO: Remove (use default)
     "consistent-return": 0,       // TODO: Remove (use default)
+    dot-location: 0,              // [2, property],
     "eqeqeq": 0,                  // TBD. Might need to be separate for content & chrome
     "global-strict": 0,           // Leave as zero (this will be unsupported in eslint 1.0.0)
     "key-spacing": 0,             // TODO: Remove (use default)
+    "linebreak-style": [2, "unix"],
     "new-cap": 0,                 // TODO: Remove (use default)
     "no-catch-shadow": 0,         // TODO: Remove (use default)
     "no-console": 0,              // Leave as 0. We use console logging in content code.
     "no-empty": 0,                // TODO: Remove (use default)
     "no-extra-bind": 0,           // Leave as 0
     "no-extra-boolean-cast": 0,   // TODO: Remove (use default)
     "no-multi-spaces": 0,         // TBD.
     "no-new": 0,                  // TODO: Remove (use default)
     "no-redeclare": 0,            // TODO: Remove (use default)
     "no-return-assign": 0,        // TODO: Remove (use default)
     "no-shadow": 0,               // TODO: Remove (use default)
     "no-spaced-func": 0,          // TODO: Remove (use default)
-    "no-undef": 0,                // TODO: Remove (use default)
     "no-underscore-dangle": 0,    // Leave as 0. Commonly used for private variables.
+    "no-unneeded-ternary": 2,
     "no-unused-expressions": 0,   // TODO: Remove (use default)
     "no-unused-vars": 0,          // TODO: Remove (use default)
     "no-use-before-define": 0,    // TODO: Remove (use default)
     "no-wrap-func": 0,            // TODO: Remove (use default)
     "quotes": 0,                  // [2, "double", "avoid-escape"],
     "strict": 0,                  // [2, "function"],
     // eslint-plugin-react rules. These are documented at
     // <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/.eslintrc-gecko
@@ -0,0 +1,64 @@
+// This file defines additional items and rules specific for gecko files.
+// This is applied on top of the basic .eslintrc for gecko specific directories
+// e.g. the modules directory.
+{
+  "ecmaFeatures": {
+    "arrowFunctions": true,
+    "blockBindings": true,
+    "destructuring": true,
+    "generators": true,
+    "restParams": true,
+    "spread": true,
+    "objectLiteralShorthandMethods": true,
+  },
+  "globals": {
+    // Gecko + Loop Globals.
+    "CardDavImporter": true,
+    "Chat": false,
+    "ChromeWorker": false,
+    "CommonUtils": false,
+    "Components": false,
+    "convertToRTCStatsReport": false,
+    "CustomizableUI": false,
+    "deriveHawkCredentials": false,
+    "eventEmitter": false,
+    "FxAccountsOAuthClient": false,
+    "FxAccountsProfileClient": false,
+    "gBrowser": false,
+    "gDNSService": false,
+    "gLoopBundle": false,
+    "GoogleImporter": true,
+    "gWM": false,
+    "HawkClient": false,
+    "injectLoopAPI": true,
+    "Iterator": false,
+    "Log": false,
+    "log": true,
+    "LOOP_SESSION_TYPE": true,
+    "LoopCalls": true,
+    "LoopContacts": true,
+    "loopCrypto": false,
+    "LoopRooms": true,
+    "LoopRoomsCache": true,
+    "LoopStorage": true,
+    "MozLoopPushHandler": true,
+    "MozLoopService": true,
+    "OS": false,
+    "roomsPushNotification": true,
+    "Services": false,
+    "Social": false,
+    "SocialShare": false,
+    "Task": false,
+    "UITour": false,
+    "XPCOMUtils": false,
+    "uuidgen": true,
+    // Test Related
+    "Assert": false,
+  },
+  "rules": {
+    "generator-star-spacing": [2, "after"],
+    // We should fix the errors and enable this (set to 2)
+    "no-var": 0,
+    "strict": [2, "global"]
+  }
+}
--- a/browser/components/loop/content/js/client.js
+++ b/browser/components/loop/content/js/client.js
@@ -1,15 +1,12 @@
 /* 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/. */
 
-/* jshint esnext:true */
-/* global loop:true, hawk, deriveHawkCredentials */
-
 var loop = loop || {};
 loop.Client = (function($) {
   "use strict";
 
   // THe expected properties to be returned from the POST /calls request.
   var expectedPostCallProperties = [
     "apiKey", "callId", "progressURL",
     "sessionId", "sessionToken", "websocketToken"
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/*jshint newcap:false*/
-/*global loop:true, React */
-
 var loop = loop || {};
 loop.contacts = (function(_, mozL10n) {
   "use strict";
 
   var sharedMixins = loop.shared.mixins;
 
   const Button = loop.shared.views.Button;
   const ButtonGroup = loop.shared.views.ButtonGroup;
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/*jshint newcap:false*/
-/*global loop:true, React */
-
 var loop = loop || {};
 loop.contacts = (function(_, mozL10n) {
   "use strict";
 
   var sharedMixins = loop.shared.mixins;
 
   const Button = loop.shared.views.Button;
   const ButtonGroup = loop.shared.views.ButtonGroup;
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false, esnext:true */
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.conversation = (function(mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false, esnext:true */
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.conversation = (function(mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/content/js/conversationAppStore.js
+++ b/browser/components/loop/content/js/conversationAppStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 /**
  * Manages the conversation window app controller view. Used to get
  * the window data and store the window type.
  */
 loop.store.ConversationAppStore = (function() {
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/*jshint newcap:false*/
-/*global loop:true, React */
-
 var loop = loop || {};
 loop.panel = (function(_, mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/*jshint newcap:false*/
-/*global loop:true, React */
-
 var loop = loop || {};
 loop.panel = (function(_, mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/content/js/roomStore.js
+++ b/browser/components/loop/content/js/roomStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 (function(mozL10n) {
   "use strict";
 
   /**
    * Shared actions.
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.roomViews = (function(mozL10n) {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
-
 var loop = loop || {};
 loop.roomViews = (function(mozL10n) {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.actions = (function() {
   "use strict";
 
   /**
    * Actions are events that are triggered by the user, e.g. clicking a button,
    * or by an async event, e.g. status received.
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.ActiveRoomStore = (function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var crypto = loop.crypto;
--- a/browser/components/loop/content/shared/js/conversationStore.js
+++ b/browser/components/loop/content/shared/js/conversationStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 (function() {
   var sharedActions = loop.shared.actions;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
--- a/browser/components/loop/content/shared/js/crypto.js
+++ b/browser/components/loop/content/shared/js/crypto.js
@@ -1,13 +1,13 @@
 /* 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/. */
 
-/* global loop:true */
+/* global Components */
 
 var loop = loop || {};
 var inChrome = typeof Components != "undefined" && "utils" in Components;
 
 (function(rootObject) {
   "use strict";
 
   var sharedUtils;
--- a/browser/components/loop/content/shared/js/dispatcher.js
+++ b/browser/components/loop/content/shared/js/dispatcher.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 /**
  * The dispatcher for actions. This dispatches actions to stores registered
  * for those actions.
  *
  * If stores need to perform async operations for actions, they should return
  * straight away, and set up a new action for the changes if necessary.
  *
  * It is an error if a returned promise rejects - they should always pass.
--- a/browser/components/loop/content/shared/js/feedbackApiClient.js
+++ b/browser/components/loop/content/shared/js/feedbackApiClient.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.FeedbackAPIClient = (function($, _) {
   "use strict";
 
   /**
    * Feedback API client. Sends feedback data to an input.mozilla.com compatible
    * API.
    *
--- a/browser/components/loop/content/shared/js/feedbackStore.js
+++ b/browser/components/loop/content/shared/js/feedbackStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.FeedbackStore = (function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var FEEDBACK_STATES = loop.store.FEEDBACK_STATES = {
--- a/browser/components/loop/content/shared/js/feedbackViews.js
+++ b/browser/components/loop/content/shared/js/feedbackViews.js
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = loop.shared.views || {};
 loop.shared.views.FeedbackView = (function(l10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/shared/js/feedbackViews.jsx
+++ b/browser/components/loop/content/shared/js/feedbackViews.jsx
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = loop.shared.views || {};
 loop.shared.views.FeedbackView = (function(l10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js
+++ b/browser/components/loop/content/shared/js/fxOSActiveRoomStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.FxOSActiveRoomStore = (function() {
   "use strict";
   var sharedActions = loop.shared.actions;
   var ROOM_STATES = loop.store.ROOM_STATES;
 
--- a/browser/components/loop/content/shared/js/mixins.js
+++ b/browser/components/loop/content/shared/js/mixins.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.mixins = (function() {
   "use strict";
 
   /**
    * Root object, by default set to window.
    * @type {DOMWindow|Object}
--- a/browser/components/loop/content/shared/js/models.js
+++ b/browser/components/loop/content/shared/js/models.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.models = (function(l10n) {
   "use strict";
 
   /**
    * Conversation model.
    */
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.OTSdkDriver = (function() {
 
   var sharedActions = loop.shared.actions;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
--- a/browser/components/loop/content/shared/js/roomStates.js
+++ b/browser/components/loop/content/shared/js/roomStates.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.ROOM_STATES = {
     // The initial state of the room
     INIT: "room-init",
     // The store is gathering the room data
     GATHER: "room-gather",
--- a/browser/components/loop/content/shared/js/store.js
+++ b/browser/components/loop/content/shared/js/store.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.createStore = (function() {
   "use strict";
 
   var baseStorePrototype = {
     __registerActions: function(actions) {
--- a/browser/components/loop/content/shared/js/utils.js
+++ b/browser/components/loop/content/shared/js/utils.js
@@ -1,13 +1,13 @@
 /* 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/. */
 
-/* global loop:true */
+/* global Components */
 
 var loop = loop || {};
 loop.shared = loop.shared || {};
 var inChrome = typeof Components != "undefined" && "utils" in Components;
 
 (function() {
   "use strict";
 
--- a/browser/components/loop/content/shared/js/validate.js
+++ b/browser/components/loop/content/shared/js/validate.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* jshint unused:false */
-
 var loop = loop || {};
 loop.validate = (function() {
   "use strict";
 
   /**
    * Computes the difference between two arrays.
    *
    * @param  {Array} arr1 First array
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = (function(_, l10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -1,16 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = (function(_, l10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
--- a/browser/components/loop/content/shared/js/websocket.js
+++ b/browser/components/loop/content/shared/js/websocket.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.CallConnectionWebSocket = (function() {
   "use strict";
 
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   // Response timeout is 5 seconds as per API.
   var kResponseTimeout = 5000;
--- a/browser/components/loop/modules/.eslintrc
+++ b/browser/components/loop/modules/.eslintrc
@@ -1,17 +1,3 @@
 {
-  "ecmaFeatures": {
-    "arrowFunctions": true,
-    "blockBindings": true,
-    "destructuring": true,
-    "generators": true,
-    "restParams": true,
-    "spread": true,
-    "objectLiteralShorthandMethods": true,
-  },
-  "rules": {
-    "generator-star-spacing": [2, "after"],
-    // We should fix the errors and enable this (set to 2)
-    "no-var": 0,
-    "strict": [2, "global"]
-  }
+  "extends": "../.eslintrc-gecko"
 }
--- a/browser/components/loop/modules/MozLoopService.jsm
+++ b/browser/components/loop/modules/MozLoopService.jsm
@@ -185,17 +185,16 @@ let MozLoopServiceInternal = {
   get initialRegistrationDelayMilliseconds() {
     try {
       // Let a pref override this for developer & testing use.
       return Services.prefs.getIntPref("loop.initialDelay");
     } catch (x) {
       // Default to 5 seconds
       return 5000;
     }
-    return initialDelay;
   },
 
   /**
    * Retrieves MozLoopService Firefox Accounts OAuth token.
    *
    * @return {Object} OAuth token
    */
   get fxAOAuthTokenData() {
--- a/browser/components/loop/standalone/content/js/fxOSMarketplace.js
+++ b/browser/components/loop/standalone/content/js/fxOSMarketplace.js
@@ -1,9 +1,11 @@
-/** @jsx React.DOM */
+/* 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/. */
 
 var loop = loop || {};
 loop.fxOSMarketplaceViews = (function() {
   "use strict";
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
--- a/browser/components/loop/standalone/content/js/fxOSMarketplace.jsx
+++ b/browser/components/loop/standalone/content/js/fxOSMarketplace.jsx
@@ -1,9 +1,11 @@
-/** @jsx React.DOM */
+/* 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/. */
 
 var loop = loop || {};
 loop.fxOSMarketplaceViews = (function() {
   "use strict";
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
--- a/browser/components/loop/standalone/content/js/multiplexGum.js
+++ b/browser/components/loop/standalone/content/js/multiplexGum.js
@@ -1,13 +1,12 @@
 /* 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/. */
 
-
 var loop = loop || {};
 
 /**
  * Monkeypatch getUserMedia in a way that prevents additional camera and
  * microphone prompts, at the cost of ignoring all constraints other than
  * the first set passed in.
  *
  * The first call to navigator.getUserMedia (also now aliased to
--- a/browser/components/loop/standalone/content/js/standaloneAppStore.js
+++ b/browser/components/loop/standalone/content/js/standaloneAppStore.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.store = loop.store || {};
 
 /**
  * Manages the conversation window app controller view. Used to get
  * the window data and store the window type.
  */
 loop.store.StandaloneAppStore = (function() {
--- a/browser/components/loop/standalone/content/js/standaloneClient.js
+++ b/browser/components/loop/standalone/content/js/standaloneClient.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 var loop = loop || {};
 loop.StandaloneClient = (function($) {
   "use strict";
 
   // The expected properties to be returned from the POST /calls request.
   var expectedCallsProperties = [ "sessionId", "sessionToken", "apiKey" ];
 
   /**
--- a/browser/components/loop/standalone/content/js/standaloneMozLoop.js
+++ b/browser/components/loop/standalone/content/js/standaloneMozLoop.js
@@ -1,14 +1,12 @@
 /* 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/. */
 
-/* global loop:true */
-
 /**
  * The StandaloneMozLoop implementation reflects that of the mozLoop API for Loop
  * in the desktop code. Not all functions are implemented.
  */
 var loop = loop || {};
 loop.StandaloneMozLoop = (function(mozL10n) {
   "use strict";
 
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React */
-/* jshint newcap:false, maxlen:false */
-
 var loop = loop || {};
 loop.standaloneRoomViews = (function(mozL10n) {
   "use strict";
 
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React */
-/* jshint newcap:false, maxlen:false */
-
 var loop = loop || {};
 loop.standaloneRoomViews = (function(mozL10n) {
   "use strict";
 
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React, MozActivity */
-/* jshint newcap:false, maxlen:false */
-
 var loop = loop || {};
 loop.webapp = (function($, _, OT, mozL10n) {
   "use strict";
 
   loop.config = loop.config || {};
   loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
 
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -1,17 +1,12 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* global loop:true, React, MozActivity */
-/* jshint newcap:false, maxlen:false */
-
 var loop = loop || {};
 loop.webapp = (function($, _, OT, mozL10n) {
   "use strict";
 
   loop.config = loop.config || {};
   loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
 
   var sharedActions = loop.shared.actions;
--- a/browser/components/loop/standalone/package.json
+++ b/browser/components/loop/standalone/package.json
@@ -7,18 +7,18 @@
     "url": "git@github.com:mozilla/loop-client.git"
   },
   "engines": {
     "node": "0.10.x",
     "npm": "1.3.x"
   },
   "dependencies": {},
   "devDependencies": {
-    "eslint": "0.20.x",
-    "eslint-plugin-react": "2.2.x",
+    "eslint": "0.21.x",
+    "eslint-plugin-react": "2.3.x",
     "express": "4.x"
   },
   "scripts": {
     "test": "make test",
     "start": "make runserver"
   },
   "license": "MPL-2.0"
 }
--- a/browser/components/loop/test/desktop-local/client_test.js
+++ b/browser/components/loop/test/desktop-local/client_test.js
@@ -1,19 +1,16 @@
 /* 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/. */
 
-/*global loop, sinon, it, beforeEach, afterEach, describe */
-
-var expect = chai.expect;
-
 describe("loop.Client", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox,
       callback,
       client,
       mozLoop,
       fakeToken,
       hawkRequestStub;
 
   var fakeErrorRes = {
--- a/browser/components/loop/test/desktop-local/contacts_test.js
+++ b/browser/components/loop/test/desktop-local/contacts_test.js
@@ -1,21 +1,18 @@
 /* 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/. */
 
-/*jshint newcap:false*/
-/*global loop, sinon */
-
-var expect = chai.expect;
-var TestUtils = React.addons.TestUtils;
-
 describe("loop.contacts", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
+
   var fakeAddContactButtonText = "Fake Add Contact";
   var fakeEditContactButtonText = "Fake Edit Contact";
   var fakeDoneButtonText = "Fake Done";
   // The fake contacts array is copied each time mozLoop.contacts.getAll() is called.
   var fakeContacts = [{
     id: 1,
     _guid: 1,
     name: ["Ally Avocado"],
--- a/browser/components/loop/test/desktop-local/conversationAppStore_test.js
+++ b/browser/components/loop/test/desktop-local/conversationAppStore_test.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.store.ConversationAppStore", function () {
 
+  var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sandbox, dispatcher;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
   });
 
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.conversationViews", function () {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
+  var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sharedView = loop.shared.views;
   var sandbox, view, dispatcher, contact, fakeAudioXHR;
   var fakeMozLoop, fakeWindow;
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -1,19 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* global loop, sinon, React, TestUtils */
-
-var expect = chai.expect;
-
 describe("loop.conversation", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
   var sharedModels = loop.shared.models,
       fakeWindow,
       sandbox;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     navigator.mozLoop = {
--- a/browser/components/loop/test/desktop-local/index.html
+++ b/browser/components/loop/test/desktop-local/index.html
@@ -77,17 +77,17 @@
   <script src="roomStore_test.js"></script>
   <script>
     // Stop the default init functions running to avoid conflicts in tests
     document.removeEventListener('DOMContentLoaded', loop.panel.init);
     document.removeEventListener('DOMContentLoaded', loop.conversation.init);
 
     describe("Uncaught Error Check", function() {
       it("should load the tests without errors", function() {
-        expect(uncaughtError && uncaughtError.message).to.be.undefined;
+        chai.expect(uncaughtError && uncaughtError.message).to.be.undefined;
       });
     });
 
     mocha.run(function () {
       $("#mocha").append("<p id='complete'>Complete.</p>");
     });
   </script>
 </body>
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -1,23 +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/. */
 
-/*jshint newcap:false*/
-/*global loop, sinon */
-
-var expect = chai.expect;
-var TestUtils = React.addons.TestUtils;
-var sharedActions = loop.shared.actions;
-var sharedUtils = loop.shared.utils;
-
 describe("loop.panel", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
+  var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
+
   var sandbox, notifications;
   var fakeXHR, fakeWindow, fakeMozLoop;
   var requests = [];
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
@@ -920,30 +917,30 @@ describe("loop.panel", function() {
       };
 
       var view = TestUtils.renderIntoDocument(
         React.createElement(loop.panel.ToSView));
 
       TestUtils.findRenderedDOMComponentWithClass(view, "terms-service");
     });
 
-    it("should not render when the value of loop.seenToS is set to 'seen'",
-      function(done) {
+    it("should not render when the value of loop.seenToS is set to 'seen'", function() {
         navigator.mozLoop.getLoopPref = function(key) {
           return {
             "gettingStarted.seen": true,
             "seenToS": "seen"
           }[key];
         };
 
-        try {
+        var view = TestUtils.renderIntoDocument(
+          React.createElement(loop.panel.ToSView));
+
+        expect(function() {
           TestUtils.findRenderedDOMComponentWithClass(view, "terms-service");
-        } catch (err) {
-          done();
-        }
+        }).to.Throw(/not find/);
     });
 
     it("should render when the value of loop.gettingStarted.seen is false",
        function() {
          navigator.mozLoop.getLoopPref = function(key) {
            return {
              "gettingStarted.seen": false,
              "seenToS": "seen"
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -1,15 +1,18 @@
-var expect = chai.expect;
-
-/* jshint newcap:false */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 describe("loop.roomViews", function () {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
+  var sharedActions = loop.shared.actions;
+  var sharedUtils = loop.shared.utils;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow,
     fakeMozLoop, fakeContextURL;
   var favicon = "data:image/x-icon;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
 
   beforeEach(function() {
--- a/browser/components/loop/test/mochitest/.eslintrc
+++ b/browser/components/loop/test/mochitest/.eslintrc
@@ -1,17 +1,35 @@
 {
-  "ecmaFeatures": {
-    "arrowFunctions": true,
-    "blockBindings": true,
-    "destructuring": true,
-    "generators": true,
-    "restParams": true,
-    "spread": true,
-    "objectLiteralShorthandMethods": true,
-  },
-  "rules": {
-    "generator-star-spacing": [2, "after"],
-    // We should fix the errors and enable this (set to 2)
-    "no-var": 0,
-    "strict": [2, "global"]
+  "extends": "../../.eslintrc-gecko",
+  "globals": {
+    // General test items.
+    "add_task": false,
+    "Cc": true,
+    "Ci": true,
+    "Cr": true,
+    "Cu": true,
+    "is": false,
+    "info": false,
+    "ok": false,
+    "registerCleanupFunction": false,
+    // head.js items
+    "HAWK_TOKEN_LENGTH": true,
+    "checkLoggedOutState": false,
+    "checkFxAOAuthTokenData": false,
+    "loadLoopPanel": false,
+    "getLoopString": false,
+    "gMozLoopAPI": true,
+    "mockDb": true,
+    "mockPushHandler": true,
+    "promiseDeletedOAuthParams": false,
+    "promiseOAuthGetRegistration": false,
+    "promiseOAuthParamsSetup": false,
+    "promiseObserverNotified": false,
+    "promiseTabLoadEvent": false,
+    "promiseWaitForCondition": false,
+    "resetFxA": true,
+    // Loop specific items
+    "MozLoopServiceInternal": true,
+    "LoopRoomsInternal": true,
+    "LoopUI": false,
   }
 }
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -346,13 +346,13 @@ const mockDb = {
     callback(null, null);
   },
   removeAll: function(callback) {
     this._store = {};
     this._next_guid = 1;
     callback(null);
   },
   promise: function(method, ...params) {
-    return new Promise(resolve => {
+    return new Promise((resolve, reject) => {
       this[method](...params, (err, res) => err ? reject(err) : resolve(res));
     });
   }
 };
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -1,16 +1,16 @@
-/* global chai, loop */
-
-var expect = chai.expect;
-var sharedActions = loop.shared.actions;
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 describe("loop.store.ActiveRoomStore", function () {
   "use strict";
 
+  var expect = chai.expect;
+  var sharedActions = loop.shared.actions;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
   var fakeMultiplexGum;
 
--- a/browser/components/loop/test/shared/conversationStore_test.js
+++ b/browser/components/loop/test/shared/conversationStore_test.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.store.ConversationStore", function () {
   "use strict";
 
+  var expect = chai.expect;
   var CALL_STATES = loop.store.CALL_STATES;
   var WS_STATES = loop.store.WS_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sandbox, dispatcher, client, store, fakeSessionData, sdkDriver;
--- a/browser/components/loop/test/shared/crypto_test.js
+++ b/browser/components/loop/test/shared/crypto_test.js
@@ -1,19 +1,16 @@
 /* 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/. */
 
-/* global loop, sinon */
-
-var expect = chai.expect;
-
 describe("loop.crypto", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox, oldCrypto;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
   });
 
   afterEach(function() {
     sandbox.restore();
--- a/browser/components/loop/test/shared/dispatcher_test.js
+++ b/browser/components/loop/test/shared/dispatcher_test.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.Dispatcher", function () {
   "use strict";
 
+  var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var dispatcher, sandbox;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
   });
 
--- a/browser/components/loop/test/shared/feedbackApiClient_test.js
+++ b/browser/components/loop/test/shared/feedbackApiClient_test.js
@@ -1,19 +1,16 @@
 /* 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/. */
 
-/*global loop, sinon, it, beforeEach, afterEach, describe */
-
-var expect = chai.expect;
-
 describe("loop.FeedbackAPIClient", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox,
       fakeXHR,
       requests = [];
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
--- a/browser/components/loop/test/shared/feedbackStore_test.js
+++ b/browser/components/loop/test/shared/feedbackStore_test.js
@@ -1,16 +1,16 @@
-/* global chai, loop */
-
-var expect = chai.expect;
-var sharedActions = loop.shared.actions;
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 describe("loop.store.FeedbackStore", function () {
   "use strict";
 
+  var expect = chai.expect;
+  var sharedActions = loop.shared.actions;
   var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
   var sandbox, dispatcher, store, feedbackClient;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     dispatcher = new loop.Dispatcher();
 
--- a/browser/components/loop/test/shared/feedbackViews_test.js
+++ b/browser/components/loop/test/shared/feedbackViews_test.js
@@ -1,26 +1,22 @@
 /* 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/. */
 
-/*global loop, sinon, React */
-/* jshint newcap:false */
-
-var expect = chai.expect;
-var l10n = navigator.mozL10n || document.mozL10n;
-var TestUtils = React.addons.TestUtils;
-var sharedActions = loop.shared.actions;
-var sharedViews = loop.shared.views;
-
-var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
-
 describe("loop.shared.views.FeedbackView", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var l10n = navigator.mozL10n || document.mozL10n;
+  var TestUtils = React.addons.TestUtils;
+  var sharedActions = loop.shared.actions;
+  var sharedViews = loop.shared.views;
+
+  var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
   var sandbox, comp, dispatcher, fakeFeedbackClient, feedbackStore;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
     fakeFeedbackClient = {send: sandbox.stub()};
     feedbackStore = new loop.store.FeedbackStore(dispatcher, {
       feedbackClient: fakeFeedbackClient
--- a/browser/components/loop/test/shared/fxOSActiveRoomStore_test.js
+++ b/browser/components/loop/test/shared/fxOSActiveRoomStore_test.js
@@ -1,18 +1,17 @@
-/* global chai, loop */
-
-var expect = chai.expect;
-var sharedActions = loop.shared.actions;
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 describe("loop.store.FxOSActiveRoomStore", function () {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
-
+  var expect = chai.expect;
+  var sharedActions = loop.shared.actions;
   var sandbox;
   var dispatcher;
   var fakeMozLoop;
   var store;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
--- a/browser/components/loop/test/shared/index.html
+++ b/browser/components/loop/test/shared/index.html
@@ -74,17 +74,17 @@
   <script src="fxOSActiveRoomStore_test.js"></script>
   <script src="conversationStore_test.js"></script>
   <script src="feedbackStore_test.js"></script>
   <script src="otSdkDriver_test.js"></script>
   <script src="store_test.js"></script>
   <script>
     describe("Uncaught Error Check", function() {
       it("should load the tests without errors", function() {
-        expect(uncaughtError && uncaughtError.message).to.be.undefined;
+        chai.expect(uncaughtError && uncaughtError.message).to.be.undefined;
       });
     });
 
     mocha.run(function () {
       $("#mocha").append("<p id='complete'>Complete.</p>");
     });
   </script>
 </body>
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -1,22 +1,19 @@
 /* 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/. */
 
-/* global loop, sinon */
-/* jshint newcap:false */
-
-var expect = chai.expect;
-
 describe("loop.shared.mixins", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox;
   var sharedMixins = loop.shared.mixins;
+  var TestUtils = React.addons.TestUtils;
   var ROOM_STATES = loop.store.ROOM_STATES;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
   });
 
   afterEach(function() {
     sandbox.restore();
--- a/browser/components/loop/test/shared/models_test.js
+++ b/browser/components/loop/test/shared/models_test.js
@@ -1,19 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* global loop, sinon */
-
-var expect = chai.expect;
-
 describe("loop.shared.models", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var l10n = navigator.mozL10n || document.mozL10n;
   var sharedModels = loop.shared.models, sandbox, fakeXHR,
       requests = [], fakeSDK, fakeMozLoop, fakeSession, fakeSessionData;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -1,16 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.OTSdkDriver", function () {
   "use strict";
 
+  var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var STREAM_PROPERTIES = loop.shared.utils.STREAM_PROPERTIES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   var sandbox;
   var dispatcher, driver, mozLoop, publisher, sdk, session, sessionData;
   var fakeLocalElement, fakeRemoteElement, fakeScreenElement;
--- a/browser/components/loop/test/shared/store_test.js
+++ b/browser/components/loop/test/shared/store_test.js
@@ -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/. */
 
-var expect = chai.expect;
-
 describe("loop.store", function () {
   "use strict";
 
+  var expect = chai.expect;
   var dispatcher;
   var sandbox;
   var sharedActions = loop.shared.actions;
+  var TestUtils = React.addons.TestUtils;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
   });
 
   afterEach(function() {
     sandbox.restore();
--- a/browser/components/loop/test/shared/utils_test.js
+++ b/browser/components/loop/test/shared/utils_test.js
@@ -1,20 +1,16 @@
 /* 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/. */
 
-/* global loop, sinon */
-/* jshint newcap:false */
-
-var expect = chai.expect;
-
 describe("loop.shared.utils", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox;
   var sharedUtils = loop.shared.utils;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
   });
 
   afterEach(function() {
--- a/browser/components/loop/test/shared/validate_test.js
+++ b/browser/components/loop/test/shared/validate_test.js
@@ -1,18 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-/*global chai, validate */
-
-var expect = chai.expect;
-
 describe("Validator", function() {
   "use strict";
 
+  var expect = chai.expect;
+
   // test helpers
   function create(dependencies, values) {
     var validator = new loop.validate.Validator(dependencies);
     return validator.validate.bind(validator, values);
   }
 
   // test types
   function X(){}
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/components/loop/test/shared/views_test.js
@@ -1,22 +1,19 @@
 /* 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/. */
 
-/*global loop, sinon, React */
-/* jshint newcap:false */
-
-var expect = chai.expect;
-var l10n = navigator.mozL10n || document.mozL10n;
-var TestUtils = React.addons.TestUtils;
-
 describe("loop.shared.views", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var l10n = navigator.mozL10n || document.mozL10n;
+  var TestUtils = React.addons.TestUtils;
+  var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedViews = loop.shared.views;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass;
   var sandbox, fakeAudioXHR, dispatcher, OS, OSVersion;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
--- a/browser/components/loop/test/shared/websocket_test.js
+++ b/browser/components/loop/test/shared/websocket_test.js
@@ -1,20 +1,16 @@
 /* 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/. */
 
-/*global loop, sinon, it, beforeEach, afterEach, describe */
-
-var expect = chai.expect;
-
-
 describe("loop.CallConnectionWebSocket", function() {
   "use strict";
 
+  var expect = chai.expect;
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   var sandbox,
       dummySocket;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
--- a/browser/components/loop/test/standalone/index.html
+++ b/browser/components/loop/test/standalone/index.html
@@ -68,17 +68,17 @@
   <script src="standaloneMozLoop_test.js"></script>
   <script src="standaloneRoomViews_test.js"></script>
   <script src="standaloneMetricsStore_test.js"></script>
   <script src="webapp_test.js"></script>
   <script src="multiplexGum_test.js"></script>
   <script>
     describe("Uncaught Error Check", function() {
       it("should load the tests without errors", function() {
-        expect(uncaughtError && uncaughtError.message).to.be.undefined;
+        chai.expect(uncaughtError && uncaughtError.message).to.be.undefined;
       });
     });
 
     mocha.run(function () {
       $("#mocha").append("<p id='complete'>Complete.</p>");
     });
 </script>
 </body>
--- a/browser/components/loop/test/standalone/multiplexGum_test.js
+++ b/browser/components/loop/test/standalone/multiplexGum_test.js
@@ -1,24 +1,21 @@
 /* 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/. */
 
-/*global loop, sinon, it, beforeEach, afterEach, describe*/
-
-var expect = chai.expect;
-
 describe("loop.standaloneMedia._MultiplexGum", function() {
   "use strict";
 
+  var expect = chai.expect;
   var defaultGum =
     navigator.getUserMedia ||
     navigator.mozGetUserMedia ||
     navigator.webkitGetUserMedia ||
-    (window.TBPlugin && TBPlugin.getUserMedia);
+    (window.TBPlugin && window.TBPlugin.getUserMedia);
 
   var sandbox;
   var multiplexGum;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     multiplexGum = new loop.standaloneMedia._MultiplexGum();
     loop.standaloneMedia.setSingleton(multiplexGum);
--- a/browser/components/loop/test/standalone/standaloneAppStore_test.js
+++ b/browser/components/loop/test/standalone/standaloneAppStore_test.js
@@ -1,14 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-var expect = chai.expect;
-
 describe("loop.store.StandaloneAppStore", function () {
+  var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sandbox, dispatcher;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
   });
--- a/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
+++ b/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
@@ -1,17 +1,16 @@
 /* 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/. */
 
-var expect = chai.expect;
-
 describe("loop.store.StandaloneMetricsStore", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox, dispatcher, store, fakeActiveRoomStore;
 
   var sharedActions = loop.shared.actions;
   var METRICS_GA_CATEGORY = loop.store.METRICS_GA_CATEGORY;
   var METRICS_GA_ACTIONS = loop.store.METRICS_GA_ACTIONS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
 
--- a/browser/components/loop/test/standalone/standaloneMozLoop_test.js
+++ b/browser/components/loop/test/standalone/standaloneMozLoop_test.js
@@ -1,17 +1,16 @@
 /* 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/. */
 
-var expect = chai.expect;
-
 describe("loop.StandaloneMozLoop", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox, fakeXHR, requests, callback, mozLoop;
   var fakeToken, fakeBaseServerUrl, fakeServerErrorDescription;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
     // https://github.com/cjohansen/Sinon.JS/issues/393
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -1,19 +1,18 @@
 /* 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/. */
 
-/* global loop, sinon */
-
-var expect = chai.expect;
-
 describe("loop.standaloneRoomViews", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
+
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
 
   var sandbox, dispatcher, activeRoomStore, feedbackStore, dispatch;
 
--- a/browser/components/loop/test/standalone/standalone_client_test.js
+++ b/browser/components/loop/test/standalone/standalone_client_test.js
@@ -1,19 +1,16 @@
 /* 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/. */
 
-/*global loop, sinon, it, beforeEach, afterEach, describe, hawk */
-
-var expect = chai.expect;
-
 describe("loop.StandaloneClient", function() {
   "use strict";
 
+  var expect = chai.expect;
   var sandbox,
       fakeXHR,
       requests = [],
       callback,
       fakeToken;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -1,20 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* global loop, sinon */
-
-var expect = chai.expect;
-var TestUtils = React.addons.TestUtils;
-
 describe("loop.webapp", function() {
   "use strict";
 
+  var expect = chai.expect;
+  var TestUtils = React.addons.TestUtils;
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models,
       sharedViews = loop.shared.views,
       sharedUtils = loop.shared.utils,
       standaloneMedia = loop.standaloneMedia,
       sandbox,
       notifications,
       stubGetPermsAndCacheMedia,
--- a/browser/components/loop/test/xpcshell/.eslintrc
+++ b/browser/components/loop/test/xpcshell/.eslintrc
@@ -1,17 +1,36 @@
 {
-  "ecmaFeatures": {
-    "arrowFunctions": true,
-    "blockBindings": true,
-    "destructuring": true,
-    "generators": true,
-    "restParams": true,
-    "spread": true,
-    "objectLiteralShorthandMethods": true,
-  },
-  "rules": {
-    "generator-star-spacing": [2, "after"],
-    // We should fix the errors and enable this (set to 2)
-    "no-var": 0,
-    "strict": [2, "global"]
+  "extends": "../../.eslintrc-gecko",
+  "globals": {
+    // General xpcshell-test functions
+    "HttpServer": false,
+    "add_task": false,
+    "add_test": false,
+    "Ci": true,
+    "Cu": true,
+    "do_check_true": false,
+    "do_check_false": false,
+    "do_check_eq": false,
+    "do_get_profile": false,
+    "do_print": false,
+    "do_register_cleanup": false,
+    "do_throw": false,
+    "do_timeout": false,
+    "run_next_test": false,
+    // head.js items.
+    "MockWebSocketChannel": false,
+    "extend": true,
+    "getLoopString": false,
+    "kServerPushUrl": true,
+    "kEndPointUrl": true,
+    "loopCrypto": true,
+    "loopServer": true,
+    "mockPushHandler": true,
+    "setupFakeFxAUserProfile": false,
+    "setupFakeLoopServer": false,
+    "timerHandlers",
+    "waitForCondition": true,
+    // Loop specific items
+    "MozLoopServiceInternal": true,
+    "LoopRoomsInternal": true,
   }
 }
--- a/browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
@@ -121,17 +121,19 @@ add_task(function* setup_server() {
       res.write(JSON.stringify([...kRoomsResponses.values()]));
     }
 
     res.processAsync();
     res.finish();
   });
 
   function returnRoomDetails(res, roomName) {
-    roomDetail.roomName = roomName;
+    var roomDetail = {
+      roomName: roomName
+    };
     res.setStatusLine(null, 200, "OK");
     res.write(JSON.stringify(roomDetail));
     res.processAsync();
     res.finish();
   }
 
   function getJSONData(body) {
     return JSON.parse(CommonUtils.readBytesFromInputStream(body));
--- a/browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
@@ -48,24 +48,16 @@ add_task(function* setup_server() {
     res.setStatusLine(null, 200, "OK");
 
     res.write(JSON.stringify([...kRoomsResponses.values()]));
 
     res.processAsync();
     res.finish();
   });
 
-  function returnRoomDetails(res, roomName) {
-    roomDetail.roomName = roomName;
-    res.setStatusLine(null, 200, "OK");
-    res.write(JSON.stringify(roomDetail));
-    res.processAsync();
-    res.finish();
-  }
-
   function getJSONData(body) {
     return JSON.parse(CommonUtils.readBytesFromInputStream(body));
   }
 
   // Add a request handler for each room in the list.
   [...kRoomsResponses.values()].forEach(function(room) {
     loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
       let roomDetail = extend({}, room);
--- a/browser/components/loop/test/xpcshell/test_loopservice_encryptionkey.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_encryptionkey.js
@@ -1,11 +1,10 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
-/* global Services, Assert */
 
 "use strict";
 
 const kGuestKeyPref = "loop.key";
 const kFxAKeyPref = "loop.key.fxa";
 
 do_register_cleanup(function() {
   Services.prefs.clearUserPref(kGuestKeyPref);
--- a/browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
@@ -1,11 +1,10 @@
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
-/*global XPCOMUtils, Services, Assert */
 
 "use strict";
 
 var fakeCharPrefName = "color";
 var fakeBoolPrefName = "boolean";
 var fakePrefValue = "green";
 
 function test_getLoopPref()
@@ -60,17 +59,17 @@ function test_setLoopPref_new()
                "Should set a new char pref under the loop. branch");
   Services.prefs.clearUserPref("loop." + fakeCharPrefName);
 }
 
 function test_setLoopPref_non_coercible_type()
 {
   MozLoopService.setLoopPref(fakeCharPrefName, true);
 
-  ok(true, "Setting non-coercible type should not fail");
+  Assert.ok(true, "Setting non-coercible type should not fail");
 }
 
 
 function test_getLoopPref_bool()
 {
   Services.prefs.setBoolPref("loop." + fakeBoolPrefName, true);
 
   var returnedPref = MozLoopService.getLoopPref(fakeBoolPrefName);
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -1,16 +1,13 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
+/* global uncaughtError:true */
 
 (function() {
   "use strict";
 
   // Stop the default init functions running to avoid conflicts.
   document.removeEventListener('DOMContentLoaded', loop.panel.init);
   document.removeEventListener('DOMContentLoaded', loop.conversation.init);
 
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -1,16 +1,13 @@
-/** @jsx React.DOM */
-
 /* 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/. */
 
-/* jshint newcap:false */
-/* global loop:true, React */
+/* global uncaughtError:true */
 
 (function() {
   "use strict";
 
   // Stop the default init functions running to avoid conflicts.
   document.removeEventListener('DOMContentLoaded', loop.panel.init);
   document.removeEventListener('DOMContentLoaded', loop.conversation.init);
 
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -153,16 +153,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "SessionFile",
   "resource:///modules/sessionstore/SessionFile.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabAttributes",
   "resource:///modules/sessionstore/TabAttributes.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabState",
   "resource:///modules/sessionstore/TabState.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
   "resource:///modules/sessionstore/TabStateCache.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TabStateFlusher",
+  "resource:///modules/sessionstore/TabStateFlusher.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
   "resource:///modules/sessionstore/Utils.jsm");
 
 /**
  * |true| if we are in debug mode, |false| otherwise.
  * Debug mode is controlled by preference browser.sessionstore.debug
  */
 let gDebuggingEnabled = false;
@@ -648,25 +650,38 @@ let SessionStoreInternal = {
         }
 
         // Record telemetry measurements done in the child and update the tab's
         // cached state. Mark the window as dirty and trigger a delayed write.
         this.recordTelemetry(aMessage.data.telemetry);
         TabState.update(browser, aMessage.data);
         this.saveStateDelayed(win);
 
+        if (aMessage.data.isFinal) {
+          // If this the final message we need to resolve all pending flush
+          // requests for the given browser as they might have been sent too
+          // late and will never respond. If they have been sent shortly after
+          // switching a browser's remoteness there isn't too much data to skip.
+          TabStateFlusher.resolveAll(browser);
+        } else if (aMessage.data.flushID) {
+          // This is an update kicked off by an async flush request. Notify the
+          // TabStateFlusher so that it can finish the request and notify its
+          // consumer that's waiting for the flush to be done.
+          TabStateFlusher.resolve(browser, aMessage.data.flushID);
+        }
+
         // Handle any updates sent by the child after the tab was closed. This
         // might be the final update as sent by the "unload" handler but also
         // any async update message that was sent before the child unloaded.
         if (this._closedTabs.has(browser.permanentKey)) {
           let {closedTabs, tabData} = this._closedTabs.get(browser.permanentKey);
 
           // Update the closed tab's state. This will be reflected in its
           // window's list of closed tabs as that refers to the same object.
-          TabState.copyFromCache({linkedBrowser: browser}, tabData.state);
+          TabState.copyFromCache(browser, tabData.state);
 
           // Is this the tab's final message?
           if (aMessage.data.isFinal) {
             // We expect no further updates.
             this._closedTabs.delete(browser.permanentKey);
             // The tab state no longer needs this reference.
             delete tabData.permanentKey;
 
@@ -686,17 +701,17 @@ let SessionStoreInternal = {
               // longer consider its data interesting enough to keep around.
               this.removeClosedTabData(closedTabs, index);
             }
           }
         }
         break;
       case "SessionStore:restoreHistoryComplete":
         // Notify the tabbrowser that the tab chrome has been restored.
-        let tabData = browser.__SS_data;
+        let tabData = TabState.collect(tab);
 
         // wall-paper fix for bug 439675: make sure that the URL to be loaded
         // is always visible in the address bar
         let activePageData = tabData.entries[tabData.index - 1] || null;
         let uri = activePageData ? activePageData.url || null : null;
         browser.userTypedValue = uri;
 
         // If the page has a title, set it.
@@ -708,48 +723,52 @@ let SessionStoreInternal = {
             tab.label = activePageData.url;
             tab.crop = "center";
           }
         }
 
         // Restore the tab icon.
         if ("image" in tabData) {
           win.gBrowser.setIcon(tab, tabData.image);
+          TabStateCache.update(browser, {image: null});
         }
 
         let event = win.document.createEvent("Events");
         event.initEvent("SSTabRestoring", true, false);
         tab.dispatchEvent(event);
         break;
       case "SessionStore:restoreTabContentStarted":
         if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
           // If a load not initiated by sessionstore was started in a
           // previously pending tab. Mark the tab as no longer pending.
           this.markTabAsRestoring(tab);
         } else {
           // If the user was typing into the URL bar when we crashed, but hadn't hit
           // enter yet, then we just need to write that value to the URL bar without
           // loading anything. This must happen after the load, since it will clear
           // userTypedValue.
-          let tabData = browser.__SS_data;
+          let tabData = TabState.collect(tab);
           if (tabData.userTypedValue && !tabData.userTypedClear) {
             browser.userTypedValue = tabData.userTypedValue;
             win.URLBarSetURI();
           }
+
+          // Remove state we don't need any longer.
+          TabStateCache.update(browser, {
+            userTypedValue: null, userTypedClear: null
+          });
         }
         break;
       case "SessionStore:restoreTabContentComplete":
         // This callback is used exclusively by tests that want to
         // monitor the progress of network loads.
         if (gDebuggingEnabled) {
           Services.obs.notifyObservers(browser, NOTIFY_TAB_RESTORED, null);
         }
 
-        delete browser.__SS_data;
-
         SessionStoreInternal._resetLocalTabRestoringState(tab);
         SessionStoreInternal.restoreNextTab();
 
         this._sendTabRestoredNotification(tab);
         break;
       case "SessionStore:reloadPendingTab":
         if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE) {
           this.restoreTabContent(tab);
@@ -1405,17 +1424,16 @@ let SessionStoreInternal = {
    *        Window reference
    * @param aTab
    *        Tab reference
    * @param aNoNotification
    *        bool Do not save state if we're updating an existing tab
    */
   onTabRemove: function ssi_onTabRemove(aWindow, aTab, aNoNotification) {
     let browser = aTab.linkedBrowser;
-    delete browser.__SS_data;
     browser.removeEventListener("SwapDocShells", this);
     browser.removeEventListener("oop-browser-crashed", this);
 
     // If this tab was in the middle of restoring or still needs to be restored,
     // we need to reset that state. If the tab was restoring, we will attempt to
     // restore the next tab.
     let previousState = browser.__SS_restoreState;
     if (previousState) {
@@ -1614,16 +1632,20 @@ let SessionStoreInternal = {
     // on a user preference we'll either restore all of them at once, or only
     // restore the selected tab and lazily restore the rest. We'll make no
     // efforts at this time to be smart and restore all of the tabs that had
     // been in a restored state at the time of the crash.
     if (aBrowser.__SS_restoreState) {
       let tab = aWindow.gBrowser.getTabForBrowser(aBrowser);
       this._resetLocalTabRestoringState(tab);
     }
+
+    // The browser crashed so we might never receive flush responses.
+    // Resolve all pending flush requests for the crashed browser.
+    TabStateFlusher.resolveAll(aBrowser);
   },
 
   // Clean up data that has been closed a long time ago.
   // Do not reschedule a save. This will wait for the next regular
   // save.
   onIdleDaily: function() {
     // Remove old closed windows
     this._cleanupOldData([this._closedWindows]);
@@ -1778,34 +1800,53 @@ let SessionStoreInternal = {
   duplicateTab: function ssi_duplicateTab(aWindow, aTab, aDelta = 0) {
     if (!aTab.ownerDocument.defaultView.__SSi) {
       throw Components.Exception("Default view is not tracked", Cr.NS_ERROR_INVALID_ARG);
     }
     if (!aWindow.gBrowser) {
       throw Components.Exception("Invalid window object: no gBrowser", Cr.NS_ERROR_INVALID_ARG);
     }
 
-    // Flush all data queued in the content script because we will need that
-    // state to properly duplicate the given tab.
-    TabState.flush(aTab.linkedBrowser);
-
-    // Duplicate the tab state
-    let tabState = TabState.clone(aTab);
-
-    tabState.index += aDelta;
-    tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
-    tabState.pinned = false;
-
+    // Create a new tab.
     let newTab = aTab == aWindow.gBrowser.selectedTab ?
       aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
       aWindow.gBrowser.addTab();
 
-    this.restoreTab(newTab, tabState, {
-      restoreImmediately: true /* Load this tab right away. */
+    // Set tab title to "Connecting..." and start the throbber to pretend we're
+    // doing something while actually waiting for data from the frame script.
+    aWindow.gBrowser.setTabTitleLoading(newTab);
+    newTab.setAttribute("busy", "true");
+
+    // Collect state before flushing.
+    let tabState = TabState.clone(aTab);
+
+    // Flush to get the latest tab state to duplicate.
+    let browser = aTab.linkedBrowser;
+    TabStateFlusher.flush(browser).then(() => {
+      // The new tab might have been closed in the meantime.
+      if (newTab.closing || !newTab.linkedBrowser) {
+        return;
+      }
+
+      // Update state with flushed data. We can't use TabState.clone() here as
+      // the tab to duplicate may have already been closed. In that case we
+      // only have access to the <xul:browser>.
+      let options = {includePrivateData: true};
+      TabState.copyFromCache(browser, tabState, options);
+
+      tabState.index += aDelta;
+      tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
+      tabState.pinned = false;
+
+      // Restore the state into the new tab.
+      this.restoreTab(newTab, tabState, {
+        restoreImmediately: true /* Load this tab right away. */
+      });
     });
+
     return newTab;
   },
 
   getClosedTabCount: function ssi_getClosedTabCount(aWindow) {
     if ("__SSi" in aWindow) {
       return this._windows[aWindow.__SSi]._closedTabs.length;
     }
 
@@ -1943,64 +1984,37 @@ let SessionStoreInternal = {
   deleteWindowValue: function ssi_deleteWindowValue(aWindow, aKey) {
     if (aWindow.__SSi && this._windows[aWindow.__SSi].extData &&
         this._windows[aWindow.__SSi].extData[aKey])
       delete this._windows[aWindow.__SSi].extData[aKey];
     this.saveStateDelayed(aWindow);
   },
 
   getTabValue: function ssi_getTabValue(aTab, aKey) {
-    let data = {};
-    if (aTab.__SS_extdata) {
-      data = aTab.__SS_extdata;
-    }
-    else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
-      // If the tab hasn't been fully restored, get the data from the to-be-restored data
-      data = aTab.linkedBrowser.__SS_data.extData;
-    }
-    return data[aKey] || "";
+    return (aTab.__SS_extdata || {})[aKey] || "";
   },
 
   setTabValue: function ssi_setTabValue(aTab, aKey, aStringValue) {
     if (typeof aStringValue != "string") {
       throw new TypeError("setTabValue only accepts string values");
     }
 
     // If the tab hasn't been restored, then set the data there, otherwise we
     // could lose newly added data.
-    let saveTo;
-    if (aTab.__SS_extdata) {
-      saveTo = aTab.__SS_extdata;
-    }
-    else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
-      saveTo = aTab.linkedBrowser.__SS_data.extData;
+    if (!aTab.__SS_extdata) {
+      aTab.__SS_extdata = {};
     }
-    else {
-      aTab.__SS_extdata = {};
-      saveTo = aTab.__SS_extdata;
-    }
-
-    saveTo[aKey] = aStringValue;
+
+    aTab.__SS_extdata[aKey] = aStringValue;
     this.saveStateDelayed(aTab.ownerDocument.defaultView);
   },
 
   deleteTabValue: function ssi_deleteTabValue(aTab, aKey) {
-    // We want to make sure that if data is accessed early, we attempt to delete
-    // that data from __SS_data as well. Otherwise we'll throw in cases where
-    // data can be set or read.
-    let deleteFrom;
-    if (aTab.__SS_extdata) {
-      deleteFrom = aTab.__SS_extdata;
-    }
-    else if (aTab.linkedBrowser.__SS_data && aTab.linkedBrowser.__SS_data.extData) {
-      deleteFrom = aTab.linkedBrowser.__SS_data.extData;
-    }
-
-    if (deleteFrom && aKey in deleteFrom) {
-      delete deleteFrom[aKey];
+    if (aTab.__SS_extdata && aKey in aTab.__SS_extdata) {
+      delete aTab.__SS_extdata[aKey];
       this.saveStateDelayed(aTab.ownerDocument.defaultView);
     }
   },
 
   getGlobalValue: function ssi_getGlobalValue(aKey) {
     return this._globalState.get(aKey);
   },
 
@@ -2814,29 +2828,35 @@ let SessionStoreInternal = {
 
     // Start a new epoch to discard all frame script messages relating to a
     // previous epoch. All async messages that are still on their way to chrome
     // will be ignored and don't override any tab data set when restoring.
     let epoch = this.startNextEpoch(browser);
 
     // keep the data around to prevent dataloss in case
     // a tab gets closed before it's been properly restored
-    browser.__SS_data = tabData;
     browser.__SS_restoreState = TAB_STATE_NEEDS_RESTORE;
     browser.setAttribute("pending", "true");
     tab.setAttribute("pending", "true");
 
     // Update the persistent tab state cache with |tabData| information.
     TabStateCache.update(browser, {
       history: {entries: tabData.entries, index: tabData.index},
       scroll: tabData.scroll || null,
       storage: tabData.storage || null,
       formdata: tabData.formdata || null,
       disallow: tabData.disallow || null,
-      pageStyle: tabData.pageStyle || null
+      pageStyle: tabData.pageStyle || null,
+
+      // This information is only needed until the tab has finished restoring.
+      // When that's done it will be removed from the cache and we always
+      // collect it in TabState._collectBaseTabData().
+      image: tabData.image || "",
+      userTypedValue: tabData.userTypedValue || "",
+      userTypedClear: tabData.userTypedClear || 0
     });
 
     browser.messageManager.sendAsyncMessage("SessionStore:restoreHistory",
                                             {tabData: tabData, epoch: epoch, loadArguments});
 
     // Restore tab attributes.
     if ("attributes" in tabData) {
       TabAttributes.set(tab, tabData.attributes);
--- a/browser/components/sessionstore/TabState.jsm
+++ b/browser/components/sessionstore/TabState.jsm
@@ -50,18 +50,18 @@ this.TabState = Object.freeze({
   collect: function (tab) {
     return TabStateInternal.collect(tab);
   },
 
   clone: function (tab) {
     return TabStateInternal.clone(tab);
   },
 
-  copyFromCache: function (tab, tabData, options) {
-    TabStateInternal.copyFromCache(tab, tabData, options);
+  copyFromCache(browser, tabData, options) {
+    TabStateInternal.copyFromCache(browser, tabData, options);
   }
 });
 
 let TabStateInternal = {
   // A map (xul:browser -> handler) that maps a tab to the
   // synchronous collection handler object for that tab.
   // See SyncHandler in content-sessionStore.js.
   _syncHandlers: new WeakMap(),
@@ -160,106 +160,86 @@ let TabStateInternal = {
    *        {includePrivateData: true} to always include private data
    *
    * @returns {object} An object with the basic data for this tab.
    */
   _collectBaseTabData: function (tab, options) {
     let tabData = {entries: [], lastAccessed: tab.lastAccessed };
     let browser = tab.linkedBrowser;
 
-    if (!browser || !browser.currentURI) {
-      // can happen when calling this function right after .addTab()
-      return tabData;
-    }
-    if (browser.__SS_data) {
-      // Use the data to be restored when the tab hasn't been
-      // completely loaded. We clone the data, since we're updating it
-      // here and the caller may update it further.
-      tabData = Utils.shallowCopy(browser.__SS_data);
-      if (tab.pinned)
-        tabData.pinned = true;
-      else
-        delete tabData.pinned;
-      tabData.hidden = tab.hidden;
-
-      // If __SS_extdata is set then we'll use that since it might be newer.
-      if (tab.__SS_extdata)
-        tabData.extData = tab.__SS_extdata;
-      // If it exists but is empty then a key was likely deleted. In that case just
-      // delete extData.
-      if (tabData.extData && !Object.keys(tabData.extData).length)
-        delete tabData.extData;
-      return tabData;
-    }
-
-    // If there is a userTypedValue set, then either the user has typed something
-    // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
-    // is used to indicate whether the tab was in some sort of loading state with
-    // userTypedValue.
-    if (browser.userTypedValue) {
-      tabData.userTypedValue = browser.userTypedValue;
-      tabData.userTypedClear = browser.userTypedClear;
-    } else {
-      delete tabData.userTypedValue;
-      delete tabData.userTypedClear;
-    }
-
     if (tab.pinned)
       tabData.pinned = true;
     else
       delete tabData.pinned;
     tabData.hidden = tab.hidden;
 
     // Save tab attributes.
     tabData.attributes = TabAttributes.get(tab);
 
-    // Store the tab icon.
-    let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
-    tabData.image = tabbrowser.getIcon(tab);
-
     if (tab.__SS_extdata)
       tabData.extData = tab.__SS_extdata;
     else if (tabData.extData)
       delete tabData.extData;
 
     // Copy data from the tab state cache only if the tab has fully finished
     // restoring. We don't want to overwrite data contained in __SS_data.
-    this.copyFromCache(tab, tabData, options);
+    this.copyFromCache(browser, tabData, options);
+
+    // After copyFromCache() was called we check for properties that are kept
+    // in the cache only while the tab is pending or restoring. Once that
+    // happened those properties will be removed from the cache and will
+    // be read from the tab/browser every time we collect data.
+
+    // Store the tab icon.
+    if (!("image" in tabData)) {
+      let tabbrowser = tab.ownerDocument.defaultView.gBrowser;
+      tabData.image = tabbrowser.getIcon(tab);
+    }
+
+    // If there is a userTypedValue set, then either the user has typed something
+    // in the URL bar, or a new tab was opened with a URI to load. userTypedClear
+    // is used to indicate whether the tab was in some sort of loading state with
+    // userTypedValue.
+    if (!("userTypedValue" in tabData) && browser.userTypedValue) {
+      tabData.userTypedValue = browser.userTypedValue;
+      tabData.userTypedClear = browser.userTypedClear;
+    }
 
     return tabData;
   },
 
   /**
-   * Copy tab data for the given |tab| from the cache to |tabData|.
+   * Copy data for the given |browser| from the cache to |tabData|.
    *
-   * @param tab (xul:tab)
-   *        The tab belonging to the given |tabData| object.
+   * @param browser (xul:browser)
+   *        The browser belonging to the given |tabData| object.
    * @param tabData (object)
    *        The tab data belonging to the given |tab|.
    * @param options (object)
    *        {includePrivateData: true} to always include private data
    */
-  copyFromCache: function (tab, tabData, options = {}) {
-    let data = TabStateCache.get(tab.linkedBrowser);
+  copyFromCache(browser, tabData, options = {}) {
+    let data = TabStateCache.get(browser);
     if (!data) {
       return;
     }
 
     // The caller may explicitly request to omit privacy checks.
     let includePrivateData = options && options.includePrivateData;
+    let isPinned = tabData.pinned || false;
 
     for (let key of Object.keys(data)) {
       let value = data[key];
 
       // Filter sensitive data according to the current privacy level.
       if (!includePrivateData) {
         if (key === "storage") {
-          value = PrivacyFilter.filterSessionStorageData(value, tab.pinned);
+          value = PrivacyFilter.filterSessionStorageData(value, isPinned);
         } else if (key === "formdata") {
-          value = PrivacyFilter.filterFormData(value, tab.pinned);
+          value = PrivacyFilter.filterFormData(value, isPinned);
         }
       }
 
       if (key === "history") {
         tabData.entries = value.entries;
 
         if (value.hasOwnProperty("index")) {
           tabData.index = value.index;
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/TabStateFlusher.jsm
@@ -0,0 +1,122 @@
+/* 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/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = ["TabStateFlusher"];
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Promise.jsm", this);
+
+/**
+ * A module that enables async flushes. Updates from frame scripts are
+ * throttled to be sent only once per second. If an action wants a tab's latest
+ * state without waiting for a second then it can request an async flush and
+ * wait until the frame scripts reported back. At this point the parent has the
+ * latest data and the action can continue.
+ */
+this.TabStateFlusher = Object.freeze({
+  /**
+   * Requests an async flush for the given browser. Returns a promise that will
+   * resolve when we heard back from the content process and the parent has
+   * all the latest data.
+   */
+  flush(browser) {
+    return TabStateFlusherInternal.flush(browser);
+  },
+
+  /**
+   * Resolves the flush request with the given flush ID.
+   */
+  resolve(browser, flushID) {
+    TabStateFlusherInternal.resolve(browser, flushID);
+  },
+
+  /**
+   * Resolves all active flush requests for a given browser. This should be
+   * used when the content process crashed or the final update message was
+   * seen. In those cases we can't guarantee to ever hear back from the frame
+   * script so we just resolve all requests instead of discarding them.
+   */
+  resolveAll(browser) {
+    TabStateFlusherInternal.resolveAll(browser);
+  }
+});
+
+let TabStateFlusherInternal = {
+  // Stores the last request ID.
+  _lastRequestID: 0,
+
+  // A map storing all active requests per browser.
+  _requests: new WeakMap(),
+
+  /**
+   * Requests an async flush for the given browser. Returns a promise that will
+   * resolve when we heard back from the content process and the parent has
+   * all the latest data.
+   */
+  flush(browser) {
+    let id = ++this._lastRequestID;
+    let mm = browser.messageManager;
+    mm.sendAsyncMessage("SessionStore:flush", {id});
+
+    // Retrieve active requests for given browser.
+    let permanentKey = browser.permanentKey;
+    let perBrowserRequests = this._requests.get(permanentKey) || new Map();
+
+    return new Promise(resolve => {
+      // Store resolve() so that we can resolve the promise later.
+      perBrowserRequests.set(id, resolve);
+
+      // Update the flush requests stored per browser.
+      this._requests.set(permanentKey, perBrowserRequests);
+    });
+  },
+
+  /**
+   * Resolves the flush request with the given flush ID.
+   */
+  resolve(browser, flushID) {
+    // Nothing to do if there are no pending flushes for the given browser.
+    if (!this._requests.has(browser.permanentKey)) {
+      return;
+    }
+
+    // Retrieve active requests for given browser.
+    let perBrowserRequests = this._requests.get(browser.permanentKey);
+    if (!perBrowserRequests.has(flushID)) {
+      return;
+    }
+
+    // Resolve the request with the given id.
+    let resolve = perBrowserRequests.get(flushID);
+    perBrowserRequests.delete(flushID);
+    resolve();
+  },
+
+  /**
+   * Resolves all active flush requests for a given browser. This should be
+   * used when the content process crashed or the final update message was
+   * seen. In those cases we can't guarantee to ever hear back from the frame
+   * script so we just resolve all requests instead of discarding them.
+   */
+  resolveAll(browser) {
+    // Nothing to do if there are no pending flushes for the given browser.
+    if (!this._requests.has(browser.permanentKey)) {
+      return;
+    }
+
+    // Retrieve active requests for given browser.
+    let perBrowserRequests = this._requests.get(browser.permanentKey);
+
+    // Resolve all requests.
+    for (let resolve of perBrowserRequests.values()) {
+      resolve();
+    }
+
+    // Clear active requests.
+    perBrowserRequests.clear();
+  }
+};
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -101,16 +101,17 @@ let EventListener = {
  * Listens for and handles messages sent by the session store service.
  */
 let MessageListener = {
 
   MESSAGES: [
     "SessionStore:restoreHistory",
     "SessionStore:restoreTabContent",
     "SessionStore:resetRestore",
+    "SessionStore:flush",
   ],
 
   init: function () {
     this.MESSAGES.forEach(m => addMessageListener(m, this));
   },
 
   receiveMessage: function ({name, data}) {
     // The docShell might be gone. Don't process messages,
@@ -132,16 +133,19 @@ let MessageListener = {
         this.restoreHistory(data);
         break;
       case "SessionStore:restoreTabContent":
         this.restoreTabContent(data);
         break;
       case "SessionStore:resetRestore":
         gContentRestore.resetRestore();
         break;
+      case "SessionStore:flush":
+        this.flush(data);
+        break;
       default:
         debug("received unknown message '" + name + "'");
         break;
     }
   },
 
   restoreHistory({epoch, tabData, loadArguments}) {
     gContentRestore.restoreHistory(tabData, loadArguments, {
@@ -187,16 +191,21 @@ let MessageListener = {
     });
 
     sendAsyncMessage("SessionStore:restoreTabContentStarted", {epoch});
 
     if (!didStartLoad) {
       // Pretend that the load succeeded so that event handlers fire correctly.
       sendAsyncMessage("SessionStore:restoreTabContentComplete", {epoch});
     }
+  },
+
+  flush({id}) {
+    // Flush the message queue, send the latest updates.
+    MessageQueue.send({flushID: id});
   }
 };
 
 /**
  * On initialization, this handler gets sent to the parent process as a CPOW.
  * The parent will use it only to flush pending data from the frame script
  * when needed, i.e. when closing a tab, closing a window, shutting down, etc.
  *
@@ -646,32 +655,35 @@ let MessageQueue = {
   },
 
   /**
    * Sends queued data to the chrome process.
    *
    * @param options (object)
    *        {id: 123} to override the update ID used to accumulate data to send.
    *        {sync: true} to send data to the parent process synchronously.
+   *        {flushID: 123} to specify that this is a flush
+   *        {isFinal: true} to signal this is the final message sent on unload
    */
   send: function (options = {}) {
     // Looks like we have been called off a timeout after the tab has been
     // closed. The docShell is gone now and we can just return here as there
     // is nothing to do.
     if (!docShell) {
       return;
     }
 
     if (this._timeout) {
       clearTimeout(this._timeout);
       this._timeout = null;
     }
 
     let sync = options && options.sync;
     let startID = (options && options.id) || this._id;
+    let flushID = (options && options.flushID) || 0;
 
     // We use sendRpcMessage in the sync case because we may have been called
     // through a CPOW. RPC messages are the only synchronous messages that the
     // child is allowed to send to the parent while it is handling a CPOW
     // request.
     let sendMessage = sync ? sendRpcMessage : sendAsyncMessage;
 
     let durationMs = Date.now();
@@ -697,17 +709,17 @@ let MessageQueue = {
 
     durationMs = Date.now() - durationMs;
     let telemetry = {
       FX_SESSION_RESTORE_CONTENT_COLLECT_DATA_LONGEST_OP_MS: durationMs
     }
 
     // Send all data to the parent process.
     sendMessage("SessionStore:update", {
-      id: this._id, data, telemetry,
+      id: this._id, data, telemetry, flushID,
       isFinal: options.isFinal || false,
       epoch: gCurrentEpoch
     });
 
     // Increase our unique message ID.
     this._id++;
   },
 
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -39,13 +39,14 @@ EXTRA_JS_MODULES.sessionstore = [
     'SessionSaver.jsm',
     'SessionStorage.jsm',
     'SessionStore.jsm',
     'SessionWorker.js',
     'SessionWorker.jsm',
     'TabAttributes.jsm',
     'TabState.jsm',
     'TabStateCache.jsm',
+    'TabStateFlusher.jsm',
     'Utils.jsm',
 ]
 
 with Files('**'):
     BUG_COMPONENT = ('Firefox', 'Session Restore')
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -60,16 +60,19 @@ support-files =
 
 
 #disabled-for-intermittent-failures--bug-766044, browser_459906_empty.html
 #disabled-for-intermittent-failures--bug-766044, browser_459906_sample.html
 #disabled-for-intermittent-failures--bug-765389, browser_461743_sample.html
 
 [browser_aboutPrivateBrowsing.js]
 [browser_aboutSessionRestore.js]
+[browser_async_duplicate_tab.js]
+[browser_async_flushes.js]
+run-if = e10s && crashreporter
 [browser_async_remove_tab.js]
 run-if = e10s
 [browser_attributes.js]
 [browser_backup_recovery.js]
 [browser_broadcast.js]
 [browser_capabilities.js]
 [browser_cleaner.js]
 [browser_cookies.js]
--- a/browser/components/sessionstore/test/browser_454908.js
+++ b/browser/components/sessionstore/test/browser_454908.js
@@ -1,17 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-let tmp = {};
-Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", tmp);
-let {SessionSaver} = tmp;
-
 const URL = ROOT + "browser_454908_sample.html";
 const PASS = "pwd-" + Math.random();
 
 /**
  * Bug 454908 - Don't save/restore values of password fields.
  */
 add_task(function* test_dont_save_passwords() {
   // Make sure we do save form data.
--- a/browser/components/sessionstore/test/browser_465215.js
+++ b/browser/components/sessionstore/test/browser_465215.js
@@ -1,36 +1,28 @@
-/* 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/. */
+"use strict";
 
-function test() {
-  /** Test for Bug 465215 **/
-
-  waitForExplicitFinish();
+let uniqueName = "bug 465215";
+let uniqueValue1 = "as good as unique: " + Date.now();
+let uniqueValue2 = "as good as unique: " + Math.random();
 
-  let uniqueName = "bug 465215";
-  let uniqueValue1 = "as good as unique: " + Date.now();
-  let uniqueValue2 = "as good as unique: " + Math.random();
-
+add_task(function* () {
   // set a unique value on a new, blank tab
-  let tab1 = gBrowser.addTab();
-  promiseBrowserLoaded(tab1.linkedBrowser).then(() => {
-    ss.setTabValue(tab1, uniqueName, uniqueValue1);
+  let tab1 = gBrowser.addTab("about:blank");
+  yield promiseBrowserLoaded(tab1.linkedBrowser);
+  ss.setTabValue(tab1, uniqueName, uniqueValue1);
 
-    // duplicate the tab with that value
-    let tab2 = ss.duplicateTab(window, tab1);
-    is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
+  // duplicate the tab with that value
+  let tab2 = ss.duplicateTab(window, tab1);
+  yield promiseTabRestored(tab2);
+  is(ss.getTabValue(tab2, uniqueName), uniqueValue1, "tab value was duplicated");
 
-    ss.setTabValue(tab2, uniqueName, uniqueValue2);
-    isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
+  ss.setTabValue(tab2, uniqueName, uniqueValue2);
+  isnot(ss.getTabValue(tab1, uniqueName), uniqueValue2, "tab values aren't sync'd");
 
-    // overwrite the tab with the value which should remove it
-    promiseTabState(tab1, {entries: []}).then(() => {
-      is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
+  // overwrite the tab with the value which should remove it
+  yield promiseTabState(tab1, {entries: []});
+  is(ss.getTabValue(tab1, uniqueName), "", "tab value was cleared");
 
-      // clean up
-      gBrowser.removeTab(tab2);
-      gBrowser.removeTab(tab1);
-      finish();
-    });
-  });
-}
+  // clean up
+  gBrowser.removeTab(tab2);
+  gBrowser.removeTab(tab1);
+});
--- a/browser/components/sessionstore/test/browser_586068-browser_state_interrupted.js
+++ b/browser/components/sessionstore/test/browser_586068-browser_state_interrupted.js
@@ -1,14 +1,16 @@
 /* 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/. */
 
 const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
 
+requestLongerTimeout(2);
+
 add_task(function* test() {
   Services.prefs.setBoolPref(PREF_RESTORE_ON_DEMAND, false);
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(PREF_RESTORE_ON_DEMAND);
   });
 
   // The first state will be loaded using setBrowserState, followed by the 2nd
   // state also being loaded using setBrowserState, interrupting the first restore.
--- a/browser/components/sessionstore/test/browser_623779.js
+++ b/browser/components/sessionstore/test/browser_623779.js
@@ -1,7 +1,13 @@
-function test() {
+"use strict";
+
+add_task(function* () {
   gBrowser.pinTab(gBrowser.selectedTab);
-  var newTab = gBrowser.duplicateTab(gBrowser.selectedTab);
+
+  let newTab = gBrowser.duplicateTab(gBrowser.selectedTab);
+  yield promiseTabRestored(newTab);
+
   ok(!newTab.pinned, "duplicating a pinned tab creates unpinned tab");
-  gBrowser.removeTab(newTab);
+  yield promiseRemoveTab(newTab);
+
   gBrowser.unpinTab(gBrowser.selectedTab);
-}
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_async_duplicate_tab.js
@@ -0,0 +1,78 @@
+"use strict";
+
+const URL = "data:text/html;charset=utf-8,<a href=%23>clickme</a>";
+
+add_task(function* test_duplicate() {
+  // Create new tab.
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // Click the link to navigate, this will add second shistory entry.
+  yield ContentTask.spawn(browser, null, function* () {
+    return new Promise(resolve => {
+      addEventListener("hashchange", function onHashChange() {
+        removeEventListener("hashchange", onHashChange);
+        resolve();
+      });
+
+      // Click the link.
+      content.document.querySelector("a").click();
+    });
+  });
+
+  // Duplicate the tab.
+  let tab2 = ss.duplicateTab(window, tab);
+
+  // Wait until the tab has fully restored.
+  yield promiseTabRestored(tab2);
+  yield TabStateFlusher.flush(tab2.linkedBrowser);
+
+  // There should be two history entries now.
+  let {entries} = JSON.parse(ss.getTabState(tab2));
+  is(entries.length, 2, "there are two shistory entries");
+
+  // Cleanup.
+  yield promiseRemoveTab(tab2);
+  yield promiseRemoveTab(tab);
+});
+
+add_task(function* test_duplicate_remove() {
+  // Create new tab.
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // Click the link to navigate, this will add second shistory entry.
+  yield ContentTask.spawn(browser, null, function* () {
+    return new Promise(resolve => {
+      addEventListener("hashchange", function onHashChange() {
+        removeEventListener("hashchange", onHashChange);
+        resolve();
+      });
+
+      // Click the link.
+      content.document.querySelector("a").click();
+    });
+  });
+
+  // Duplicate the tab.
+  let tab2 = ss.duplicateTab(window, tab);
+
+  // Before the duplication finished, remove the tab.
+  yield Promise.all([promiseRemoveTab(tab), promiseTabRestored(tab2)]);
+  yield TabStateFlusher.flush(tab2.linkedBrowser);
+
+  // There should be two history entries now.
+  let {entries} = JSON.parse(ss.getTabState(tab2));
+  is(entries.length, 2, "there are two shistory entries");
+
+  // Cleanup.
+  yield promiseRemoveTab(tab2);
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_async_flushes.js
@@ -0,0 +1,112 @@
+"use strict";
+
+const URL = "data:text/html;charset=utf-8,<a href=%23>clickme</a>";
+
+add_task(function* test_flush() {
+  // Create new tab.
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // There should be one history entry.
+  let {entries} = JSON.parse(ss.getTabState(tab));
+  is(entries.length, 1, "there is a single history entry");
+
+  // Click the link to navigate, this will add second shistory entry.
+  yield ContentTask.spawn(browser, null, function* () {
+    return new Promise(resolve => {
+      addEventListener("hashchange", function onHashChange() {
+        removeEventListener("hashchange", onHashChange);
+        resolve();
+      });
+
+      // Click the link.
+      content.document.querySelector("a").click();
+    });
+  });
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // There should be two history entries now.
+  ({entries} = JSON.parse(ss.getTabState(tab)));
+  is(entries.length, 2, "there are two shistory entries");
+
+  // Cleanup.
+  gBrowser.removeTab(tab);
+});
+
+add_task(function* test_crash() {
+  // Create new tab.
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // There should be one history entry.
+  let {entries} = JSON.parse(ss.getTabState(tab));
+  is(entries.length, 1, "there is a single history entry");
+
+  // Click the link to navigate.
+  yield ContentTask.spawn(browser, null, function* () {
+    return new Promise(resolve => {
+      addEventListener("hashchange", function onHashChange() {
+        removeEventListener("hashchange", onHashChange);
+        resolve();
+      });
+
+      // Click the link.
+      content.document.querySelector("a").click();
+    });
+  });
+
+  // Crash the browser and flush. Both messages are async and will be sent to
+  // the content process. The "crash" message makes it first so that we don't
+  // get a chance to process the flush. The TabStateFlusher however should be
+  // notified so that the flush still completes.
+  let promise1 = crashBrowser(browser);
+  let promise2 = TabStateFlusher.flush(browser);
+  yield Promise.all([promise1, promise2]);
+
+  // The pending update should be lost.
+  ({entries} = JSON.parse(ss.getTabState(tab)));
+  is(entries.length, 1, "still only one history entry");
+
+  // Cleanup.
+  gBrowser.removeTab(tab);
+});
+
+add_task(function* test_remove() {
+  // Create new tab.
+  let tab = gBrowser.addTab(URL);
+  let browser = tab.linkedBrowser;
+  yield promiseBrowserLoaded(browser);
+
+  // Flush to empty any queued update messages.
+  yield TabStateFlusher.flush(browser);
+
+  // There should be one history entry.
+  let {entries} = JSON.parse(ss.getTabState(tab));
+  is(entries.length, 1, "there is a single history entry");
+
+  // Click the link to navigate.
+  yield ContentTask.spawn(browser, null, function* () {
+    return new Promise(resolve => {
+      addEventListener("hashchange", function onHashChange() {
+        removeEventListener("hashchange", onHashChange);
+        resolve();
+      });
+
+      // Click the link.
+      content.document.querySelector("a").click();
+    });
+  });
+
+  // Request a flush and remove the tab. The flush should still complete.
+  yield Promise.all([TabStateFlusher.flush(browser), promiseRemoveTab(tab)]);
+})
--- a/browser/components/sessionstore/test/browser_broadcast.js
+++ b/browser/components/sessionstore/test/browser_broadcast.js
@@ -49,21 +49,22 @@ add_task(function flush_on_quit_requeste
  * duplicating a tab.
  */
 add_task(function flush_on_duplicate() {
   let tab = yield createTabWithStorageData(["http://example.com"]);
   let browser = tab.linkedBrowser;
 
   yield modifySessionStorage(browser, {test: "on-duplicate"});
   let tab2 = ss.duplicateTab(window, tab);
+  yield promiseTabRestored(tab2);
+
   let {storage} = JSON.parse(ss.getTabState(tab2));
   is(storage["http://example.com"].test, "on-duplicate",
     "sessionStorage data has been flushed when duplicating tabs");
 
-  yield promiseTabRestored(tab2);
   yield promiseRemoveTab(tab2);
   [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
   is(storage["http://example.com"].test, "on-duplicate",
     "sessionStorage data has been flushed when duplicating tabs");
 
   gBrowser.removeTab(tab);
 });
 
--- a/browser/components/sessionstore/test/browser_crashedTabs.js
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -13,135 +13,29 @@ registerCleanupFunction(() => {
 });
 
 // Allow tabs to restore on demand so we can test pending states
 Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
 
 // Running this test in ASAN is slow.
 requestLongerTimeout(2);
 
-/**
- * Returns a Promise that resolves once a remote <xul:browser> has experienced
- * a crash. Also does the job of cleaning up the minidump of the crash.
- *
- * @param browser
- *        The <xul:browser> that will crash
- * @return Promise
- */
-function crashBrowser(browser) {
-  // This frame script is injected into the remote browser, and used to
-  // intentionally crash the tab. We crash by using js-ctypes and dereferencing
-  // a bad pointer. The crash should happen immediately upon loading this
-  // frame script.
-  let frame_script = () => {
-    const Cu = Components.utils;
-    Cu.import("resource://gre/modules/ctypes.jsm");
-
-    let dies = function() {
-      privateNoteIntentionalCrash();
-      let zero = new ctypes.intptr_t(8);
-      let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
-      badptr.contents
-    };
-
-    dump("Et tu, Brute?");
-    dies();
-  }
-
-  let crashCleanupPromise = new Promise((resolve, reject) => {
-    let observer = (subject, topic, data) => {
-      is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
-      ok(subject instanceof Ci.nsIPropertyBag2,
-         'Subject implements nsIPropertyBag2.');
-      // we might see this called as the process terminates due to previous tests.
-      // We are only looking for "abnormal" exits...
-      if (!subject.hasKey("abnormal")) {
-        info("This is a normal termination and isn't the one we are looking for...");
-        return;
-      }
-
-      let dumpID;
-      if ('nsICrashReporter' in Ci) {
-        dumpID = subject.getPropertyAsAString('dumpID');
-        ok(dumpID, "dumpID is present and not an empty string");
-      }
-
-      if (dumpID) {
-        let minidumpDirectory = getMinidumpDirectory();
-        removeFile(minidumpDirectory, dumpID + '.dmp');
-        removeFile(minidumpDirectory, dumpID + '.extra');
-      }
-
-      Services.obs.removeObserver(observer, 'ipc:content-shutdown');
-      info("Crash cleaned up");
-      resolve();
-    };
-
-    Services.obs.addObserver(observer, 'ipc:content-shutdown');
-  });
-
-  let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
-    browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
-      browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
-      info("about:tabcrashed loaded");
-      resolve();
-    }, false, true);
-  });
-
-  // This frame script will crash the remote browser as soon as it is
-  // evaluated.
-  let mm = browser.messageManager;
-  mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
-  return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]).then(() => {
-    let tab = gBrowser.getTabForBrowser(browser);
-    is(tab.getAttribute("crashed"), "true", "Tab should be marked as crashed");
-  });
-}
-
 function clickButton(browser, id) {
   info("Clicking " + id);
 
   let frame_script = (id) => {
     let button = content.document.getElementById(id);
     button.click();
   };
 
   let mm = browser.messageManager;
   mm.loadFrameScript("data:,(" + frame_script.toString() + ")('" + id + "');", false);
 }
 
 /**
- * Returns the directory where crash dumps are stored.
- *
- * @return nsIFile
- */
-function getMinidumpDirectory() {
-  let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
-  dir.append("minidumps");
-  return dir;
-}
-
-/**
- * Removes a file from a directory. This is a no-op if the file does not
- * exist.
- *
- * @param directory
- *        The nsIFile representing the directory to remove from.
- * @param filename
- *        A string for the file to remove from the directory.
- */
-function removeFile(directory, filename) {
-  let file = directory.clone();
-  file.append(filename);
-  if (file.exists()) {
-    file.remove(false);
-  }
-}
-
-/**
  * Checks the documentURI of the root document of a remote browser
  * to see if it equals URI. Returns a Promise that resolves if
  * there is a match, and rejects with an error message if they
  * do not match.
  *
  * @param browser
  *        The remote <xul:browser> to check the root document URI in.
  * @param URI
--- a/browser/components/sessionstore/test/browser_formdata_xpath.js
+++ b/browser/components/sessionstore/test/browser_formdata_xpath.js
@@ -57,18 +57,18 @@ add_task(function test_form_data_restora
   // Check that all form values have been duplicated.
   for (let xpath of Object.keys(FIELDS)) {
     let expected = JSON.stringify(FIELDS[xpath]);
     let actual = JSON.stringify(yield getFormValue(browser2, xpath));
     is(actual, expected, "The value for \"" + xpath + "\" was correctly restored");
   }
 
   // Remove all tabs.
-  gBrowser.removeTab(tab2);
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab2);
+  yield promiseRemoveTab(tab);
 
   // Restore one of the tabs again.
   tab = ss.undoCloseTab(window, 0);
   browser = tab.linkedBrowser;
   yield promiseTabRestored(tab);
 
   // Check that none of the form values have been restored due to the privacy
   // level settings.
@@ -76,17 +76,17 @@ add_task(function test_form_data_restora
     let expected = FIELDS[xpath];
     if (expected) {
       let actual = yield getFormValue(browser, xpath, expected);
       isnot(actual, expected, "The value for \"" + xpath + "\" was correctly discarded");
     }
   }
 
   // Cleanup.
-  gBrowser.removeTab(tab);
+  yield promiseRemoveTab(tab);
 });
 
 function createFilePath(leaf) {
   let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
   file.append(leaf);
   return file.path;
 }
 
--- a/browser/components/sessionstore/test/browser_swapDocShells.js
+++ b/browser/components/sessionstore/test/browser_swapDocShells.js
@@ -1,30 +1,26 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
-function test() {
-  waitForExplicitFinish();
-
-  Task.spawn(function task() {
-    let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
-    yield promiseBrowserLoaded(gBrowser.selectedBrowser);
+add_task(function* () {
+  let tab = gBrowser.selectedTab = gBrowser.addTab("about:mozilla");
+  yield promiseBrowserLoaded(gBrowser.selectedBrowser);
 
-    let win = gBrowser.replaceTabWithWindow(tab);
-    yield promiseDelayedStartupFinished(win);
-    yield promiseBrowserHasURL(win.gBrowser.browsers[0], "about:mozilla");
+  let win = gBrowser.replaceTabWithWindow(tab);
+  yield promiseDelayedStartupFinished(win);
+  yield promiseBrowserHasURL(win.gBrowser.browsers[0], "about:mozilla");
 
-    win.duplicateTabIn(win.gBrowser.selectedTab, "tab");
-    let browser = win.gBrowser.browsers[1];
-    yield promiseBrowserLoaded(browser);
-    is(browser.currentURI.spec, "about:mozilla", "tab was duplicated");
+  win.duplicateTabIn(win.gBrowser.selectedTab, "tab");
+  yield promiseTabRestored(win.gBrowser.tabs[1]);
 
-    win.close();
-  }).then(finish);
-}
+  let browser = win.gBrowser.browsers[1];
+  is(browser.currentURI.spec, "about:mozilla", "tab was duplicated");
+
+  yield promiseWindowClosed(win);
+});
 
 function promiseDelayedStartupFinished(win) {
   let deferred = Promise.defer();
   whenDelayedStartupFinished(win, deferred.resolve);
   return deferred.promise;
 }
 
 function promiseBrowserHasURL(browser, url) {
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -20,26 +20,24 @@ for (let script of FRAME_SCRIPTS) {
 }
 
 registerCleanupFunction(() => {
   for (let script of FRAME_SCRIPTS) {
     mm.removeDelayedFrameScript(script, true);
   }
 });
 
-let tmp = {};
-Cu.import("resource://gre/modules/Promise.jsm", tmp);
-Cu.import("resource://gre/modules/Task.jsm", tmp);
-Cu.import("resource:///modules/sessionstore/SessionStore.jsm", tmp);
-Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", tmp);
-Cu.import("resource:///modules/sessionstore/SessionFile.jsm", tmp);
-Cu.import("resource:///modules/sessionstore/TabState.jsm", tmp);
-let {Promise, Task, SessionStore, SessionSaver, SessionFile, TabState} = tmp;
+const {Promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
+const {SessionStore} = Cu.import("resource:///modules/sessionstore/SessionStore.jsm", {});
+const {SessionSaver} = Cu.import("resource:///modules/sessionstore/SessionSaver.jsm", {});
+const {SessionFile} = Cu.import("resource:///modules/sessionstore/SessionFile.jsm", {});
+const {TabState} = Cu.import("resource:///modules/sessionstore/TabState.jsm", {});
+const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 
-let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 // Some tests here assume that all restored tabs are loaded without waiting for
 // the user to bring them to the foreground. We ensure this by resetting the
 // related preference (see the "firefox.js" defaults file for details).
 Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", false);
 registerCleanupFunction(function () {
   Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
 });
@@ -528,8 +526,114 @@ for (let name of FORM_HELPERS) {
   this[name] = (browser, data) => sendMessage(browser, msg, data);
 }
 
 // Removes the given tab immediately and returns a promise that resolves when
 // all pending status updates (messages) of the closing tab have been received.
 function promiseRemoveTab(tab) {
   return BrowserTestUtils.removeTab(tab);
 }
+
+/**
+ * Returns a Promise that resolves once a remote <xul:browser> has experienced
+ * a crash. Also does the job of cleaning up the minidump of the crash.
+ *
+ * @param browser
+ *        The <xul:browser> that will crash
+ * @return Promise
+ */
+function crashBrowser(browser) {
+  /**
+   * Returns the directory where crash dumps are stored.
+   *
+   * @return nsIFile
+   */
+  function getMinidumpDirectory() {
+    let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
+    dir.append("minidumps");
+    return dir;
+  }
+
+  /**
+   * Removes a file from a directory. This is a no-op if the file does not
+   * exist.
+   *
+   * @param directory
+   *        The nsIFile representing the directory to remove from.
+   * @param filename
+   *        A string for the file to remove from the directory.
+   */
+  function removeFile(directory, filename) {
+    let file = directory.clone();
+    file.append(filename);
+    if (file.exists()) {
+      file.remove(false);
+    }
+  }
+
+  // This frame script is injected into the remote browser, and used to
+  // intentionally crash the tab. We crash by using js-ctypes and dereferencing
+  // a bad pointer. The crash should happen immediately upon loading this
+  // frame script.
+  let frame_script = () => {
+    const Cu = Components.utils;
+    Cu.import("resource://gre/modules/ctypes.jsm");
+
+    let dies = function() {
+      privateNoteIntentionalCrash();
+      let zero = new ctypes.intptr_t(8);
+      let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+      badptr.contents
+    };
+
+    dump("Et tu, Brute?");
+    dies();
+  }
+
+  let crashCleanupPromise = new Promise((resolve, reject) => {
+    let observer = (subject, topic, data) => {
+      is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+      ok(subject instanceof Ci.nsIPropertyBag2,
+         'Subject implements nsIPropertyBag2.');
+      // we might see this called as the process terminates due to previous tests.
+      // We are only looking for "abnormal" exits...
+      if (!subject.hasKey("abnormal")) {
+        info("This is a normal termination and isn't the one we are looking for...");
+        return;
+      }
+
+      let dumpID;
+      if ('nsICrashReporter' in Ci) {
+        dumpID = subject.getPropertyAsAString('dumpID');
+        ok(dumpID, "dumpID is present and not an empty string");
+      }
+
+      if (dumpID) {
+        let minidumpDirectory = getMinidumpDirectory();
+        removeFile(minidumpDirectory, dumpID + '.dmp');
+        removeFile(minidumpDirectory, dumpID + '.extra');
+      }
+
+      Services.obs.removeObserver(observer, 'ipc:content-shutdown');
+      info("Crash cleaned up");
+      resolve();
+    };
+
+    Services.obs.addObserver(observer, 'ipc:content-shutdown');
+  });
+
+  let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
+    browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
+      browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
+      info("about:tabcrashed loaded");
+      resolve();
+    }, false, true);
+  });
+
+  // This frame script will crash the remote browser as soon as it is
+  // evaluated.
+  let mm = browser.messageManager;
+  mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
+  return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]).then(() => {
+    let tab = gBrowser.getTabForBrowser(browser);
+    is(tab.getAttribute("crashed"), "true", "Tab should be marked as crashed");
+  });
+}
--- a/browser/components/tabview/test/browser_tabview_bug706430.js
+++ b/browser/components/tabview/test/browser_tabview_bug706430.js
@@ -1,36 +1,38 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 let state1 = {
   windows: [{
     tabs: [{
-      entries: [{ url: "about:blank#1" }],
+      entries: [{ url: "about:robots#1" }],
       hidden: true,
-      extData: {"tabview-tab": '{"url":"about:blank#1","groupID":1,"bounds":{"left":120,"top":20,"width":20,"height":20}}'}
+      extData: {"tabview-tab": '{"url":"about:robots#1","groupID":1,"bounds":{"left":120,"top":20,"width":20,"height":20}}'}
     },{
-      entries: [{ url: "about:blank#2" }],
+      entries: [{ url: "about:robots#2" }],
       hidden: false,
-      extData: {"tabview-tab": '{"url":"about:blank#2","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
+      extData: {"tabview-tab": '{"url":"about:robots#2","groupID":2,"bounds":{"left":20,"top":20,"width":20,"height":20}}'},
     }],
     selected: 2,
     extData: {
       "tabview-groups": '{"nextID":3,"activeGroupId":2, "totalNumber":2}',
       "tabview-group":
         '{"1":{"bounds":{"left":15,"top":5,"width":280,"height":232},"id":1},' +
         '"2":{"bounds":{"left":309,"top":5,"width":267,"height":226},"id":2}}'
     }
   }]
 };
 
 let state2 = {
   windows: [{
-    tabs: [{entries: [{ url: "about:blank#1" }], hidden: true},
-           {entries: [{ url: "about:blank#2" }], hidden: false}],
+    tabs: [{entries: [{ url: "about:robots#1" }], hidden: true},
+           {entries: [{ url: "about:robots#2" }], hidden: false}],
     selected: 2
   }]
 };
 
 let ss = Cc["@mozilla.org/browser/sessionstore;1"]
          .getService(Ci.nsISessionStore);
 
 function test() {
@@ -39,18 +41,18 @@ function test() {
   newWindowWithState(state1, function (win) {
     registerCleanupFunction(function () win.close());
 
     showTabView(function () {
       let cw = win.TabView.getContentWindow();
       let [group1, group2] = cw.GroupItems.groupItems;
       let [tab1, tab2] = win.gBrowser.tabs;
 
-      checkUrl(group1.getChild(0), "about:blank#1", "tab1 is in first group");
-      checkUrl(group2.getChild(0), "about:blank#2", "tab2 is in second group");
+      checkUrl(group1.getChild(0), "about:robots#1", "tab1 is in first group");
+      checkUrl(group2.getChild(0), "about:robots#2", "tab2 is in second group");
 
       whenWindowStateReady(win, function () {
         let groups = cw.GroupItems.groupItems;
         is(groups.length, 1, "one groupItem");
         is(groups[0].getChildren().length, 2, "single groupItem has two children");
 
         finish();
       });
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -13,16 +13,17 @@ support-files =
   doc_theme.css
   doc_viewsource.html
   browser_toolbox_options_enable_serviceworkers_testing.html
   serviceworker.js
 
 [browser_devtools_api.js]
 [browser_devtools_api_destroy.js]
 [browser_dynamic_tool_enabling.js]
+[browser_ignore_toolbox_network_requests.js]
 [browser_keybindings.js]
 [browser_new_activation_workflow.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
 [browser_two_tabs.js]
 [browser_toolbox_dynamic_registration.js]
 [browser_toolbox_getpanelwhenready.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/framework/test/browser_ignore_toolbox_network_requests.js
@@ -0,0 +1,30 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that network requests originating from the toolbox don't get recorded in
+// the network panel.
+
+add_task(function*() {
+  // TODO: This test tries to verify the normal behavior of the netmonitor and
+  // therefore needs to avoid the explicit check for tests. Bug 1167188 will
+  // allow us to remove this workaround.
+  let isTesting = gDevTools.testing;
+  gDevTools.testing = false;
+
+  let tab = yield addTab(URL_ROOT + "doc_viewsource.html");
+  let target = TargetFactory.forTab(tab);
+  let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
+  let panel = toolbox.getPanel("styleeditor");
+
+  is(panel.UI.editors.length, 1, "correct number of editors opened");
+
+  let monitor = yield toolbox.selectTool("netmonitor");
+  let { RequestsMenu } = monitor.panelWin.NetMonitorView;
+  is(RequestsMenu.itemCount, 0, "No network requests appear in the network panel");
+
+  yield gDevTools.closeToolbox(target);
+  tab = target = toolbox = panel = null;
+  gBrowser.removeCurrentTab();
+  gDevTools.testing = isTesting;
+});
--- a/browser/devtools/markupview/markup-view.css
+++ b/browser/devtools/markupview/markup-view.css
@@ -181,8 +181,14 @@ ul.children + .tag-line::before {
 .markupview-events {
   display: none;
   cursor: pointer;
 }
 
 .editor.text {
   display: inline-block;
 }
+
+.editor.text pre,
+.editor.comment pre {
+  font: inherit;
+}
+
--- a/browser/devtools/markupview/markup-view.xhtml
+++ b/browser/devtools/markupview/markup-view.xhtml
@@ -79,19 +79,19 @@
    --><span class="editable" save="${inner}" tabindex="0"><!--
      --><span save="${name}" class="attr-name theme-fg-color2"></span><!--
      -->=&quot;<!--
      --><span save="${val}" class="attr-value theme-fg-color6"></span><!--
      -->&quot;<!--
    --></span><!--
  --></span>
 
-    <span id="template-text" save="${elt}" class="editor text">
-      <pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre>
-    </span>
+    <span id="template-text" save="${elt}" class="editor text"><!--
+   --><pre save="${value}" style="display:inline-block; white-space: normal;" tabindex="0"></pre><!--
+ --></span>
 
     <span id="template-comment"
           save="${elt}"
           class="editor comment theme-comment"><!--
    --><span>&lt;!--</span><!--
    --><pre save="${value}" style="display:inline-block;" tabindex="0"></pre><!--
    --><span>--&gt;</span><!--
  --></span>
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -1,23 +1,23 @@
 [DEFAULT]
 tags = devtools
-skip-if = e10s # Handle in Bug 1077464 for profiler
 subsuite = devtools
 support-files =
   doc_innerHTML.html
   doc_simple-test.html
   head.js
 
 # Commented out tests are profiler tests
 # that need to be moved over to performance tool
 
-[browser_perf-aaa-run-first-leaktest.js]
+[browser_aaa-run-first-leaktest.js]
 [browser_marker-utils.js]
 [browser_markers-gc.js]
+skip-if = e10s # GC events seem unreliable in multiprocess
 [browser_markers-parse-html.js]
 [browser_markers-styles.js]
 [browser_markers-timestamp.js]
 [browser_perf-allocations-to-samples.js]
 [browser_perf-categories-js-calltree.js]
 [browser_perf-compatibility-01.js]
 [browser_perf-compatibility-02.js]
 [browser_perf-compatibility-03.js]
rename from browser/devtools/performance/test/browser_perf-aaa-run-first-leaktest.js
rename to browser/devtools/performance/test/browser_aaa-run-first-leaktest.js
--- a/browser/devtools/performance/test/browser_perf-front-profiler-02.js
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-02.js
@@ -5,33 +5,37 @@
  * Tests if the profiler connection front does not activate the built-in
  * profiler module if not necessary, and doesn't deactivate it when
  * a recording is stopped.
  */
 
 let test = Task.async(function*() {
   let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
   let front = panel.panelWin.gFront;
+  loadFrameScripts();
 
-  ok(!nsIProfilerModule.IsActive(),
+  ok(!(yield PMM_isProfilerActive()),
     "The built-in profiler module should not have been automatically started.");
 
   let activated = front.once("profiler-activated");
   let rec = yield front.startRecording();
   yield activated;
   yield front.stopRecording(rec);
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should still be active (1).");
 
   let alreadyActive = front.once("profiler-already-active");
   rec = yield front.startRecording();
   yield alreadyActive;
   yield front.stopRecording(rec);
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should still be active (2).");
 
-  yield teardown(panel);
-
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should have been automatically stoped.");
+  // Manually tear down so we can check profiler status
+  let tab = panel.target.tab;
+  yield panel._toolbox.destroy();
+  ok(!(yield PMM_isProfilerActive()),
+    "The built-in profiler module should no longer be active.");
+  yield removeTab(tab);
+  tab = null;
 
   finish();
 });
--- a/browser/devtools/performance/test/browser_perf-front-profiler-03.js
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-03.js
@@ -11,23 +11,30 @@ let test = Task.async(function*() {
   let firstFront = firstPanel.panelWin.gFront;
 
   let activated = firstFront.once("profiler-activated");
   yield firstFront.startRecording();
   yield activated;
 
   let { panel: secondPanel } = yield initPerformance(SIMPLE_URL);
   let secondFront = secondPanel.panelWin.gFront;
+  loadFrameScripts();
 
   let alreadyActive = secondFront.once("profiler-already-active");
   yield secondFront.startRecording();
   yield alreadyActive;
 
-  yield teardown(firstPanel);
-  ok(nsIProfilerModule.IsActive(),
+  // Manually teardown the tabs so we can check profiler status
+  let tab1 = firstPanel.target.tab;
+  let tab2 = secondPanel.target.tab;
+  yield firstPanel._toolbox.destroy();
+  yield removeTab(tab1);
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should still be active.");
 
-  yield teardown(secondPanel);
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should have been automatically stoped.");
+  yield secondPanel._toolbox.destroy();
+  ok(!(yield PMM_isProfilerActive()),
+    "The built-in profiler module should no longer be active.");
+  yield removeTab(tab2);
+  tab1 = tab2 = null;
 
   finish();
 });
--- a/browser/devtools/performance/test/browser_perf-front-profiler-04.js
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-04.js
@@ -5,20 +5,21 @@
  * Tests if the built-in profiler module is not reactivated if no other
  * consumer was using it over the remote debugger protocol, and ensures
  * that the actor will work properly even in such cases (e.g. the Gecko Profiler
  * addon was installed and automatically activated the profiler module).
  */
 
 let test = Task.async(function*() {
   // Ensure the profiler is already running when the test starts.
+  loadFrameScripts();
   let ENTRIES = 1000000;
   let INTERVAL = 1;
   let FEATURES = ["js"];
-  nsIProfilerModule.StartProfiler(ENTRIES, INTERVAL, FEATURES, FEATURES.length);
+  yield sendProfilerCommand("StartProfiler", [ENTRIES, INTERVAL, FEATURES, FEATURES.length]);
 
   let { panel: firstPanel } = yield initPerformance(SIMPLE_URL);
   let firstFront = firstPanel.panelWin.gFront;
 
   let firstAlreadyActive = firstFront.once("profiler-already-active");
   let recording = yield firstFront.startRecording();
   yield firstAlreadyActive;
   ok(recording._profilerStartTime > 0, "The profiler was not restarted.");
@@ -27,17 +28,17 @@ let test = Task.async(function*() {
   let secondFront = secondPanel.panelWin.gFront;
 
   let secondAlreadyActive = secondFront.once("profiler-already-active");
   let secondRecording = yield secondFront.startRecording();
   yield secondAlreadyActive;
   ok(secondRecording._profilerStartTime > 0, "The profiler was not restarted.");
 
   yield teardown(firstPanel);
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should still be active.");
 
   yield teardown(secondPanel);
-  ok(!nsIProfilerModule.IsActive(),
+  ok(!(yield PMM_isProfilerActive()),
     "The built-in profiler module should have been automatically stoped.");
 
   finish();
 });
--- a/browser/devtools/performance/test/browser_perf-recording-notices-04.js
+++ b/browser/devtools/performance/test/browser_perf-recording-notices-04.js
@@ -1,23 +1,24 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that when a recording overlaps the circular buffer, that
  * a class is assigned to the recording notices.
  */
 function spawnTest () {
-  // Make sure the profiler module is stopped so we can set a new buffer limit
-  nsIProfilerModule.StopProfiler();
-  Services.prefs.setIntPref(PROFILER_BUFFER_SIZE_PREF, 1000);
-
   let { panel } = yield initPerformance(SIMPLE_URL, void 0, { TEST_MOCK_PROFILER_CHECK_TIMER: 10 });
   let { EVENTS, $, PerformanceController, PerformanceView } = panel.panelWin;
 
+  // Make sure the profiler module is stopped so we can set a new buffer limit
+  loadFrameScripts();
+  yield PMM_stopProfiler();
+  Services.prefs.setIntPref(PROFILER_BUFFER_SIZE_PREF, 1000);
+
   yield startRecording(panel);
 
   let percent = 0;
   while (percent < 100) {
     [,percent] = yield onceSpread(PerformanceView, EVENTS.UI_BUFFER_UPDATED);
   }
 
   let bufferUsage = PerformanceController.getCurrentRecording().getBufferUsage();
--- a/browser/devtools/performance/test/browser_perf-shared-connection-03.js
+++ b/browser/devtools/performance/test/browser_perf-shared-connection-03.js
@@ -4,26 +4,28 @@
 /**
  * Tests if the shared PerformanceActorsConnection can properly send requests.
  */
 
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let front = panel.panelWin.gFront;
 
-  ok(!nsIProfilerModule.IsActive(),
+  loadFrameScripts();
+
+  ok(!(yield PMM_isProfilerActive()),
     "The built-in profiler module should not have been automatically started.");
 
   let result = yield front._request("profiler", "startProfiler");
   is(result.started, true,
     "The request finished successfully and the profiler should've been started.");
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should now be active.");
 
   result = yield front._request("profiler", "stopProfiler");
   is(result.started, false,
     "The request finished successfully and the profiler should've been stopped.");
-  ok(!nsIProfilerModule.IsActive(),
+  ok(!(yield PMM_isProfilerActive()),
     "The built-in profiler module should now be inactive.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/performance/test/browser_perf-ui-recording.js
+++ b/browser/devtools/performance/test/browser_perf-ui-recording.js
@@ -6,26 +6,27 @@
  * in the UI.
  */
 let WAIT_TIME = 10;
 
 function spawnTest () {
   let { panel } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, PerformanceController } = panel.panelWin;
   let front = panel.panelWin.gFront;
+  loadFrameScripts();
 
-  ok(!nsIProfilerModule.IsActive(),
+  ok(!(yield PMM_isProfilerActive()),
     "The built-in profiler module should not have been automatically started.");
 
   yield startRecording(panel);
   busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
 
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should now be active.");
 
   yield stopRecording(panel);
 
-  ok(nsIProfilerModule.IsActive(),
+  ok((yield PMM_isProfilerActive()),
     "The built-in profiler module should still be active.");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -8,20 +8,20 @@ let { Services } = Cu.import("resource:/
 let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { merge } = devtools.require("sdk/util/object");
+let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 let { getPerformanceActorsConnection, PerformanceFront } = devtools.require("devtools/performance/front");
+let TargetFactory = devtools.TargetFactory;
 
-let nsIProfilerModule = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
-let TargetFactory = devtools.TargetFactory;
 let mm = null;
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js"
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/performance/test/";
 const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
 
 const MEMORY_SAMPLE_PROB_PREF = "devtools.performance.memory.sample-probability";
 const MEMORY_MAX_LOG_LEN_PREF = "devtools.performance.memory.max-log-length";
@@ -66,37 +66,32 @@ let DEFAULT_PREFS = [
 // Enable the new performance panel for all tests.
 Services.prefs.setBoolPref("devtools.performance.enabled", true);
 // Enable logging for all the tests. Both the debugger server and frontend will
 // be affected by this pref.
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 /**
  * Call manually in tests that use frame script utils after initializing
- * the tool. Must be called after initializing so we can detect
- * whether or not `content` is a CPOW or not. Call after init but before navigating
- * to different pages.
+ * the tool. Must be called after initializing (once we have a tab).
  */
 function loadFrameScripts () {
   mm = gBrowser.selectedBrowser.messageManager;
   mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
 }
 
 registerCleanupFunction(() => {
   gDevTools.testing = false;
   info("finish() was called, cleaning up...");
 
   // Rollback any pref changes
   Object.keys(DEFAULT_PREFS).forEach(pref => {
     Preferences.set(pref, DEFAULT_PREFS[pref]);
   });
 
-  // Make sure the profiler module is stopped when the test finishes.
-  nsIProfilerModule.StopProfiler();
-
   Cu.forceGC();
 });
 
 function addTab(aUrl, aWindow) {
   info("Adding tab: " + aUrl);
 
   let deferred = Promise.defer();
   let targetWindow = aWindow || window;
@@ -572,8 +567,44 @@ function synthesizeProfileForTest(sample
   });
 
   let uniqueStacks = new RecordingUtils.UniqueStacks();
   return RecordingUtils.deflateThread({
     samples: samples,
     markers: []
   }, uniqueStacks);
 }
+
+function PMM_isProfilerActive () {
+  return sendProfilerCommand("IsActive");
+}
+
+function PMM_stopProfiler () {
+  return Task.spawn(function*() {
+    let isActive = (yield sendProfilerCommand("IsActive")).isActive;
+    if (isActive) {
+      return sendProfilerCommand("StopProfiler");
+    }
+  });
+}
+
+function sendProfilerCommand (method, args=[]) {
+  let deferred = Promise.defer();
+
+  if (!mm) {
+    throw new Error("`loadFrameScripts()` must be called when using MessageManager.");
+  }
+
+  let id = generateUUID().toString();
+  mm.addMessageListener("devtools:test:profiler:response", handler);
+  mm.sendAsyncMessage("devtools:test:profiler", { method, args, id });
+
+  function handler ({ data }) {
+    if (id !== data.id) {
+      return;
+    }
+
+    mm.removeMessageListener("devtools:test:profiler:response", handler);
+    deferred.resolve(data.data);
+  }
+
+  return deferred.promise;
+}
--- a/browser/devtools/performance/views/toolbar.js
+++ b/browser/devtools/performance/views/toolbar.js
@@ -55,17 +55,17 @@ let ToolbarView = {
     for (let [markerName, markerDetails] of Iterator(TIMELINE_BLUEPRINT)) {
       let menuitem = document.createElement("menuitem");
       menuitem.setAttribute("closemenu", "none");
       menuitem.setAttribute("type", "checkbox");
       menuitem.setAttribute("align", "center");
       menuitem.setAttribute("flex", "1");
       menuitem.setAttribute("label", MarkerUtils.getMarkerClassName(markerName));
       menuitem.setAttribute("marker-type", markerName);
-      menuitem.className = markerDetails.colorName;
+      menuitem.className = `marker-color-${markerDetails.colorName}`;
 
       menuitem.addEventListener("command", this._onHiddenMarkersChanged);
 
       $("#performance-filter-menupopup").appendChild(menuitem);
     }
   },
 
   /**
deleted file mode 100644
--- a/browser/devtools/profiler/moz.build
+++ /dev/null
@@ -1,9 +0,0 @@
-# vim: set filetype=python:
-# 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/.
-
-EXTRA_JS_MODULES.devtools.profiler += [
-    'panel.js',
-    'utils/shared.js'
-]
deleted file mode 100644
--- a/browser/devtools/profiler/panel.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* 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/. */
-"use strict";
-
-const {Cc, Ci, Cu, Cr} = require("chrome");
-
-Cu.import("resource://gre/modules/Task.jsm");
-
-loader.lazyRequireGetter(this, "promise");
-loader.lazyRequireGetter(this, "EventEmitter",
-  "devtools/toolkit/event-emitter");
-
-loader.lazyRequireGetter(this, "getProfilerConnection",
-  "devtools/profiler/shared", true);
-loader.lazyRequireGetter(this, "ProfilerFront",
-  "devtools/profiler/shared", true);
-
-function ProfilerPanel(iframeWindow, toolbox) {
-  this.panelWin = iframeWindow;
-  this._toolbox = toolbox;
-
-  EventEmitter.decorate(this);
-}
-
-exports.ProfilerPanel = ProfilerPanel;
-
-ProfilerPanel.prototype = {
-  /**
-   * Open is effectively an asynchronous constructor.
-   *
-   * @return object
-   *         A promise that is resolved when the Profiler completes opening.
-   */
-  open: Task.async(function*() {
-    this._connection = getProfilerConnection(this._toolbox);
-    yield this._connection.open();
-
-    this.panelWin.gToolbox = this._toolbox;
-    this.panelWin.gTarget = this.target;
-    this.panelWin.gFront = new ProfilerFront(this._connection);
-    yield this.panelWin.startupProfiler();
-
-    this.isReady = true;
-    this.emit("ready");
-    return this;
-  }),
-
-  // DevToolPanel API
-
-  get target() this._toolbox.target,
-
-  destroy: Task.async(function*() {
-    // Make sure this panel is not already destroyed.
-    if (this._destroyed) {
-      return;
-    }
-
-    yield this.panelWin.shutdownProfiler();
-
-    // Destroy the connection to ensure packet handlers are removed from client.
-    this._connection.destroy();
-
-    this.emit("destroyed");
-    this._destroyed = true;
-  })
-};
deleted file mode 100644
--- a/browser/devtools/profiler/profiler.js
+++ /dev/null
@@ -1,232 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/devtools/Loader.jsm");
-Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
-
-devtools.lazyRequireGetter(this, "Services");
-devtools.lazyRequireGetter(this, "promise");
-devtools.lazyRequireGetter(this, "EventEmitter",
-  "devtools/toolkit/event-emitter");
-devtools.lazyRequireGetter(this, "DevToolsUtils",
-  "devtools/toolkit/DevToolsUtils");
-devtools.lazyRequireGetter(this, "FramerateFront",
-  "devtools/server/actors/framerate", true);
-
-devtools.lazyRequireGetter(this, "L10N",
-  "devtools/shared/profiler/global", true);
-devtools.lazyRequireGetter(this, "CATEGORIES",
-  "devtools/shared/profiler/global", true);
-devtools.lazyRequireGetter(this, "CATEGORY_MAPPINGS",
-  "devtools/shared/profiler/global", true);
-devtools.lazyRequireGetter(this, "CATEGORY_OTHER",
-  "devtools/shared/profiler/global", true);
-devtools.lazyRequireGetter(this, "ThreadNode",
-  "devtools/shared/profiler/tree-model", true);
-devtools.lazyRequireGetter(this, "CallView",
-  "devtools/shared/profiler/tree-view", true);
-
-devtools.lazyImporter(this, "FileUtils",
-  "resource://gre/modules/FileUtils.jsm");
-devtools.lazyImporter(this, "NetUtil",
-  "resource://gre/modules/NetUtil.jsm");
-devtools.lazyImporter(this, "LineGraphWidget",
-  "resource:///modules/devtools/Graphs.jsm");
-devtools.lazyImporter(this, "BarGraphWidget",
-  "resource:///modules/devtools/Graphs.jsm");
-devtools.lazyImporter(this, "CanvasGraphUtils",
-  "resource:///modules/devtools/Graphs.jsm");
-devtools.lazyImporter(this, "SideMenuWidget",
-  "resource:///modules/devtools/SideMenuWidget.jsm");
-
-const RECORDING_DATA_DISPLAY_DELAY = 10; // ms
-const FRAMERATE_CALC_INTERVAL = 16; // ms
-const FRAMERATE_GRAPH_HEIGHT = 60; // px
-const CATEGORIES_GRAPH_HEIGHT = 60; // px
-const CATEGORIES_GRAPH_MIN_BARS_WIDTH = 3; // px
-const CALL_VIEW_FOCUS_EVENTS_DRAIN = 10; // ms
-const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms
-const GRAPH_ZOOM_MIN_TIMESPAN = 20; // ms
-
-// This identifier string is used to tentatively ascertain whether or not
-// a JSON loaded from disk is actually something generated by this tool.
-// It isn't, of course, a definitive verification, but a Good Enough™
-// approximation before continuing the import. Don't localize this.
-const PROFILE_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
-const PROFILE_SERIALIZER_VERSION = 1;
-
-// The panel's window global is an EventEmitter firing the following events:
-const EVENTS = {
-  // When a recording is started or stopped, via the `stopwatch` button, or
-  // when `console.profile` and `console.profileEnd` is invoked.
-  RECORDING_STARTED: "Profiler:RecordingStarted",
-  RECORDING_ENDED: "Profiler:RecordingEnded",
-
-  // When a recording is abruptly ended, either because the built-in profiler
-  // module is stopped by a third party, or because the recordings list is
-  // cleared while there's one in progress.
-  RECORDING_LOST: "Profiler:RecordingCancelled",
-
-  // When a recording is displayed in the ProfileView.
-  RECORDING_DISPLAYED: "Profiler:RecordingDisplayed",
-
-  // When a new tab is spawned in the ProfileView from a graphs selection.
-  TAB_SPAWNED_FROM_SELECTION: "Profiler:TabSpawnedFromSelection",
-
-  // When a new tab is spawned in the ProfileView from a node in the tree.
-  TAB_SPAWNED_FROM_FRAME_NODE: "Profiler:TabSpawnedFromFrameNode",
-
-  // When different panels in the ProfileView are shown.
-  EMPTY_NOTICE_SHOWN: "Profiler:EmptyNoticeShown",
-  RECORDING_NOTICE_SHOWN: "Profiler:RecordingNoticeShown",
-  LOADING_NOTICE_SHOWN: "Profiler:LoadingNoticeShown",
-  TABBED_BROWSER_SHOWN: "Profiler:TabbedBrowserShown",
-
-  // When a source is shown in the JavaScript Debugger at a specific location.
-  SOURCE_SHOWN_IN_JS_DEBUGGER: "Profiler:SourceShownInJsDebugger",
-  SOURCE_NOT_FOUND_IN_JS_DEBUGGER: "Profiler:SourceNotFoundInJsDebugger"
-};
-
-/**
- * The current target and the profiler connection, set by this tool's host.
- */
-let gToolbox, gTarget, gFront;
-
-/**
- * Initializes the profiler controller and views.
- */
-let startupProfiler = Task.async(function*() {
-  yield promise.all([
-    PrefObserver.register(),
-    EventsHandler.initialize(),
-    RecordingsListView.initialize(),
-    ProfileView.initialize()
-  ]);
-
-  // Profiles may have been created before this tool was opened, e.g. via
-  // `console.profile` and `console.profileEnd(). Populate the UI with them.
-  for (let recordingData of gFront.finishedConsoleRecordings) {
-    let profileLabel = recordingData.profilerData.profileLabel;
-    let recordingItem = RecordingsListView.addEmptyRecording(profileLabel);
-    RecordingsListView.customizeRecording(recordingItem, recordingData);
-  }
-  for (let { profileLabel } of gFront.pendingConsoleRecordings) {
-    RecordingsListView.handleRecordingStarted(profileLabel);
-  }
-
-  // Select the first recording, if available.
-  RecordingsListView.selectedIndex = 0;
-});
-
-/**
- * Destroys the profiler controller and views.
- */
-let shutdownProfiler = Task.async(function*() {
-  yield promise.all([
-    PrefObserver.unregister(),
-    EventsHandler.destroy(),
-    RecordingsListView.destroy(),
-    ProfileView.destroy()
-  ]);
-});
-
-/**
- * Observes pref changes on the devtools.profiler branch and triggers the
- * required frontend modifications.
- */
-let PrefObserver = {
-  register: function() {
-    this.branch = Services.prefs.getBranch("devtools.profiler.");
-    this.branch.addObserver("", this, false);
-  },
-  unregister: function() {
-    this.branch.removeObserver("", this);
-  },
-  observe: function(subject, topic, pref) {
-    if (pref == "ui.show-platform-data") {
-      RecordingsListView.forceSelect(RecordingsListView.selectedItem);
-    }
-  }
-};
-
-/**
- * Functions handling target-related lifetime events.
- */
-let EventsHandler = {
-  /**
-   * Listen for events emitted by the current tab target.
-   */
-  initialize: function() {
-    this._onConsoleProfileStart = this._onConsoleProfileStart.bind(this);
-    this._onConsoleProfileEnd = this._onConsoleProfileEnd.bind(this);
-
-    gFront.on("profile", this._onConsoleProfileStart);
-    gFront.on("profileEnd", this._onConsoleProfileEnd);
-    gFront.on("profiler-unexpectedly-stopped", this._onProfilerDeactivated);
-  },
-
-  /**
-   * Remove events emitted by the current tab target.
-   */
-  destroy: function() {
-    gFront.off("profile", this._onConsoleProfileStart);
-    gFront.off("profileEnd", this._onConsoleProfileEnd);
-    gFront.off("profiler-unexpectedly-stopped", this._onProfilerDeactivated);
-  },
-
-  /**
-   * Invoked whenever `console.profile` is called.
-   *
-   * @param string profileLabel
-   *        The provided string argument if available, undefined otherwise.
-   */
-  _onConsoleProfileStart: function(event, profileLabel) {
-    RecordingsListView.handleRecordingStarted(profileLabel);
-  },
-
-  /**
-   * Invoked whenever `console.profileEnd` is called.
-   *
-   * @param object recordingData
-   *        The profiler and refresh driver ticks data received from the front.
-   */
-  _onConsoleProfileEnd: function(event, recordingData) {
-    RecordingsListView.handleRecordingEnded(recordingData);
-  },
-
-  /**
-   * Invoked whenever the built-in profiler module is deactivated.
-   * @see ProfilerConnection.prototype._onProfilerUnexpectedlyStopped
-   */
-  _onProfilerDeactivated: function() {
-    RecordingsListView.removeForPredicate(e => e.isRecording);
-    RecordingsListView.handleRecordingCancelled();
-  }
-};
-
-/**
- * Shortcuts for accessing various profiler preferences.
- */
-const Prefs = new ViewHelpers.Prefs("devtools.profiler", {
-  showPlatformData: ["Bool", "ui.show-platform-data"]
-});
-
-/**
- * Convenient way of emitting events from the panel window.
- */
-EventEmitter.decorate(this);
-
-/**
- * DOM query helpers.
- */
-function $(selector, target = document) {
-  return target.querySelector(selector);
-}
-function $$(selector, target = document) {
-  return target.querySelectorAll(selector);
-}
deleted file mode 100644
--- a/browser/devtools/profiler/profiler.xul
+++ /dev/null
@@ -1,142 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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/. -->
-<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/devtools/widgets.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/skin/devtools/profiler.css" type="text/css"?>
-<!DOCTYPE window [
-  <!ENTITY % profilerDTD SYSTEM "chrome://browser/locale/devtools/profiler.dtd">
-  %profilerDTD;
-]>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script src="chrome://browser/content/devtools/theme-switching.js"/>
-  <script type="application/javascript" src="profiler.js"/>
-  <script type="application/javascript" src="ui-recordings.js"/>
-  <script type="application/javascript" src="ui-profile.js"/>
-
-  <hbox class="theme-body" flex="1">
-    <vbox id="recordings-pane">
-      <toolbar id="recordings-toolbar"
-               class="devtools-toolbar">
-        <hbox id="recordings-controls"
-              class="devtools-toolbarbutton-group">
-          <toolbarbutton id="record-button"
-                         class="devtools-toolbarbutton"
-                         oncommand="RecordingsListView._onRecordButtonClick()"
-                         tooltiptext="&profilerUI.recordButton.tooltip;"/>
-          <toolbarbutton id="import-button"
-                         class="devtools-toolbarbutton"
-                         oncommand="RecordingsListView._onImportButtonClick()"
-                         label="&profilerUI.importButton;"/>
-          <toolbarbutton id="clear-button"
-                         class="devtools-toolbarbutton"
-                         oncommand="RecordingsListView._onClearButtonClick()"
-                         label="&profilerUI.clearButton;"/>
-        </hbox>
-      </toolbar>
-      <vbox id="recordings-list" flex="1"/>
-      <splitter class="devtools-horizontal-splitter" />
-      <vbox id="profile-options" class="devtools-toolbar">
-        <checkbox id="invert-tree" label="&profilerUI.invertTree;"
-                  tooltiptext="&profilerUI.invertTree.tooltiptext;" />
-      </vbox>
-    </vbox>
-
-    <deck id="profile-pane"
-          class="devtools-responsive-container"
-          flex="1">
-      <hbox id="empty-notice"
-            class="notice-container"
-            align="center"
-            pack="center"
-            flex="1">
-        <label value="&profilerUI.emptyNotice1;"/>
-        <button id="profiling-notice-button"
-                class="devtools-toolbarbutton"
-                standalone="true"
-                oncommand="RecordingsListView._onRecordButtonClick()"/>
-        <label value="&profilerUI.emptyNotice2;"/>
-      </hbox>
-
-      <hbox id="recording-notice"
-            class="notice-container"
-            align="center"
-            pack="center"
-            flex="1">
-        <label value="&profilerUI.stopNotice1;"/>
-        <button id="profiling-notice-button"
-                class="devtools-toolbarbutton"
-                standalone="true"
-                checked="true"
-                oncommand="RecordingsListView._onRecordButtonClick()"/>
-        <label value="&profilerUI.stopNotice2;"/>
-      </hbox>
-
-      <hbox id="loading-notice"
-            class="notice-container"
-            align="center"
-            pack="center"
-            flex="1">
-        <label value="&profilerUI.loadingNotice;"/>
-      </hbox>
-
-      <tabbox id="profile-content"
-              class="theme-sidebar devtools-sidebar-tabs"
-              flex="1">
-        <hbox>
-          <tabs/>
-          <button id="profile-newtab-button"
-                  tooltiptext="&profilerUI.newtab.tooltiptext;"/>
-        </hbox>
-        <tabpanels flex="1"/>
-      </tabbox>
-    </deck>
-  </hbox>
-
-  <template>
-    <!-- Template for a tab inside the #profile-content tabbox. -->
-    <tab id="profile-content-tab-template" covered="true">
-      <label class="tab-title-label"/>
-    </tab>
-
-    <!-- Template for a panel inside the #profile-content tabbox. -->
-    <tabpanel id="profile-content-tabpanel-template">
-      <vbox class="framerate"/>
-      <vbox class="categories"/>
-      <vbox class="call-tree" flex="1">
-        <hbox class="call-tree-headers-container">
-          <label class="plain call-tree-header"
-                 type="duration"
-                 crop="end"
-                 value="&profilerUI.table.totalDuration2;"/>
-          <label class="plain call-tree-header"
-                 type="percentage"
-                 crop="end"
-                 value="&profilerUI.table.totalPercentage;"/>
-          <label class="plain call-tree-header"
-                 type="self-duration"
-                 crop="end"
-                 value="&profilerUI.table.selfDuration2;"/>
-          <label class="plain call-tree-header"
-                 type="self-percentage"
-                 crop="end"
-                 value="&profilerUI.table.selfPercentage;"/>
-          <label class="plain call-tree-header"
-                 type="samples"
-                 crop="end"
-                 value="&profilerUI.table.samples;"/>
-          <label class="plain call-tree-header"
-                 type="function"
-                 crop="end"
-                 value="&profilerUI.table.function;"/>
-        </hbox>
-        <vbox class="call-tree-cells-container" flex="1"/>
-      </vbox>
-    </tabpanel>
-  </template>
-
-</window>
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser.ini
+++ /dev/null
@@ -1,93 +0,0 @@
-[DEFAULT]
-tags = devtools
-subsuite = devtools
-support-files =
-  doc_simple-test.html
-  head.js
-
-[browser_profiler_aaa_run_first_leaktest.js]
-[browser_profiler_console-record-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-04.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-05.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-06.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-07.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-08.js]
-skip-if = true # Bug 1047124
-[browser_profiler_console-record-09.js]
-skip-if = true # Bug 1047124
-[browser_profiler_data-massaging-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_data-massaging-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_data-samples.js]
-skip-if = true # Bug 1047124
-[browser_profiler_gecko-pref-changed.js]
-skip-if = true # Bug 1047124
-[browser_profiler_jump-to-debugger-01.js]
-[browser_profiler_jump-to-debugger-02.js]
-[browser_profiler_profile-deselection.js]
-skip-if = true # Bug 1047124
-[browser_profiler_profile-view-events.js]
-[browser_profiler_recording-cancelled.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recording-selected-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recording-selected-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recording-selected-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recording-utils.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recordings-clear.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recordings-io-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_recordings-io-02.js]
-[browser_profiler_recordings-io-03.js]
-[browser_profiler_shared-connection-01.js]
-[browser_profiler_shared-connection-02.js]
-[browser_profiler_shared-connection-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-connection-04.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-04.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-05.js]
-skip-if = true # Bug 1047124
-[browser_profiler_shared-front-06.js]
-skip-if = true # Bug 1047124
-[browser_profiler_simple-record-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_simple-record-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_simple-record-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_sudden-deactivation-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_sudden-deactivation-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_tabbed-browser-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_tabbed-browser-02.js]
-skip-if = true # Bug 1047124
-[browser_profiler_tabbed-browser-03.js]
-skip-if = true # Bug 1047124
-[browser_profiler_tabbed-browser-add-remove-01.js]
-skip-if = true # Bug 1047124
-[browser_profiler_tabbed-browser-add-remove-02.js]
-skip-if = true # Bug 1047124
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_aaa_run_first_leaktest.js
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler leaks on initialization and sudden destruction.
- * You can also use this initialization format as a template for other tests.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-
-  ok(target, "Should have a target available.");
-  ok(debuggee, "Should have a debuggee available.");
-  ok(panel, "Should have a panel available.");
-
-  ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window.");
-  ok(panel.panelWin.gTarget, "Should have a target reference on the panel window.");
-  ok(panel.panelWin.gFront, "Should have a front reference on the panel window.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-01.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is populated by console recordings that have finished
- * before it was opened.
- */
-
-let test = Task.async(function*() {
-  let profilerConnected = waitForProfilerConnection();
-
-  let [target, debuggee, networkPanel] = yield initFrontend(SIMPLE_URL, "netmonitor");
-  let toolbox = networkPanel._toolbox;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(toolbox);
-
-  yield profilerConnected;
-  yield consoleProfile(sharedProfilerConnection, 1);
-  yield consoleProfile(sharedProfilerConnection, 2);
-  yield consoleProfileEnd(sharedProfilerConnection);
-  yield consoleProfileEnd(sharedProfilerConnection);
-
-  yield toolbox.selectTool("jsprofiler");
-  let profilerPanel = toolbox.getCurrentPanel();
-  let { $, EVENTS, RecordingsListView, ProfileView } = profilerPanel.panelWin;
-
-  yield profilerPanel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  ok(true, "The first already finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be automatically selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "2",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "1",
-    "The profile label for the second recording is correct.");
-  ok(!RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  yield teardown(networkPanel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(connection) {
-  let notified = connection.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-02.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is populated by in-progress console recordings
- * when it is opened.
- */
-
-let test = Task.async(function*() {
-  let profilerConnected = waitForProfilerConnection();
-
-  let [target, debuggee, networkPanel] = yield initFrontend(SIMPLE_URL, "netmonitor");
-  let toolbox = networkPanel._toolbox;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(toolbox);
-
-  yield profilerConnected;
-  yield consoleProfile(sharedProfilerConnection, 1);
-  yield consoleProfile(sharedProfilerConnection, 2);
-
-  yield toolbox.selectTool("jsprofiler");
-  let profilerPanel = toolbox.getCurrentPanel();
-  let { $, L10N, RecordingsListView, ProfileView } = profilerPanel.panelWin;
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 0,
-    "There shouldn't be any tabs visible yet.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be automatically selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is correct.");
-  ok(RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is correct.");
-  ok(RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  let firstTarget = RecordingsListView.items[0].target;
-  let secondTarget = RecordingsListView.items[1].target;
-
-  is($(".recording-item-title", firstTarget).getAttribute("value"), "1",
-    "The first recording item's title is correct.");
-  is($(".recording-item-title", secondTarget).getAttribute("value"), "2",
-    "The second recording item's title is correct.");
-
-  is($(".recording-item-duration", firstTarget).getAttribute("value"),
-    L10N.getStr("recordingsList.recordingLabel"),
-    "The first recording item's duration is correct.");
- is($(".recording-item-duration", secondTarget).getAttribute("value"),
-    L10N.getStr("recordingsList.recordingLabel"),
-    "The second recording item's duration is correct.");
-
-  is($(".recording-item-save", firstTarget).getAttribute("value"), "",
-    "The first recording item's save link should be invisible.");
-  is($(".recording-item-save", secondTarget).getAttribute("value"), "",
-    "The second recording item's save link should be invisible.");
-
-  yield teardown(profilerPanel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-03.js
+++ /dev/null
@@ -1,95 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is populated by in-progress console recordings, and
- * also console recordings that have finished before it was opened.
- */
-
-let test = Task.async(function*() {
-  let profilerConnected = waitForProfilerConnection();
-
-  let [target, debuggee, networkPanel] = yield initFrontend(SIMPLE_URL, "netmonitor");
-  let toolbox = networkPanel._toolbox;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(toolbox);
-
-  yield profilerConnected;
-  yield consoleProfile(sharedProfilerConnection, 1);
-  yield consoleProfile(sharedProfilerConnection, 2);
-  yield consoleProfileEnd(sharedProfilerConnection);
-
-  yield toolbox.selectTool("jsprofiler");
-  let profilerPanel = toolbox.getCurrentPanel();
-  let { $, EVENTS, RecordingsListView, ProfileView } = profilerPanel.panelWin;
-
-  yield profilerPanel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  ok(true, "The already finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be automatically selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "2",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "1",
-    "The profile label for the second recording is correct.");
-  ok(RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  let recordingDisplayed = profilerPanel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(sharedProfilerConnection);
-  yield recordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There still shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should still be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-
-  is(RecordingsListView.items[1], RecordingsListView.selectedItem,
-    "The second recording item should still be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "2",
-    "The profile label for the first recording is still correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is still correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "1",
-    "The profile label for the second recording is still correct.");
-  ok(!RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is still correct.");
-
-  yield teardown(profilerPanel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(connection) {
-  let notified = connection.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-04.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is correctly populated by new console recordings
- * after it is opened.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "hello world");
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 0,
-    "There shouldn't be any tabs visible yet.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first and only recording item should be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "hello world",
-    "The profile label for the first recording is correct.");
-  ok(RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  let recordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront);
-  yield recordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 1,
-    "There should still be one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There still shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should still be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first and only recording item should still be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "hello world",
-    "The profile label for the first recording is still correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is still correct.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front) {
-  let notified = front.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-05.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is correctly populated by sequential console recordings
- * with the same label, after it is opened.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "hello world");
-  let firstRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront);
-  yield firstRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  yield consoleProfile(gFront, "hello world");
-  let secondRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront);
-  yield secondRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically redisplayed.");
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be just one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first and only recording item should be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "hello world",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front) {
-  let notified = front.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-06.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can correctly handle sequential console recordings,
- * finished in reverse order.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "1");
-  yield consoleProfile(gFront, "2");
-
-  let firstRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront, "1");
-  yield firstRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is correct.");
-  ok(RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  let secondRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront, "2");
-  yield secondRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically redisplayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.items[1], RecordingsListView.selectedItem,
-    "The second recording item should now be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is still correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is still correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is still correct.");
-  ok(!RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is still correct.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front, label) {
-  let notified = front.once("profileEnd");
-  console.profileEnd(label);
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-07.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can correctly handle sequential console recordings,
- * finished in reverse order, and the second call to `console.profileEnd`
- * doesn't have any argument.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "1");
-  yield consoleProfile(gFront, "2");
-
-  let firstRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront, true, "1");
-  yield firstRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is correct.");
-  ok(RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  let secondRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront);
-  yield secondRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically redisplayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.items[1], RecordingsListView.selectedItem,
-    "The second recording item should now be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is still correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is still correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is still correct.");
-  ok(!RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is still correct.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front, withLabel, labelValue) {
-  let notified = front.once("profileEnd");
-  if (!withLabel) {
-    console.profileEnd();
-  } else {
-    console.profileEnd(labelValue);
-  }
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-08.js
+++ /dev/null
@@ -1,61 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can correctly handle sequential console recordings,
- * finished in reverse order, and the second call to `console.profileEnd`
- * contains the same label as the first call.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "1");
-  yield consoleProfile(gFront, "2");
-
-  let firstRecordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield consoleProfileEnd(gFront, "1");
-  yield firstRecordingDisplayed;
-  ok(true, "The newly finished console recording was automatically displayed.");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is(RecordingsListView.items[0], RecordingsListView.selectedItem,
-    "The first recording item should be selected.");
-
-  is(RecordingsListView.items[0].attachment.profilerData.profileLabel, "1",
-    "The profile label for the first recording is correct.");
-  ok(!RecordingsListView.items[0].isRecording,
-    "The 'isRecording' flag for the first recording is correct.");
-
-  is(RecordingsListView.items[1].attachment.profilerData.profileLabel, "2",
-    "The profile label for the second recording is correct.");
-  ok(RecordingsListView.items[1].isRecording,
-    "The 'isRecording' flag for the second recording is correct.");
-
-  gFront.once("profileEnd", () => {
-    ok(false, "The second console recording shouldn't have ended.")
-  });
-  panel.panelWin.once(EVENTS.RECORDING_DISPLAYED, () => {
-    ok(false, "The second console recording shouldn't have been displayed.");
-  });
-
-  console.profileEnd("1");
-  yield DevToolsUtils.waitForTime(1000);
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front, label) {
-  let notified = front.once("profileEnd");
-  console.profileEnd(label);
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_console-record-09.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can correctly handle simultaneous console and manual
- * recordings (via `console.profile` and clicking the record button).
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView, ProfileView } = panel.panelWin;
-
-  info("Starting a manual recording...");
-  yield startRecording(panel);
-
-  info("Starting two console recordings, one without a label, one with.");
-  yield consoleProfile(gFront);
-  yield consoleProfile(gFront, true, "hello world");
-
-  is(RecordingsListView.itemCount, 3,
-    "There should be three recordings visible now.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording item was selected in the list.");
-
-  testListItem(RecordingsListView, 0, undefined, true);
-  testListItem(RecordingsListView, 1, undefined, true);
-  testListItem(RecordingsListView, 2, "hello world", true);
-
-  info("Stopping the manual recording...");
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(RecordingsListView.itemCount, 3,
-    "There should still be three recordings visible now.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording item is still selected in the list.");
-
-  testListItem(RecordingsListView, 0, undefined, false);
-  testListItem(RecordingsListView, 1, undefined, true);
-  testListItem(RecordingsListView, 2, "hello world", true);
-
-  info("Stopping the unlabeled console recording...");
-  yield consoleProfileEnd(gFront);
-
-  is(RecordingsListView.itemCount, 3,
-    "There should still be three recordings visible now.");
-  is(RecordingsListView.selectedIndex, 1,
-    "The second recording item was selected in the list.");
-
-  testListItem(RecordingsListView, 0, undefined, false);
-  testListItem(RecordingsListView, 1, undefined, false);
-  testListItem(RecordingsListView, 2, "hello world", true);
-
-  info("Stopping the labeled console recording...");
-  yield consoleProfileEnd(gFront);
-
-  is(RecordingsListView.itemCount, 3,
-    "There should still be three recordings visible now.");
-  is(RecordingsListView.selectedIndex, 2,
-    "The third recording item was selected in the list.");
-
-  testListItem(RecordingsListView, 0, undefined, false);
-  testListItem(RecordingsListView, 1, undefined, false);
-  testListItem(RecordingsListView, 2, "hello world", false);
-
-  yield teardown(panel);
-  finish();
-});
-
-function testListItem(view, index, profileLabel, isRecording) {
-  is(view.items[index].attachment.profilerData.profileLabel, profileLabel,
-    "The recording item at index " + index + " has a correct profile label.");
-  is(!!view.items[index].isRecording, isRecording,
-    "The recording item at index " + index + " has a correct `isRecording` flag.");
-}
-
-function* consoleProfile(front, withLabel, labelValue) {
-  let notified = front.once("profile");
-  if (!withLabel) {
-    console.profile();
-  } else {
-    console.profile(labelValue);
-  }
-  yield notified;
-}
-
-function* consoleProfileEnd(front) {
-  let notified = front.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_data-massaging-01.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the retrieved profiler data samples are correctly filtered and
- * normalized before passed to consumers.
- */
-
-const WAIT_TIME = 1000; // ms
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  // Perform the first recording...
-
-  yield front.startRecording();
-  let profilingStartTime = front._profilingStartTime;
-  info("Started profiling at: " + profilingStartTime);
-
-  busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
-
-  let firstRecordingData = yield front.stopRecording();
-  let firstRecordingFinishTime = firstRecordingData.profilerData.currentTime;
-
-  is(profilingStartTime, 0,
-    "The profiling start time should be 0 for the first recording.");
-  ok(firstRecordingData.recordingDuration >= WAIT_TIME,
-    "The first recording duration is correct.");
-  ok(firstRecordingFinishTime >= WAIT_TIME,
-    "The first recording finish time is correct.");
-
-  // Perform the second recording...
-
-  yield front.startRecording();
-  profilingStartTime = front._profilingStartTime;
-  info("Started profiling at: " + profilingStartTime);
-
-  busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
-
-  let secondRecordingData = yield front.stopRecording();
-  let secondRecordingFinishTime = secondRecordingData.profilerData.currentTime;
-  let secondRecordingProfile = secondRecordingData.profilerData.profile;
-  let secondRecordingSamples = secondRecordingProfile.threads[0].samples;
-
-  isnot(profilingStartTime, 0,
-    "The profiling start time should not be 0 on the second recording.");
-  ok(secondRecordingData.recordingDuration >= WAIT_TIME,
-    "The second recording duration is correct.");
-  ok(secondRecordingFinishTime - firstRecordingFinishTime >= WAIT_TIME,
-    "The second recording finish time is correct.");
-
-  ok(secondRecordingSamples[0].time < profilingStartTime,
-    "The second recorded sample times were normalized.");
-  ok(secondRecordingSamples[0].time > 0,
-    "The second recorded sample times were normalized correctly.");
-  ok(!secondRecordingSamples.find(e => e.time + profilingStartTime <= firstRecordingFinishTime),
-    "There should be no samples from the first recording in the second one, " +
-    "even though the total number of frames did not overflow.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_data-massaging-02.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the retrieved profiler data samples are correctly filtered and
- * normalized before passed to consumers.
- */
-
-const WAIT_TIME = 1000; // ms
-const FRAMES_OVERFLOW = 1000;
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  front._customProfilerOptions = { entries: FRAMES_OVERFLOW };
-
-  yield front.startRecording();
-  let profilingStartTime = front._profilingStartTime;
-  info("Started profiling at: " + profilingStartTime);
-
-  yield idleWait(WAIT_TIME); // allow refresh driver ticks to accumulate
-  busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
-  yield idleWait(WAIT_TIME); // allow refresh driver ticks to accumulate more
-
-  let recordingData = yield front.stopRecording();
-  let ticks = recordingData.ticksData;
-  let profile = recordingData.profilerData.profile;
-  let samples = profile.threads[0].samples;
-
-  ok(samples[0].time >= WAIT_TIME,
-    "The recorded samples overflowed, so the older ones were clamped.");
-
-  if (ticks.length) {
-    ok(ticks[0] >= samples[0].time,
-      "The refresh driver ticks were filtered before being retrieved (1).");
-    ok(ticks[ticks.length - 1] <= findOldestSampleTime(samples),
-      "The refresh driver ticks were filtered before being retrieved (2).");
-  }
-
-  yield teardown(panel);
-  finish();
-});
-
-function findOldestSampleTime(samples) {
-  for (let i = samples.length - 1; i >= 0; i--) {
-    let sample = samples[i];
-    if ("time" in sample) {
-      return sample.time;
-    }
-  }
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_data-samples.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the retrieved profiler data samples always have a (root) node.
- * If this ever changes, the |ThreadNode.prototype.insert| function in
- * browser/devtools/profiler/utils/tree-model.js will have to be changed.
- */
-
-const WAIT_TIME = 1000; // ms
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  yield front.startRecording();
-  busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
-
-  let recordingData = yield front.stopRecording();
-  let profile = recordingData.profilerData.profile;
-
-  for (let thread of profile.threads) {
-    info("Checking thread: " + thread.name);
-
-    for (let sample of thread.samples) {
-      if (sample.frames[0].location != "(root)") {
-        ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
-      }
-    }
-  }
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_gecko-pref-changed.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profile view is rebuilt every time the
- * devtools.profiler.ui.show-platform-data pref is changed.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, Prefs, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  let emptyNoticeDisplayed = panel.panelWin.once(EVENTS.EMPTY_NOTICE_SHOWN);
-  let tabbedBrowserRedisplayed = panel.panelWin.once(EVENTS.TABBED_BROWSER_SHOWN);
-  let recordingRedisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-
-  let prevPrefValue = Prefs.showPlatformData;
-  Prefs.showPlatformData ^= 1;
-
-  yield emptyNoticeDisplayed;
-  yield tabbedBrowserRedisplayed;
-  yield recordingRedisplayed;
-  ok(true, "The profile view was rebuilt.");
-
-  is(RecordingsListView.itemCount, 1,
-    "There should still be one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There still shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should still be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_jump-to-debugger-01.js
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can jump to the debugger.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let toolbox = panel._toolbox;
-  let EVENTS = panel.panelWin.EVENTS;
-
-  let whenSourceShown = panel.panelWin.once(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
-  panel.panelWin.viewSourceInDebugger(SIMPLE_URL, 14);
-  yield whenSourceShown;
-
-  let debuggerPanel = toolbox.getPanel("jsdebugger");
-  ok(debuggerPanel, "The debugger panel was opened.");
-
-  let { DebuggerView } = debuggerPanel.panelWin;
-  let Sources = DebuggerView.Sources;
-
-  is(Sources.selectedValue, getSourceActor(Sources, SIMPLE_URL),
-    "The correct source is shown in the debugger.");
-  is(DebuggerView.editor.getCursor().line + 1, 14,
-    "The correct line is highlighted in the debugger's source editor.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_jump-to-debugger-02.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler can jump to the debugger, when the source was already
- * loaded in that tool.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, debuggerPanel] = yield initFrontend(SIMPLE_URL, "jsdebugger");
-  let toolbox = debuggerPanel._toolbox;
-  let debuggerWin = debuggerPanel.panelWin;
-  let debuggerEvents = debuggerWin.EVENTS;
-  let { DebuggerView } = debuggerWin;
-  let Sources = DebuggerView.Sources;
-
-  yield debuggerWin.once(debuggerEvents.SOURCE_SHOWN);
-  ok("A source was shown in the debugger.");
-
-  is(Sources.selectedValue, getSourceActor(Sources, SIMPLE_URL),
-    "The correct source is initially shown in the debugger.");
-  is(DebuggerView.editor.getCursor().line, 0,
-    "The correct line is initially highlighted in the debugger's source editor.");
-
-  yield toolbox.selectTool("jsprofiler");
-  let profilerPanel = toolbox.getCurrentPanel();
-  let profilerWin = profilerPanel.panelWin;
-  let profilerEvents = profilerWin.EVENTS;
-
-  let whenSourceShown = profilerWin.once(profilerEvents.SOURCE_SHOWN_IN_JS_DEBUGGER);
-  profilerWin.viewSourceInDebugger(SIMPLE_URL, 14);
-  yield whenSourceShown;
-
-  debuggerPanel = toolbox.getPanel("jsdebugger");
-  ok(debuggerPanel, "The debugger panel was reselected.");
-
-  is(DebuggerView.Sources.selectedValue, getSourceActor(Sources, SIMPLE_URL),
-    "The correct source is still shown in the debugger.");
-  is(DebuggerView.editor.getCursor().line + 1, 14,
-    "The correct line is now highlighted in the debugger's source editor.");
-
-  yield teardown(profilerPanel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_profile-deselection.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler shows the appropriate notice when a selection
- * from the recordings list is lost.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, RecordingsListView } = panel.panelWin;
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible yet.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view.");
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible now.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  RecordingsListView.selectedItem = null;
-
-  is(RecordingsListView.itemCount, 1,
-    "There should still be one recording visible now.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view again.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_profile-view-events.js
+++ /dev/null
@@ -1,46 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profile view correctly displays its panels and emits
- * the appropriate events.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, ProfileView } = panel.panelWin;
-
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "The empty notice should initially be displayed in the profile view.");
-
-  let recordingNoticeDisplayed = panel.panelWin.once(EVENTS.RECORDING_NOTICE_SHOWN);
-  ProfileView.showRecordingNotice();
-  yield recordingNoticeDisplayed;
-
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "The recording notice should now be displayed in the profile view.");
-
-  let loadingNoticeDisplayed = panel.panelWin.once(EVENTS.LOADING_NOTICE_SHOWN);
-  ProfileView.showLoadingNotice();
-  yield loadingNoticeDisplayed;
-
-  is($("#profile-pane").selectedPanel, $("#loading-notice"),
-    "The loading notice should now be displayed in the profile view.");
-
-  let tabbedBrowserDisplayed = panel.panelWin.once(EVENTS.TABBED_BROWSER_SHOWN);
-  ProfileView.showTabbedBrowser();
-  yield tabbedBrowserDisplayed;
-
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should now be displayed in the profile view.");
-
-  let emptyNoticeDisplayed = panel.panelWin.once(EVENTS.EMPTY_NOTICE_SHOWN);
-  ProfileView.showEmptyNotice();
-  yield emptyNoticeDisplayed;
-
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "The empty notice should now be redisplayed in the profile view.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recording-cancelled.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler properly handles the built-in profiler module
- * suddenly stopping.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, gFront, EVENTS, RecordingsListView } = panel.panelWin;
-
-  yield consoleProfile(gFront, "test");
-  yield startRecording(panel);
-
-  is(gFront.pendingConsoleRecordings.length, 1,
-    "There should be one pending console recording.");
-  is(gFront.finishedConsoleRecordings.length, 0,
-    "There should be no finished console recordings.");
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  let whenFrontendNotified = gFront.once("profiler-unexpectedly-stopped");
-  let whenRecordingLost = panel.panelWin.once(EVENTS.RECORDING_LOST);
-  nsIProfilerModule.StopProfiler();
-
-  yield whenFrontendNotified;
-  ok(true, "The frontend was notified about the profiler being stopped.");
-
-  yield whenRecordingLost;
-  ok(true, "The frontend reacted to the profiler being stopped.");
-
-  is(gFront.pendingConsoleRecordings.length, 0,
-    "There should be no pending console recordings.");
-  is(gFront.finishedConsoleRecordings.length, 0,
-    "There should still be no finished console recordings.");
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recording-selected-01.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler correctly handles multiple recordings and can
- * successfully switch between them.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is(RecordingsListView.selectedIndex, 1,
-    "The second recording item should be selected.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  let recordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  RecordingsListView.selectedIndex = 0;
-  yield recordingDisplayed;
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording item should be selected.");
-
-  is(ProfileView.tabCount, 1,
-    "There should still be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-
-  let emptyNoticeDisplayed = panel.panelWin.once(EVENTS.EMPTY_NOTICE_SHOWN);
-  RecordingsListView.selectedIndex = -1;
-  yield emptyNoticeDisplayed;
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.selectedItem, null,
-    "No recording item should be selected.");
-
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "The empty notice should still be displayed in the profile view.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recording-selected-02.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler correctly handles multiple recordings and can
- * successfully switch between them, even when one of them is in progress.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  yield startRecording(panel);
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording item should be selected.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-
-  let recordingNoticeDisplayed = panel.panelWin.once(EVENTS.RECORDING_NOTICE_SHOWN);
-  RecordingsListView.selectedIndex = 1;
-  yield recordingNoticeDisplayed;
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.selectedIndex, 1,
-    "The second recording item should be selected now.");
-
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "The recording notice should be displayed in the profile view.");
-
-  let recordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  RecordingsListView.selectedIndex = 0;
-  yield recordingDisplayed;
-
-  is(RecordingsListView.itemCount, 2,
-    "There should still be two recordings visible.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording item should be selected again.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible again.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view again.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recording-selected-03.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler UI does not forget that recording is active when
- * selected recording changes. Bug 1060885.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  yield startRecording(panel);
-
-  info("Selecting recording #0 and waiting for it to be displayed.");
-  let selected = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  RecordingsListView.selectedIndex = 0;
-  yield selected;
-
-  ok($("#record-button").hasAttribute("checked"),
-    "Button is still checked after selecting another item.");
-
-  ok(!$("#record-button").hasAttribute("locked"),
-    "Button is not locked after selecting another item.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recording-utils.js
+++ /dev/null
@@ -1,68 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the recording utility functions work as expected.
- */
-
-const WAIT_TIME = 100; // ms
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { RecordingsListView, RecordingUtils } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield idleWait(WAIT_TIME); // allow refresh driver ticks to accumulate
-  busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  let recordingData = RecordingsListView.selectedItem.attachment;
-  ok(recordingData, "The recording data was successfully retrieved.");
-
-  // Test plotting categories...
-
-  let profilerData = recordingData.profilerData;
-  let categoriesData = RecordingUtils.plotCategoriesFor(profilerData, 0, Infinity);
-
-  for (let category of categoriesData) {
-    is(Object.keys(category).toSource(), '["delta", "values"]',
-      "The correct keys are in the cateogries data.");
-    is(typeof category.delta, "number",
-      "The delta property is a number, as expected.");
-    is(typeof category.values, "object",
-      "The values property is a object, as expected.");
-    ok("length" in category.values,
-      "The values property is an array, as expected.");
-  }
-
-  // Test plotting framerate...
-
-  let ticksData = recordingData.ticksData;
-  let framerateData = RecordingUtils.plotFramerateFor(ticksData, 0, Infinity);
-
-  ok("length" in framerateData,
-    "The framerate data is an array, as expected.");
-
-  for (let rate of framerateData) {
-    is(typeof rate.delta, "number",
-      "The delta property is a number, as expected.");
-    is(typeof rate.value, "number",
-      "The value property is a number, as expected.");
-  }
-
-  // Test categories and framerate syncing...
-
-  RecordingUtils.syncCategoriesWithFramerate(categoriesData, framerateData);
-  info("Total framerate data: " + framerateData.length);
-  info("Total categories data: " + categoriesData.length);
-
-  if (framerateData.length >= 2 && categoriesData.length >= 2) {
-    is(categoriesData[0].delta, framerateData[0].delta,
-      "The categories and framerate data now start at the exact same time.");
-    is(categoriesData.pop().delta, framerateData.pop().delta,
-      "The categories and framerate data now end at the exact same time.");
-  }
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recordings-clear.js
+++ /dev/null
@@ -1,50 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is able clear all recordings.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, gFront, EVENTS, RecordingsListView, ProfileView } = panel.panelWin;
-
-  // Start and finish a manual recording.
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  // Start and finish a console recording.
-  yield consoleProfile(gFront, "test 1");
-  yield consoleProfileEnd(gFront, { waitForDisplayOn: panel });
-
-  // Start a new manual and console recording, but keep them in progress.
-  yield startRecording(panel);
-  yield consoleProfile(gFront, "test 2");
-
-  is(RecordingsListView.itemCount, 4,
-    "There should be four recordings visible now.");
-  is(RecordingsListView.selectedIndex, 1,
-    "The second recording should be selected.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  let whenRecordingLost = panel.panelWin.once(EVENTS.RECORDING_LOST);
-  EventUtils.synthesizeMouseAtCenter($("#clear-button"), {}, panel.panelWin);
-  yield whenRecordingLost;
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(front, { waitForDisplayOn: panel }) {
-  let notified = front.once("profileEnd");
-  let displayed = panel.panelWin.once(panel.panelWin.EVENTS.RECORDING_DISPLAYED);
-  console.profileEnd();
-  yield promise.all([notified, displayed]);
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recordings-io-01.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is able to save and load recordings.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { RecordingsListView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  // Verify original recording.
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible now.");
-
-  let originalRecordingItem = RecordingsListView.getItemAtIndex(0);
-  ok(originalRecordingItem,
-    "A recording item was available for import.");
-
-  let originalData = originalRecordingItem.attachment;
-  ok(originalData,
-    "The original item has recording data attached to it.");
-
-  // Save recording.
-
-  let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
-  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
-
-  yield panel.panelWin.saveRecordingToFile(originalRecordingItem, file);
-  ok(true, "The recording data appears to have been successfully saved.");
-
-  // Import recording.
-
-  yield panel.panelWin.loadRecordingFromFile(file);
-  ok(true, "The recording data appears to have been successfully imported.");
-
-  // Verify imported recording.
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible now.");
-
-  let importedRecordingItem = RecordingsListView.getItemAtIndex(1);
-  ok(importedRecordingItem,
-    "A recording item was imported.");
-
-  let importedData = importedRecordingItem.attachment;
-  ok(importedData,
-    "The imported item has recording data attached to it.");
-
-  ok(("fileType" in originalData) && ("fileType" in importedData),
-    "The serialization process attached an identifier to the recording data.");
-  ok(("version" in originalData) && ("version" in importedData),
-    "The serialization process attached a version to the recording data.");
-
-  is(importedData.toSource(), originalData.toSource(),
-    "The impored data is identical to the original data.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recordings-io-02.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler gracefully handles loading bogus files.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { RecordingsListView } = panel.panelWin;
-
-  let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
-  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
-
-  try {
-    yield panel.panelWin.loadRecordingFromFile(file);
-    ok(false, "The recording succeeded unexpectedly.");
-  } catch (e) {
-    is(e.message, "Could not read recording data file.");
-    ok(true, "The recording was cancelled.");
-  }
-
-  is(RecordingsListView.itemCount, 0,
-    "There should still be no recordings visible.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_recordings-io-03.js
+++ /dev/null
@@ -1,57 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler gracefully handles loading files that are JSON,
- * but don't contain the appropriate recording data.
- */
-
-let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
-let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { RecordingsListView } = panel.panelWin;
-
-  let file = FileUtils.getFile("TmpD", ["tmpprofile.json"]);
-  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
-  yield asyncCopy({ bogus: "data" }, file);
-
-  try {
-    yield panel.panelWin.loadRecordingFromFile(file);
-    ok(false, "The recording succeeded unexpectedly.");
-  } catch (e) {
-    is(e.message, "Unrecognized recording data file.");
-    ok(true, "The recording was cancelled.");
-  }
-
-  is(RecordingsListView.itemCount, 0,
-    "There should still be no recordings visible.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function getUnicodeConverter() {
-  let className = "@mozilla.org/intl/scriptableunicodeconverter";
-  let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
-  converter.charset = "UTF-8";
-  return converter;
-}
-
-function asyncCopy(data, file) {
-  let deferred = promise.defer();
-
-  let string = JSON.stringify(data);
-  let inputStream = getUnicodeConverter().convertToInputStream(string);
-  let outputStream = FileUtils.openSafeFileOutputStream(file);
-
-  NetUtil.asyncCopy(inputStream, outputStream, status => {
-    if (!Components.isSuccessCode(status)) {
-      deferred.reject(new Error("Could not save data to file."));
-    }
-    deferred.resolve();
-  });
-
-  return deferred.promise;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-connection-01.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if there's only one shared profiler connection instance per toolbox.
- */
-
-let gProfilerConnections = 0;
-Services.obs.addObserver(profilerConnectionObserver, "profiler-connection-created", false);
-
-let test = Task.async(function*() {
-  let firstTab = yield addTab(SIMPLE_URL);
-  let firstTarget = TargetFactory.forTab(firstTab);
-  yield firstTarget.makeRemote();
-
-  let toolboxFirstTab;
-  yield gDevTools.showToolbox(firstTarget, "webconsole").then((aToolbox) => {
-    toolboxFirstTab = aToolbox;
-  });
-
-  is(gProfilerConnections, 1,
-    "A shared profiler connection should have been created.");
-
-  yield gDevTools.showToolbox(firstTarget, "jsprofiler");
-  is(gProfilerConnections, 1,
-    "No new profiler connections should have been created.");
-
-  let secondTab = yield addTab(SIMPLE_URL);
-  let secondTarget = TargetFactory.forTab(secondTab);
-  yield secondTarget.makeRemote();
-
-  let toolboxSecondTab;
-  yield gDevTools.showToolbox(secondTarget, "jsprofiler").then((aToolbox) => {
-    toolboxSecondTab = aToolbox;
-  });
-
-  is(gProfilerConnections, 2,
-    "Only one new profiler connection should have been created.");
-
-  yield toolboxFirstTab.destroy().then(() => {
-    removeTab(firstTab);
-  });
-  yield toolboxSecondTab.destroy().then(() => {
-    removeTab(secondTab);
-    finish();
-  });
-});
-
-function profilerConnectionObserver(subject, topic, data) {
-  is(topic, "profiler-connection-created", "The correct topic was observed.");
-  gProfilerConnections++;
-}
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(profilerConnectionObserver, "profiler-connection-created");
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-connection-02.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the shared profiler connection is only opened once.
- */
-
-let gProfilerConnectionsOpened = 0;
-Services.obs.addObserver(profilerConnectionObserver, "profiler-connection-opened", false);
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-
-  is(gProfilerConnectionsOpened, 1,
-    "Only one profiler connection was opened.");
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(panel._toolbox);
-
-  ok(sharedProfilerConnection,
-    "A shared profiler connection for the current toolbox was retrieved.");
-  is(sharedProfilerConnection._request, panel.panelWin.gFront._request,
-    "The same shared profiler connection is used by the panel's front.");
-
-  ok(sharedProfilerConnection._target,
-    "A target exists for the current profiler connection.");
-  ok(sharedProfilerConnection._client,
-    "A target exists for the current profiler connection.");
-  is(sharedProfilerConnection._pendingConsoleRecordings.length, 0,
-    "There shouldn't be any pending console recordings yet.");
-  is(sharedProfilerConnection._finishedConsoleRecordings.length, 0,
-    "There shouldn't be any finished console recordings yet.");
-
-  yield sharedProfilerConnection.open();
-  is(gProfilerConnectionsOpened, 1,
-    "No additional profiler connections were opened.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function profilerConnectionObserver(subject, topic, data) {
-  is(topic, "profiler-connection-opened", "The correct topic was observed.");
-  gProfilerConnectionsOpened++;
-}
-
-registerCleanupFunction(() => {
-  Services.obs.removeObserver(profilerConnectionObserver, "profiler-connection-opened");
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-connection-03.js
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the shared profiler connection can properly send requests.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should not have been automatically started.");
-
-  let result = yield front._request("profiler", "startProfiler");
-  is(result.started, true,
-    "The request finished successfully and the profiler should've been started.");
-  ok(nsIProfilerModule.IsActive(),
-    "The built-in profiler module should now be active.");
-
-  result = yield front._request("profiler", "stopProfiler");
-  is(result.started, false,
-    "The request finished successfully and the profiler should've been stopped.");
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should now be inactive.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-connection-04.js
+++ /dev/null
@@ -1,200 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the shared profiler connection's console notifications are
- * handled as expected.
- */
-
-let test = Task.async(function*() {
-  // This test seems to be a bit slow on debug builds.
-  requestLongerTimeout(3);
-
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(panel._toolbox);
-
-  let pendingRecordings = sharedProfilerConnection._pendingConsoleRecordings;
-  let finishedRecordings = sharedProfilerConnection._finishedConsoleRecordings;
-  let stackSize = 0;
-  sharedProfilerConnection.on("profile", () => stackSize++);
-  sharedProfilerConnection.on("profileEnd", () => stackSize--);
-
-  ok(!(yield sharedProfilerConnection._request("profiler", "isActive")).isActive,
-    "The profiler should not be active yet.");
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should not be recording yet.");
-
-  // Start calling `console.profile()`...
-
-  let pushedLabels = [];
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection)));
-
-  // Quickly check if the profiler and framerate actor have started recording.
-
-  ok((yield sharedProfilerConnection._request("profiler", "isActive")).isActive,
-    "The profiler should have been started.");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should have started recording.");
-
-  // Continue calling `console.profile()` with different arguments...
-
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, undefined)));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, null)));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, 0)));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, "")));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, [])));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, [1, 2, 3])));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, [4, 5, 6])));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, "hello world")));
-  pushedLabels.push((yield consoleProfile(sharedProfilerConnection, true, "hello world")));
-
-  // Verify the actors' and stack state.
-
-  ok((yield sharedProfilerConnection._request("profiler", "isActive")).isActive,
-    "The profiler should still be active.");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be active.");
-
-  is(pushedLabels.length, 10,
-    "There should be 10 calls made to `console.profile()`.");
-  is(pushedLabels[0], undefined,
-    "The first `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[1], "null",
-    "The second `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[2], "null",
-    "The third `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[3], "0",
-    "The fourth `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[4], "",
-    "The fifth `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[5], "",
-    "The sixth `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[6], "1,2,3",
-    "The seventh `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[7], "4,5,6",
-    "The eigth `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[8], "hello world",
-    "The ninth `console.profile()` call had the correct coerced argument.");
-  is(pushedLabels[9], "hello world",
-    "The tenth `console.profile()` call had the correct coerced argument.");
-
-  is(stackSize, 7,
-    "There should be 7 pending profiles on the stack.");
-  is(pendingRecordings.length, 7,
-    "The internal pending console recordings count is correct.");
-  is(finishedRecordings.length, 0,
-    "The internal finished console recordings count is correct.");
-
-  testPendingRecording(pendingRecordings, 0, pushedLabels[0]);
-  testPendingRecording(pendingRecordings, 1, pushedLabels[1]);
-  testPendingRecording(pendingRecordings, 2, pushedLabels[3]);
-  testPendingRecording(pendingRecordings, 3, pushedLabels[4]);
-  testPendingRecording(pendingRecordings, 4, pushedLabels[6]);
-  testPendingRecording(pendingRecordings, 5, pushedLabels[7]);
-  testPendingRecording(pendingRecordings, 6, pushedLabels[8]);
-
-  // Start calling `console.profileEnd()`...
-
-  let poppedLabels = [];
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection)));
-
-  // Quickly check if the profiler and framerate actor are still recording.
-
-  ok((yield sharedProfilerConnection._request("profiler", "isActive")).isActive,
-    "The profiler should still be active after one recording stopped.");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be active after one recording stopped.");
-
-  // Continue calling `console.profileEnd()` with different arguments...
-
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, null)));
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, 0)));
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, "")));
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, [1, 2, 3])));
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, [4, 5, 6])));
-  poppedLabels.push((yield consoleProfileEnd(sharedProfilerConnection, true, "hello world")));
-
-  // Verify the actors' and stack state.
-
-  ok((yield sharedProfilerConnection._request("profiler", "isActive")).isActive,
-    "The profiler should still be active after all recordings stopped.");
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should not be active after all recordings stopped.");
-
-  is(poppedLabels.length, 7,
-    "There should be 7 calls made to `console.profileEnd()`.");
-  is(poppedLabels[0], undefined,
-    "The first `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[1], "null",
-    "The second `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[2], "0",
-    "The third `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[3], "",
-    "The fourth `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[4], "1,2,3",
-    "The fifth `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[5], "4,5,6",
-    "The sixth `console.profileEnd()` call had the correct coerced argument.");
-  is(poppedLabels[6], "hello world",
-    "The seventh `console.profileEnd()` call had the correct coerced argument.");
-
-  is(stackSize, 0,
-    "There should be 0 pending profiles on the stack.");
-  is(pendingRecordings.length, 0,
-    "The internal pending console recordings count is correct.");
-  is(finishedRecordings.length, 7,
-    "The internal finished console recordings count is correct.");
-
-  testFinishedRecording(finishedRecordings, 0, poppedLabels[0]);
-  testFinishedRecording(finishedRecordings, 1, poppedLabels[1]);
-  testFinishedRecording(finishedRecordings, 2, poppedLabels[2]);
-  testFinishedRecording(finishedRecordings, 3, poppedLabels[3]);
-  testFinishedRecording(finishedRecordings, 4, poppedLabels[4]);
-  testFinishedRecording(finishedRecordings, 5, poppedLabels[5]);
-  testFinishedRecording(finishedRecordings, 6, poppedLabels[6]);
-
-  // We're done here.
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(connection, withLabel, labelValue) {
-  let notified = connection.once("invoked-console-profile");
-  if (!withLabel) {
-    console.profile();
-  } else {
-    console.profile(labelValue);
-  }
-  return (yield notified);
-}
-
-function* consoleProfileEnd(connection, withLabel, labelValue) {
-  let notified = connection.once("invoked-console-profileEnd");
-  if (!withLabel) {
-    console.profileEnd();
-  } else {
-    console.profileEnd(labelValue);
-  }
-  return (yield notified);
-}
-
-function testPendingRecording(pendingRecordings, index, label) {
-  is(pendingRecordings[index].profileLabel, label,
-    "The pending profile at index " + index + " on the stack has the correct label.");
-  ok(pendingRecordings[index].profilingStartTime >= 0,
-    "...and has a positive start time.");
-}
-
-function testFinishedRecording(finishedRecordings, index, label) {
-  is(finishedRecordings[index].profilerData.profileLabel, label,
-    "The finished profile at index " + index + " has the correct label.");
-  ok(finishedRecordings[index].recordingDuration > 0,
-    "...and has a strictly positive recording duration.");
-  ok("samples" in finishedRecordings[index].profilerData.profile.threads[0],
-    "...with profiler data samples attached.");
-  ok("ticksData" in finishedRecordings[index],
-    "...and with refresh driver ticks data attached.");
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-01.js
+++ /dev/null
@@ -1,53 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler connection front relays console notifications.
- */
-
-let test = Task.async(function*() {
-  // This test seems to be a bit slow on debug builds.
-  requestLongerTimeout(3);
-
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(panel._toolbox);
-
-  let stackSize = 0;
-  front.on("profile", () => stackSize++);
-  front.on("profileEnd", () => stackSize--);
-
-  for (let i = 0; i < 10; i++) {
-    yield consoleProfile(sharedProfilerConnection, i);
-    is(stackSize, i + 1,
-      "The current stack size is correctly: " + (i + 1));
-    is(front.pendingConsoleRecordings.length, i + 1,
-      "The publicly exposed pending recordings array has the correct size.");
-  }
-  for (let i = 9; i >= 0; i--) {
-    yield consoleProfileEnd(sharedProfilerConnection);
-    is(stackSize, i,
-      "The current stack size is correctly: " + i);
-    is(front.pendingConsoleRecordings.length, i,
-      "The publicly exposed pending recordings array has the correct size.");
-    is(front.finishedConsoleRecordings.length, 10 - i,
-      "The publicly exposed finished recordings array has the correct size.");
-  }
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(connection) {
-  let notified = connection.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-02.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler connection front does not activate the built-in
- * profiler module if not necessary, and doesn't deactivate it when
- * a recording is stopped.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should not have been automatically started.");
-
-  let activated = front.once("profiler-activated");
-  yield front.startRecording();
-  yield activated;
-  yield front.stopRecording();
-  ok(nsIProfilerModule.IsActive(),
-    "The built-in profiler module should still be active (1).");
-
-  let alreadyActive = front.once("profiler-already-active");
-  yield front.startRecording();
-  yield alreadyActive;
-  yield front.stopRecording();
-  ok(nsIProfilerModule.IsActive(),
-    "The built-in profiler module should still be active (2).");
-
-  yield teardown(panel);
-
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should have been automatically stoped.");
-
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-03.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the built-in profiler module doesn't deactivate when the toolbox
- * is destroyed if there are other consumers using it.
- */
-
-let test = Task.async(function*() {
-  let [,, firstPanel] = yield initFrontend(SIMPLE_URL);
-  let firstFront = firstPanel.panelWin.gFront;
-
-  let activated = firstFront.once("profiler-activated");
-  yield firstFront.startRecording();
-  yield activated;
-
-  let [,, secondPanel] = yield initFrontend(SIMPLE_URL);
-  let secondFront = secondPanel.panelWin.gFront;
-
-  let alreadyActive = secondFront.once("profiler-already-active");
-  yield secondFront.startRecording();
-  yield alreadyActive;
-
-  yield teardown(firstPanel);
-  ok(nsIProfilerModule.IsActive(),
-    "The built-in profiler module should still be active.");
-
-  yield teardown(secondPanel);
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should have been automatically stoped.");
-
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-04.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the built-in profiler module is not reactivated if no other
- * consumer was using it over the remote debugger protocol, and ensures
- * that the actor will work properly even in such cases (e.g. the Gecko Profiler
- * addon was installed and automatically activated the profiler module).
- */
-
-let test = Task.async(function*() {
-  // Ensure the profiler is already running when the test starts.
-  let ENTRIES = 1000000;
-  let INTERVAL = 1;
-  let FEATURES = ["js"];
-  nsIProfilerModule.StartProfiler(ENTRIES, INTERVAL, FEATURES, FEATURES.length);
-
-  let [,, firstPanel] = yield initFrontend(SIMPLE_URL);
-  let firstFront = firstPanel.panelWin.gFront;
-
-  let alredyActive = firstFront.once("profiler-already-active");
-  yield firstFront.startRecording();
-  yield alredyActive;
-  ok(firstFront._profilingStartTime > 0, "The profiler was not restarted.");
-
-  let [,, secondPanel] = yield initFrontend(SIMPLE_URL);
-  let secondFront = secondPanel.panelWin.gFront;
-
-  let alreadyActive = secondFront.once("profiler-already-active");
-  yield secondFront.startRecording();
-  yield alreadyActive;
-  ok(secondFront._profilingStartTime > 0, "The profiler was not restarted.");
-
-  yield teardown(firstPanel);
-  ok(nsIProfilerModule.IsActive(),
-    "The built-in profiler module should still be active.");
-
-  yield teardown(secondPanel);
-  ok(!nsIProfilerModule.IsActive(),
-    "The built-in profiler module should have been automatically stoped.");
-
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-05.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the framerate front is kept recording only when required.
- */
-
-let test = Task.async(function*() {
-  let [,, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(panel._toolbox);
-
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should not be recording yet.");
-
-  yield front.startRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should now be recording.");
-
-  yield consoleProfile(sharedProfilerConnection, "1");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (1).");
-
-  yield front.startRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (2).");
-
-  yield consoleProfile(sharedProfilerConnection, "2");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (3).");
-
-  yield front.stopRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (4).");
-
-  yield consoleProfileEnd(sharedProfilerConnection);
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (5).");
-
-  yield front.stopRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (6).");
-
-  yield consoleProfileEnd(sharedProfilerConnection);
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should finally have stopped recording.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(connection) {
-  let notified = connection.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_shared-front-06.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the framerate front is kept recording only when required,
- * this time doing everything in reverse.
- */
-
-let test = Task.async(function*() {
-  let [,, panel] = yield initFrontend(SIMPLE_URL);
-  let front = panel.panelWin.gFront;
-
-  let SharedProfilerUtils = devtools.require("devtools/profiler/shared");
-  let sharedProfilerConnection = SharedProfilerUtils.getProfilerConnection(panel._toolbox);
-
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should not be recording yet.");
-
-  yield consoleProfile(sharedProfilerConnection, "1");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should now be recording.");
-
-  yield front.startRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (1).");
-
-  yield consoleProfile(sharedProfilerConnection, "2");
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (2).");
-
-  yield front.startRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (3).");
-
-  yield consoleProfileEnd(sharedProfilerConnection);
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (4).");
-
-  yield front.stopRecording();
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (5).");
-
-  yield consoleProfileEnd(sharedProfilerConnection);
-  ok((yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should still be recording (6).");
-
-  yield front.stopRecording();
-  ok(!(yield sharedProfilerConnection._framerate.isRecording()),
-    "The framerate actor should finally have stopped recording.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(connection, label) {
-  let notified = connection.once("profile");
-  console.profile(label);
-  yield notified;
-}
-
-function* consoleProfileEnd(connection) {
-  let notified = connection.once("profileEnd");
-  console.profileEnd();
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_simple-record-01.js
+++ /dev/null
@@ -1,51 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is able to start a simple recording.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, L10N, RecordingsListView, ProfileView } = panel.panelWin;
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible yet.");
-  ok($(".side-menu-widget-empty-text"),
-    "There should be some empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 0,
-    "There shouldn't be any tabs visible yet.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view.");
-
-  yield startRecording(panel);
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible now.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 0,
-    "There still shouldn't be any tabs visible yet.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  let recordingItem = RecordingsListView.selectedItem;
-  is(recordingItem, RecordingsListView.items[0],
-    "The first and only recording item should be automatically selected.");
-
-  is($(".recording-item-title", recordingItem.target).getAttribute("value"),
-    L10N.getFormatStr("recordingsList.itemLabel", 1),
-    "The recording item's title is correct.");
-
-  is($(".recording-item-duration", recordingItem.target).getAttribute("value"),
-    L10N.getStr("recordingsList.recordingLabel"),
-    "The recording item's duration is correct.");
-
-  is($(".recording-item-save", recordingItem.target).getAttribute("value"), "",
-    "The recording item's save link should be invisible.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_simple-record-02.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler is able to end a simple recording.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, L10N, RecordingsListView, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible.");
-  ok(!$(".side-menu-widget-empty-text"),
-    "There shouldn't be any empty text displayed in the recordings list.");
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-
-  let recordingItem = RecordingsListView.selectedItem;
-  is(recordingItem, RecordingsListView.items[0],
-    "The first and only recording item should be automatically selected.");
-
-  ok(recordingItem.attachment.recordingDuration > 0,
-    "The recording duration appears to be correct.");
-  is(recordingItem.attachment.profileLabel, undefined,
-    "The profile label should be undefined for non-console recordings.");
-  ok(recordingItem.attachment.profilerData,
-    "The profiler data appears to be correct.");
-  ok(recordingItem.attachment.ticksData,
-    "The ticks data appears to be correct.");
-
-  is($(".recording-item-title", recordingItem.target).getAttribute("value"),
-    L10N.getFormatStr("recordingsList.itemLabel", 1),
-    "The recording item's title is correct.");
-
-  ok($(".recording-item-duration", recordingItem.target).getAttribute("value")
-    .match(/\d+ ms/),
-    "The recording item's duration is correct.");
-
-  is($(".recording-item-save", recordingItem.target).getAttribute("value"),
-    L10N.getStr("recordingsList.saveLabel"),
-    "The recording item's save link is correct.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_simple-record-03.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler goes through the appropriate steps while displaying
- * a finished recording.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { EVENTS } = panel.panelWin;
-
-  yield startRecording(panel);
-
-  let loadingNoticeDisplayed = panel.panelWin.once(EVENTS.LOADING_NOTICE_SHOWN);
-  let recordingDisplayed = panel.panelWin.once(EVENTS.RECORDING_DISPLAYED);
-  yield stopRecording(panel, { waitForDisplay: false });
-
-  yield loadingNoticeDisplayed;
-  ok(true, "The loading noticed was briefly displayed.");
-
-  yield recordingDisplayed;
-  ok(true, "The recording was finally displayed.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_sudden-deactivation-01.js
+++ /dev/null
@@ -1,43 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler removes all pending recordings in case of a
- * sudden deactivation.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, RecordingsListView } = panel.panelWin;
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible yet.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view.");
-
-  yield startRecording(panel);
-
-  is(RecordingsListView.itemCount, 1,
-    "There should be one recording visible now.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  let whenLost = panel.panelWin.once(EVENTS.RECORDING_LOST);
-
-  // Forcibly stop the built-in profiler module.
-  nsIProfilerModule.StopProfiler();
-
-  yield whenLost;
-  ok(true, "Recording was cancelled in the frontend.");
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible now.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view again.");
-
-  ok(!$("#record-button").hasAttribute("checked"),
-    "The record button was unchecked.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_sudden-deactivation-02.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler removes all pending recordings in case of a
- * sudden deactivation, even the ones started via `console.profile`.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, EVENTS, gFront, RecordingsListView } = panel.panelWin;
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible yet.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view.");
-
-  yield startRecording(panel);
-  yield consoleProfile(gFront, "test");
-
-  is(RecordingsListView.itemCount, 2,
-    "There should be two recordings visible now.");
-  is(RecordingsListView.selectedIndex, 0,
-    "The first recording should be selected in the list.");
-  is($("#profile-pane").selectedPanel, $("#recording-notice"),
-    "There should be a recording notice displayed in the profile view.");
-
-  let whenLost = panel.panelWin.once(EVENTS.RECORDING_LOST);
-
-  // Forcibly stop the built-in profiler module.
-  nsIProfilerModule.StopProfiler();
-
-  yield whenLost;
-  ok(true, "Recording was cancelled in the frontend.");
-
-  is(RecordingsListView.itemCount, 0,
-    "There should be no recordings visible now.");
-  is(RecordingsListView.selectedIndex, -1,
-    "There shouldn't be any recording selected in the list.");
-  is($("#profile-pane").selectedPanel, $("#empty-notice"),
-    "There should be an empty notice displayed in the profile view again.");
-
-  ok(!$("#record-button").hasAttribute("checked"),
-    "The record button was unchecked.");
-
-  yield teardown(panel);
-  finish();
-});
-
-function* consoleProfile(front, label) {
-  let notified = front.once("profile");
-  console.profile(label);
-  yield notified;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_tabbed-browser-01.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler displays and organizes the recording data in tabs.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  is(ProfileView.tabCount, 1,
-    "There should be one tab visible.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should be displayed in the profile view.");
-  is($("#profile-content").selectedIndex, 0,
-    "The first tab is selected.");
-
-  ok($("#profile-content .tab-title-label").getAttribute("value")
-    .match(/\d+ ms . \d+ ms/),
-    "The recording's first tab title is correct.");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_tabbed-browser-02.js
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler displays and organizes the recording data in tabs,
- * spawning a new tab from frame nodes when required.
- */
-
-const WAIT_TIME = 1000; // ms
-
-let gPrevShowPlatformData =
-  Services.prefs.getBoolPref("devtools.profiler.ui.show-platform-data");
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, $$, EVENTS, Prefs, ProfileView } = panel.panelWin;
-
-  // Make sure platform data is displayed, so there's samples available.
-  Prefs.showPlatformData = true;
-
-  yield startRecording(panel);
-  busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  let treeRoot = ProfileView._getCallTreeRoot();
-  ok(treeRoot,
-    "There's a call tree view available on the profile view.");
-  let callItem = treeRoot.getChild(0);
-  ok(callItem,
-    "The first displayed item in the tree was retreived.");
-  let callNode = callItem.target;
-  ok(callNode,
-    "The first displayed item in the tree has a corresponding DOM node.");
-
-  is($(".call-tree-name", callNode).getAttribute("value"), "Startup::XRE_Main",
-    "The first displayed item in the tree has the expected function name.");
-  is($(".call-tree-cell[type=percentage]", callNode).getAttribute("value"), "100%",
-    "The first displayed item in the tree has the expected percentage usage.");
-
-  let tabSpawned = panel.panelWin.once(EVENTS.TAB_SPAWNED_FROM_FRAME_NODE);
-  EventUtils.sendMouseEvent({ type: "mousedown" }, callNode.querySelector(".call-tree-zoom"));
-  yield tabSpawned;
-
-  is(ProfileView.tabCount, 2,
-    "There should be two tabs visible now.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-  is($("#profile-content").selectedIndex, 1,
-    "The second tab is now selected.");
-
-  let firstTabTitle = $$("#profile-content .tab-title-label")[0].getAttribute("value");
-  let secondTabTitle = $$("#profile-content .tab-title-label")[1].getAttribute("value");
-  info("The first tab's title is: " + firstTabTitle);
-  info("The second tab's title is: " + secondTabTitle);
-
-  isnot(firstTabTitle, secondTabTitle,
-    "The first and second tab titles are different.");
-  ok(firstTabTitle.match(/\d+ ms . \d+ ms/),
-    "The recording's first tab title is correct.");
-  ok(secondTabTitle.match(/[\d\.,]+ ms . [\d\.,]+ ms/),
-    "The recording's second tab title is correct.");
-
-  let newTreeRoot = ProfileView._getCallTreeRoot();
-  ok(newTreeRoot,
-    "There's a call tree view available on the profile view again.");
-  let newCallItem = newTreeRoot.getChild(0);
-  ok(newCallItem,
-    "The first displayed item in the tree was retreived again.");
-  let newCallNode = newCallItem.target;
-  ok(newCallNode,
-    "The first displayed item in the tree has a corresponding DOM node again.");
-
-  isnot(treeRoot, newTreeRoot,
-    "The new call tree view has a different root this time.");
-  isnot(callNode, newCallNode,
-    "The new call tree view has a different corresponding DOM node this time.");
-
-  is($(".call-tree-name", newTreeRoot.target).getAttribute("value"), "Startup::XRE_Main",
-    "The first displayed item in the tree has the expected function name.");
-  is($(".call-tree-cell[type=percentage]", newTreeRoot.target).getAttribute("value"), "100%",
-    "The first displayed item in the tree has the expected percentage usage.");
-
-  yield teardown(panel);
-  finish();
-});
-
-registerCleanupFunction(() => {
-  Services.prefs.setBoolPref(
-    "devtools.profiler.ui.show-platform-data", gPrevShowPlatformData);
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_tabbed-browser-03.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if the profiler displays and organizes the recording data in tabs,
- * spawning a new tab from a graph's selection when required.
- */
-
-const WAIT_TIME = 1000; // ms
-
-let gPrevShowPlatformData =
-  Services.prefs.getBoolPref("devtools.profiler.ui.show-platform-data");
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, $$, EVENTS, Prefs, ProfileView } = panel.panelWin;
-
-  // Make sure platform data is displayed, so there's samples available.
-  Prefs.showPlatformData = true;
-
-  yield startRecording(panel);
-  busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  let categoriesGraph = ProfileView._getCategoriesGraph();
-  ok(categoriesGraph,
-    "There's a categories graph available on the profile view.");
-
-  ok(!categoriesGraph.hasSelection(),
-    "The categories graph shouldn't have a selection available yet.");
-  ok($("#profile-newtab-button").hidden,
-    "The new tab button on the profile view tab bar should be hidden.");
-
-  dragStart(categoriesGraph, 10);
-  dragStop(categoriesGraph, 50);
-
-  ok(categoriesGraph.hasSelection(),
-    "The categories graph should have a selection available now.");
-  ok(!$("#profile-newtab-button").hidden,
-    "The new tab button on the profile view tab bar should be visible.");
-
-  let tabSpawned = panel.panelWin.once(EVENTS.TAB_SPAWNED_FROM_SELECTION);
-  EventUtils.sendMouseEvent({ type: "click" }, $("#profile-newtab-button"));
-  yield tabSpawned;
-
-  is(ProfileView.tabCount, 2,
-    "There should be two tabs visible now.");
-  is($("#profile-pane").selectedPanel, $("#profile-content"),
-    "The profile content should still be displayed in the profile view.");
-  is($("#profile-content").selectedIndex, 1,
-    "The second tab is now selected.");
-
-  let firstTabTitle = $$("#profile-content .tab-title-label")[0].getAttribute("value");
-  let secondTabTitle = $$("#profile-content .tab-title-label")[1].getAttribute("value");
-  info("The first tab's title is: " + firstTabTitle);
-  info("The second tab's title is: " + secondTabTitle);
-
-  isnot(firstTabTitle, secondTabTitle,
-    "The first and second tab titles are different.");
-  ok(firstTabTitle.match(/\d+ ms . \d+ ms/),
-    "The recording's first tab title is correct.");
-  ok(secondTabTitle.match(/[\d\.,]+ ms . [\d\.,]+ ms/),
-    "The recording's second tab title is correct.");
-
-  yield teardown(panel);
-  finish();
-});
-
-registerCleanupFunction(() => {
-  Services.prefs.setBoolPref(
-    "devtools.profiler.ui.show-platform-data", gPrevShowPlatformData);
-});
-
-// EventUtils just doesn't work!
-
-function dragStart(graph, x, y = 1) {
-  x /= window.devicePixelRatio;
-  y /= window.devicePixelRatio;
-  graph._onMouseMove({ testX: x, testY: y });
-  graph._onMouseDown({ testX: x, testY: y });
-}
-
-function dragStop(graph, x, y = 1) {
-  x /= window.devicePixelRatio;
-  y /= window.devicePixelRatio;
-  graph._onMouseMove({ testX: x, testY: y });
-  graph._onMouseUp({ testX: x, testY: y });
-}
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_tabbed-browser-add-remove-01.js
+++ /dev/null
@@ -1,44 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if adding, naming and selecting tabs in the ProfileView works.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, $$, L10N, Prefs, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  let tabIndex = ProfileView.addTab();
-  is(tabIndex, 1,
-    "A new tab was successfully added to the tabbed browser.");
-
-  is($("#profile-content tabs").childNodes.length, 2,
-    "There should be two tabs in the tabbed browser.");
-  is($("#profile-content tabpanels").childNodes.length, 2,
-    "There should be two tabpanels in the tabbed browser.");
-
-  is($("#profile-content").selectedIndex, 0,
-    "The first tab should still be selected in the tabbed browser (1).");
-  is($("#profile-content").selectedTab, $$("#profile-content tab")[0],
-    "The first tab should still be selected in the tabbed browser (2).");
-
-  ProfileView.nameTab(tabIndex, 12.34, 56.78);
-
-  ok($$("#profile-content .tab-title-label")[1].getAttribute("value"),
-    L10N.getFormatStr("profile.tab", "12.34", "56.78"),
-    "The newly added tab's title was successfully changed.");
-
-  ProfileView.selectTab(tabIndex);
-
-  is($("#profile-content").selectedIndex, 1,
-    "The second tab is now selected in the tabbed browser (1).");
-  is($("#profile-content").selectedTab, $$("#profile-content tab")[1],
-    "The second tab is now selected in the tabbed browser (2).");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/browser_profiler_tabbed-browser-add-remove-02.js
+++ /dev/null
@@ -1,55 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Tests if removing tabs in the ProfileView works.
- */
-
-let test = Task.async(function*() {
-  let [target, debuggee, panel] = yield initFrontend(SIMPLE_URL);
-  let { $, $$, L10N, Prefs, ProfileView } = panel.panelWin;
-
-  yield startRecording(panel);
-  yield stopRecording(panel, { waitForDisplay: true });
-
-  let newTab1 = ProfileView.addTab();
-  let newTab2 = ProfileView.addTab();
-  let newTab3 = ProfileView.addTab();
-
-  is($("#profile-content tabs").childNodes.length, 4,
-    "There should be four tabs in the tabbed browser.");
-  is($("#profile-content tabpanels").childNodes.length, 4,
-    "There should be four tabpanels in the tabbed browser.");
-
-  is($("#profile-content").selectedIndex, 0,
-    "The first tab should still be selected in the tabbed browser (1).");
-  is($("#profile-content").selectedTab, $$("#profile-content tab")[0],
-    "The first tab should still be selected in the tabbed browser (2).");
-
-  ProfileView.removeTabsAfter(newTab1);
-
-  is($("#profile-content tabs").childNodes.length, 2,
-    "There should be two tabs in the tabbed browser now.");
-  is($("#profile-content tabpanels").childNodes.length, 2,
-    "There should be two tabpanels in the tabbed browser now.");
-
-  is($("#profile-content").selectedIndex, 0,
-    "The first tab should still be selected in the tabbed browser (3).");
-  is($("#profile-content").selectedTab, $$("#profile-content tab")[0],
-    "The first tab should still be selected in the tabbed browser (4).");
-
-  ProfileView.removeAllTabs();
-
-  is($("#profile-content tabs").childNodes.length, 0,
-    "There should be no tabs in the tabbed browser now.");
-  is($("#profile-content tabpanels").childNodes.length, 0,
-    "There should be no tabpanels in the tabbed browser now.");
-
-  is($("#profile-content").selectedIndex, -1,
-    "No tab should be selected in the tabbed browser (3).");
-  is($("#profile-content").selectedTab, null,
-    "No tab should be selected in the tabbed browser (4).");
-
-  yield teardown(panel);
-  finish();
-});
deleted file mode 100644
--- a/browser/devtools/profiler/test/doc_simple-test.html
+++ /dev/null
@@ -1,22 +0,0 @@
-<!-- Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/ -->
-<!doctype html>
-
-<html>
-  <head>
-    <meta charset="utf-8"/>
-    <title>Profiler test page</title>
-  </head>
-
-  <body>
-    <script type="text/javascript">
-      function test() {
-        var a = "Hello world!";
-      }
-
-      // Prevent this script from being garbage collected.
-      window.setInterval(test, 1);
-    </script>
-  </body>
-
-</html>
deleted file mode 100644
--- a/browser/devtools/profiler/test/head.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-"use strict";
-
-const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
-
-let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
-
-// Disable logging for all the tests. Both the debugger server and frontend will
-// be affected by this pref.
-let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
-Services.prefs.setBoolPref("devtools.debugger.log", false);
-
-let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
-let { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
-let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
-let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
-let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
-
-let nsIProfilerModule = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
-let TargetFactory = devtools.TargetFactory;
-let Toolbox = devtools.Toolbox;
-
-const EXAMPLE_URL = "http://example.com/browser/browser/devtools/profiler/test/";
-const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
-
-// All tests are asynchronous.
-waitForExplicitFinish();
-
-registerCleanupFunction(() => {
-  info("finish() was called, cleaning up...");
-  Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
-
-  // Make sure the profiler module is stopped when the test finishes.
-  nsIProfilerModule.StopProfiler();
-});
-
-function addTab(url) {
-  info("Adding tab: " + url);
-
-  let deferred = promise.defer();
-  let tab = gBrowser.selectedTab = gBrowser.addTab(url);
-  let linkedBrowser = tab.linkedBrowser;
-
-  linkedBrowser.addEventListener("load", function onLoad() {
-    linkedBrowser.removeEventListener("load", onLoad, true);
-    info("Tab added and finished loading: " + url);
-    deferred.resolve(tab);
-  }, true);
-
-  return deferred.promise;
-}
-
-function removeTab(tab) {
-  info("Removing tab.");
-
-  let deferred = promise.defer();
-  let tabContainer = gBrowser.tabContainer;
-
-  tabContainer.addEventListener("TabClose", function onClose(aEvent) {
-    tabContainer.removeEventListener("TabClose", onClose, false);
-    info("Tab removed and finished closing.");
-    deferred.resolve();
-  }, false);
-
-  gBrowser.removeTab(tab);
-  return deferred.promise;
-}
-
-function* initFrontend(url, id = "jsprofiler") {
-  info("Initializing a " + id + " pane.");
-
-  let tab = yield addTab(url);
-  let target = TargetFactory.forTab(tab);
-  let debuggee = tab.linkedBrowser.contentWindow.wrappedJSObject;
-
-  yield target.makeRemote();
-
-  let toolbox = yield gDevTools.showToolbox(target, id);
-  let panel = toolbox.getCurrentPanel();
-  return [target, debuggee, panel];
-}
-
-function* teardown(panel) {
-  info("Destroying the specified profiler.");
-
-  let tab = panel.target.tab;
-  yield panel._toolbox.destroy();
-  yield removeTab(tab);
-}
-
-function* waitForProfilerConnection() {
-  let profilerConnected = promise.defer();
-  let profilerConnectionObserver = () => profilerConnected.resolve();
-  Services.obs.addObserver(profilerConnectionObserver, "profiler-connection-opened", false);
-
-  yield profilerConnected.promise;
-  Services.obs.removeObserver(profilerConnectionObserver, "profiler-connection-opened");
-}
-
-function busyWait(time) {
-  let start = Date.now();
-  let stack;
-  while (Date.now() - start < time) { stack = Components.stack; }
-}
-
-function idleWait(time) {
-  return DevToolsUtils.waitForTime(time);
-}
-
-function* startRecording(panel) {
-  let win = panel.panelWin;
-  let started = win.once(win.EVENTS.RECORDING_STARTED);
-  let button = win.$("#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.");
-
-  EventUtils.synthesizeMouseAtCenter(button, {}, win);
-  yield started;
-
-  ok(button.hasAttribute("checked"),
-    "The record button should is now checked.");
-  ok(!button.hasAttribute("locked"),
-    "The record button should still not be locked.");
-}
-
-function* stopRecording(panel, { waitForDisplay }) {
-  let win = panel.panelWin;
-  let ended = win.once(win.EVENTS.RECORDING_ENDED);
-  let displayed = win.once(win.EVENTS.RECORDING_DISPLAYED);
-  let button = win.$("#record-button");
-
-  ok(button.hasAttribute("checked"),
-    "The record button should already be checked.");
-  ok(!button.hasAttribute("locked"),
-    "The record button should not be locked.");
-
-  EventUtils.synthesizeMouseAtCenter(button, {}, win);
-  yield ended;
-
-  ok(button.hasAttribute("checked"),
-    "The record button should still be checked.");
-  ok(button.hasAttribute("locked"),
-    "The record button now has a locked attribute.");
-
-  if (waitForDisplay) {
-    yield displayed;
-    if (!win.RecordingsListView.getItemForPredicate(e => e.isRecording)) {
-      ok(!button.hasAttribute("checked"),
-        "The record button should not be checked anymore.");
-    }
-    ok(!button.hasAttribute("locked"),
-      "The record button should not be locked anymore.");
-  }
-}
-
-function getSourceActor(aSources, aURL) {
-  let item = aSources.getItemForAttachment(a => a.source.url === aURL);
-  return item && item.value;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/ui-profile.js
+++ /dev/null
@@ -1,834 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-/**
- * Functions handling the profile inspection UI, showing the framerate and
- * cateogry graphs, along with a call tree view.
- *
- * A profile view is a tabbed browser, so recording data will be displayed in
- * tabs. Certain messages like 'Loading' or 'Recording...' may also be shown.
- */
-let ProfileView = {
-  /**
-   * Initialization function, called when the tool is started.
-   */
-  initialize: function() {
-    this._tabs = $("#profile-content tabs");
-    this._panels = $("#profile-content tabpanels");
-    this._tabTemplate = $("#profile-content-tab-template");
-    this._panelTemplate = $("#profile-content-tabpanel-template");
-    this._newtabButton = $("#profile-newtab-button");
-    this._invertTree = $("#invert-tree");
-
-    this._recordingInfoByPanel = new WeakMap();
-    this._framerateGraphByPanel = new Map();
-    this._categoriesGraphByPanel = new Map();
-    this._callTreeRootByPanel = new Map();
-
-    this._onTabSelect = this._onTabSelect.bind(this);
-    this._onNewTabClick = this._onNewTabClick.bind(this);
-    this._onInvertTree = this._onInvertTree.bind(this);
-    this._onGraphLegendSelection = this._onGraphLegendSelection.bind(this);
-    this._onGraphMouseUp = this._onGraphMouseUp.bind(this);
-    this._onGraphScroll = this._onGraphScroll.bind(this);
-    this._onCallViewFocus = this._onCallViewFocus.bind(this);
-    this._onCallViewLink = this._onCallViewLink.bind(this);
-    this._onCallViewZoom = this._onCallViewZoom.bind(this);
-
-    this._panels.addEventListener("select", this._onTabSelect, false);
-    this._newtabButton.addEventListener("click", this._onNewTabClick, false);
-    this._invertTree.addEventListener("command", this._onInvertTree, false);
-  },
-
-  /**
-   * Destruction function, called when the tool is closed.
-   */
-  destroy: function() {
-    this.removeAllTabs();
-
-    this._panels.removeEventListener("select", this._onTabSelect, false);
-    this._newtabButton.removeEventListener("click", this._onNewTabClick, false);
-    this._invertTree.removeEventListener("command", this._onInvertTree, false);
-  },
-
-  /**
-   * Shows a message detailing that there are is no data available.
-   * The tabbed browser will also be hidden.
-   */
-  showEmptyNotice: function() {
-    $("#profile-pane").selectedPanel = $("#empty-notice");
-    window.emit(EVENTS.EMPTY_NOTICE_SHOWN);
-  },
-
-  /**
-   * Shows a message detailing that a recording is currently in progress.
-   * The tabbed browser will also be hidden.
-   */
-  showRecordingNotice: function() {
-    $("#profile-pane").selectedPanel = $("#recording-notice");
-    window.emit(EVENTS.RECORDING_NOTICE_SHOWN);
-  },
-
-  /**
-   * Shows a message detailing that a finished recording is being loaded.
-   * The tabbed browser will also be hidden.
-   */
-  showLoadingNotice: function() {
-    $("#profile-pane").selectedPanel = $("#loading-notice");
-    window.emit(EVENTS.LOADING_NOTICE_SHOWN);
-  },
-
-  /**
-   * Shows the tabbed browser displaying recording data.
-   */
-  showTabbedBrowser: function() {
-    $("#profile-pane").selectedPanel = $("#profile-content");
-    window.emit(EVENTS.TABBED_BROWSER_SHOWN);
-  },
-
-  /**
-   * Selects the tab at the specified index in this tabbed browser.
-   *
-   * @param number tabIndex
-   *        The index of the tab to select. If no tab is available at the
-   *        specified index, all tabs will be deselected.
-   */
-  selectTab: function(tabIndex) {
-    $("#profile-content").selectedIndex = tabIndex;
-  },
-
-  /**
-   * Adds an empty tab in this tabbed browser.
-   *
-   * @return number
-   *         The newly created tab's index.
-   */
-  addTab: function() {
-    let tab = this._tabs.appendChild(this._tabTemplate.cloneNode(true));
-    let panel = this._panels.appendChild(this._panelTemplate.cloneNode(true));
-
-    // "Uncover" the tab via a CSS animation.
-    tab.removeAttribute("covered");
-
-    let tabIndex = this._tabs.itemCount - 1;
-    return tabIndex;
-  },
-
-  /**
-   * Sets the title of a tab in this tabbed browser.
-   *
-   * @param number tabIndex
-   *        The index of the tab to name.
-   * @param number beginAt, endAt
-   *        The 'start → stop' components of the tab title.
-   */
-  nameTab: function(tabIndex, beginAt, endAt) {
-    let tab = this._getTab(tabIndex);
-    let a = L10N.numberWithDecimals(beginAt, 2);
-    let b = L10N.numberWithDecimals(endAt, 2);
-    let labelNode = $(".tab-title-label", tab);
-    labelNode.setAttribute("value", L10N.getFormatStr("profile.tab", a, b));
-  },
-
-  /**
-   * Populates the panel for a tab in this tabbed browser with the provided
-   * recording data.
-   *
-   * @param number tabIndex
-   *        The index of the tab to populate.
-   * @param object recordingData
-   *        The profiler and refresh driver ticks data received from the front.
-   * @param number beginAt
-   *        The earliest time in the recording data to start at (in milliseconds).
-   * @param number endAt
-   *        The latest time in the recording data to end at (in milliseconds).
-   * @param object options
-   *        Additional options supported by this operation.
-   *        @see ProfileView._populatePanelWidgets
-   */
-  populateTab: Task.async(function*(tabIndex, recordingData, beginAt, endAt, options) {
-    let tab = this._getTab(tabIndex);
-    let panel = this._getPanel(tabIndex);
-    if (!tab || !panel) {
-      return;
-    }
-
-    this._recordingInfoByPanel.set(panel, {
-      recordingData: recordingData,
-      displayRange: { beginAt: beginAt, endAt: endAt }
-    });
-
-    let { profilerData, ticksData } = recordingData;
-    let categoriesData = RecordingUtils.plotCategoriesFor(profilerData, beginAt, endAt);
-    let framerateData = RecordingUtils.plotFramerateFor(ticksData, beginAt, endAt);
-    RecordingUtils.syncCategoriesWithFramerate(categoriesData, framerateData);
-
-    yield this._populatePanelWidgets(panel, {
-      profilerData: profilerData,
-      framerateData: framerateData,
-      categoriesData: categoriesData
-    }, beginAt, endAt, options);
-  }),
-
-  /**
-   * Adds a new tab in this tabbed browser, populates it with the provided
-   * recording data and automatically selects it.
-   *
-   * @param object recordingData
-   *        The profiler and refresh driver ticks data received from the front.
-   * @param number beginAt
-   *        The earliest time in the recording data to start at (in milliseconds).
-   * @param number endAt
-   *        The latest time in the recording data to end at (in milliseconds).
-   * @param object options
-   *        Additional options supported by this operation.
-   *        @see ProfileView._populatePanelWidgets
-   */
-  addTabAndPopulate: Task.async(function*(recordingData, beginAt, endAt, options) {
-    let tabIndex = this.addTab();
-    this.nameTab(tabIndex, beginAt, endAt);
-
-    // Wait for a few milliseconds before presenting the recording data,
-    // to allow the 'Loading' panel to finish being drawn (if there is one).
-    yield DevToolsUtils.waitForTime(RECORDING_DATA_DISPLAY_DELAY);
-    yield this.populateTab(tabIndex, recordingData, beginAt, endAt, options);
-    this.selectTab(tabIndex);
-  }),
-
-  /**
-   * Removes all tabs and corresponding views from this tabbed browser.
-   */
-  removeAllTabs: function() {
-    for (let [, graph] of this._framerateGraphByPanel) graph.destroy();
-    for (let [, graph] of this._categoriesGraphByPanel) graph.destroy();
-    for (let [, root] of this._callTreeRootByPanel) root.remove();
-
-    this._recordingInfoByPanel = new WeakMap();
-    this._framerateGraphByPanel.clear();
-    this._categoriesGraphByPanel.clear();
-    this._callTreeRootByPanel.clear();
-
-    while (this._tabs.hasChildNodes()) {
-      this._tabs.firstChild.remove();
-    }
-    while (this._panels.hasChildNodes()) {
-      this._panels.firstChild.remove();
-    }
-  },
-
-  /**
-   * Removes all tabs exclusively after the one at the specified index.
-   *
-   * @param number tabIndex
-   *        The "leftmost" tab to still keep. Remaining tabs will be removed.
-   */
-  removeTabsAfter: function(tabIndex) {
-    tabIndex++;
-
-    while (tabIndex < this._tabs.itemCount) {
-      let tab = this._getTab(tabIndex);
-      let panel = this._getPanel(tabIndex);
-
-      this._framerateGraphByPanel.delete(panel);
-      this._categoriesGraphByPanel.delete(panel);
-      this._callTreeRootByPanel.delete(panel);
-      tab.remove();
-      panel.remove();
-    }
-  },
-
-  /**
-   * Gets the total number of tabs displayed in this tabbed browser.
-   * @return number
-   */
-  get tabCount() {
-    let tabs = this._tabs.childNodes.length;
-    let tabpanels = this._panels.childNodes.length;
-    if (tabs != tabpanels) {
-      throw "The number of tabs isn't equal to the number of tabpanels.";
-    }
-    return tabs;
-  },
-
-  /**
-   * Adds a new tab in this tabbed browser, populates it based on the current
-   * selection range in the displayed data and automatically selects it.
-   */
-  _spawnTabFromSelection: Task.async(function*() {
-    let { recordingData } = this._getRecordingInfo();
-    let categoriesGraph = this._getCategoriesGraph();
-
-    // A selection is assumed to be available in the current tab.
-    let { min: beginAt, max: endAt } = categoriesGraph.getMappedSelection();
-
-    // Hide the "new tab" button since a selection won't implicitly be made
-    // in the newly created tab.
-    this._newtabButton.hidden = true;
-
-    yield this.addTabAndPopulate(recordingData, beginAt, endAt);
-
-    // Signal that a new tab was spawned from a graph's selection.
-    window.emit(EVENTS.TAB_SPAWNED_FROM_SELECTION);
-  }),
-
-  /**
-   * Adds a new tab in this tabbed browser, populates it based on the provided
-   * frame node and automatically selects it.
-   *
-   * @param FrameNode frameNode
-   *        Information about the function call node in the tree.
-   */
-  _spawnTabFromFrameNode: Task.async(function*(frameNode) {
-    let { recordingData } = this._getRecordingInfo();
-    let sampleTimes = frameNode.sampleTimes;
-    let beginAt = sampleTimes[0].start;
-    let endAt = sampleTimes[sampleTimes.length - 1].end;
-
-    // Hide the "new tab" button since a selection won't implicitly be made
-    // in the newly created tab.
-    this._newtabButton.hidden = true;
-
-    yield this.addTabAndPopulate(recordingData, beginAt, endAt, { skipCallTree: true });
-    this._populateCallTreeFromFrameNode(this._getPanel(), frameNode);
-
-    // Signal that a new tab was spawned from a node in the call tree.
-    window.emit(EVENTS.TAB_SPAWNED_FROM_FRAME_NODE);
-  }),
-
-  /**
-   * Filters the recording data displayed in the call tree view to match
-   * the current selection range in the graphs.
-   *
-   * @param object options
-   *        Additional options supported by this operation.
-   *        @see ProfileView._populatePanelWidgets
-   */
-  _rebuildTreeFromSelection: function(options) {
-    let { recordingData, displayRange } = this._getRecordingInfo();
-    let categoriesGraph = this._getCategoriesGraph();
-    let selectedPanel = this._getPanel();
-
-    // If there's no selection, get the original display range and hide the
-    // "new tab" button.
-    if (!categoriesGraph.hasSelection()) {
-      let { beginAt, endAt } = displayRange;
-      this._newtabButton.hidden = true;
-      this._populateCallTree(selectedPanel, recordingData.profilerData, beginAt, endAt, options);
-    }
-    // Otherwise, just get the selected display range and only show the
-    // "new tab" button if the selection is wide enough.
-    else {
-      let { min: beginAt, max: endAt } = categoriesGraph.getMappedSelection();
-      this._newtabButton.hidden = (endAt - beginAt) < GRAPH_ZOOM_MIN_TIMESPAN;
-      this._populateCallTree(selectedPanel, recordingData.profilerData, beginAt, endAt, options);
-    }
-  },
-
-  /**
-   * Highlights certain areas in the categories graph to match the currently
-   * selected frame node's sample times in the tree view.
-   *
-   * @param ThreadNode | FrameNode frameNode
-   *        The root node data source for this tree.
-   */
-  _highlightAreaFromFrameNode: function(frameNode) {
-    let categoriesGraph = this._getCategoriesGraph();
-    if (categoriesGraph) {
-      categoriesGraph.setMask(frameNode.sampleTimes);
-    }
-  },
-
-  /**
-   * Populates all the widgets in the specified tab's panel with the provided
-   * data. The already existing widgets will be removed.
-   *
-   * @param nsIDOMNode panel
-   *        The <panel> element in this <tabbox>.
-   * @param object dataSource
-   *        The profiler, framerate and categories data source.
-   * @param number beginAt
-   *        The earliest allowed time for tree nodes (in milliseconds).
-   * @param number endAt
-   *        The latest allowed time for tree nodes (in milliseconds).
-   * @param object options
-   *        Additional options supported by this operation:
-   *          - skipCallTree: true if the call tree should not be populated
-   *          - skipCallTreeFocus: true if the root node shouldn't be focused
-   */
-  _populatePanelWidgets: Task.async(function*(panel, dataSource, beginAt, endAt, options = {}) {
-    let { profilerData, framerateData, categoriesData } = dataSource;
-
-    let framerateGraph = yield this._populateFramerateGraph(panel, framerateData, beginAt);
-    let categoriesGraph = yield this._populateCategoriesGraph(panel, categoriesData, beginAt);
-    CanvasGraphUtils.linkAnimation(framerateGraph, categoriesGraph);
-    CanvasGraphUtils.linkSelection(framerateGraph, categoriesGraph);
-
-    if (!options.skipCallTree) {
-      this._populateCallTree(panel, profilerData, beginAt, endAt, options);
-    }
-  }),
-
-  /**
-   * Populates the framerate graph in the specified tab's panel with the
-   * provided data. The already existing graph will be removed.
-   *
-   * @param nsIDOMNode panel
-   *        The <panel> element in this <tabbox>.
-   * @param object framerateData
-   *        The data source for this graph.
-   * @param number beginAt
-   *        The earliest time in the recording data to start at (in milliseconds).
-   */
-  _populateFramerateGraph: Task.async(function*(panel, framerateData, beginAt) {
-    let oldGraph = this._getFramerateGraph(panel);
-    if (oldGraph) {
-      oldGraph.destroy();
-    }
-
-    // Don't create a graph if there's not enough data to show.
-    if (!framerateData || framerateData.length < 2) {
-      return null;
-    }
-
-    let graph = new LineGraphWidget($(".framerate", panel), {
-      metric: L10N.getStr("graphs.fps")
-    });
-    graph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
-    graph.dataOffsetX = beginAt;
-
-    yield graph.setDataWhenReady(framerateData);
-
-    graph.on("mouseup", this._onGraphMouseUp);
-    graph.on("scroll", this._onGraphScroll);
-
-    this._framerateGraphByPanel.set(panel, graph);
-    return graph;
-  }),
-
-  /**
-   * Populates the categories graph in the specified tab's panel with the
-   * provided data. The already existing graph will be removed.
-   *
-   * @param nsIDOMNode panel
-   *        The <panel> element in this <tabbox>.
-   * @param object categoriesData
-   *        The data source for this graph.
-   * @param number beginAt
-   *        The earliest time in the recording data to start at (in milliseconds).
-   */
-  _populateCategoriesGraph: Task.async(function*(panel, categoriesData, beginAt) {
-    let oldGraph = this._getCategoriesGraph(panel);
-    if (oldGraph) {
-      oldGraph.destroy();
-    }
-    // Don't create a graph if there's not enough data to show.
-    if (!categoriesData || categoriesData.length < 2) {
-      return null;
-    }
-
-    let graph = new BarGraphWidget($(".categories", panel));
-    graph.fixedHeight = CATEGORIES_GRAPH_HEIGHT;
-    graph.minBarsWidth = CATEGORIES_GRAPH_MIN_BARS_WIDTH;
-    graph.format = CATEGORIES.sort((a, b) => a.ordinal > b.ordinal);
-    graph.dataOffsetX = beginAt;
-
-    yield graph.setDataWhenReady(categoriesData);
-
-    graph.on("legend-selection", this._onGraphLegendSelection);
-    graph.on("mouseup", this._onGraphMouseUp);
-    graph.on("scroll", this._onGraphScroll);
-
-    this._categoriesGraphByPanel.set(panel, graph);
-    return graph;
-  }),
-
-  /**
-   * Populates the call tree view in the specified tab's panel with the
-   * provided data. The already existing tree will be removed.
-   *
-   * @param nsIDOMNode panel
-   *        The <panel> element in this <tabbox>.
-   * @param object profilerData
-   *        The data source for this tree.
-   * @param number startTime
-   *        The earliest time in the data source to start at (in milliseconds).
-   * @param number endTime
-   *        The latest time in the data source to end at (in milliseconds).
-   * @param object options
-   *        Additional options supported by this operation.
-   *        @see ProfileView._populatePanelWidgets
-   */
-  _populateCallTree: function(panel, profilerData, startTime, endTime, options = {}) {
-    let threadSamples = profilerData.profile.threads[0].samples;
-    let contentOnly = !Prefs.showPlatformData;
-    let invertChecked = this._invertTree.hasAttribute("checked");
-    let threadNode = new ThreadNode(threadSamples,
-      { startTime, endTime, contentOnly, invertChecked });
-
-    // If we have an empty profile (no samples), then don't invert the tree, as
-    // it would hide the root node and a completely blank call tree space can be
-    // mis-interpreted as an error.
-    options.inverted = invertChecked && threadNode.samples > 0;
-    this._populateCallTreeFromFrameNode(panel, threadNode, options);
-  },
-
-  /**
-   * Populates the call tree view in the specified tab's panel with the
-   * provided frame node. The already existing tree will be removed.
-   *
-   * @param nsIDOMNode panel
-   *        The <panel> element in this <tabbox>.
-   * @param ThreadNode | FrameNode frameNode
-   *        The root node data source for this tree.
-   * @param object options
-   *        Additional options supported by this operation.
-   *        @see ProfileView._populatePanelWidgets
-   */
-  _populateCallTreeFromFrameNode: function(panel, frameNode, options = {}) {
-    let oldRoot = this._getCallTreeRoot(panel);
-    if (oldRoot) {
-      oldRoot.remove();
-    }
-
-    let callTreeRoot = new CallView({
-      autoExpandDepth: options.inverted ? 0 : undefined,
-      frame: frameNode,
-      hidden: options.inverted,
-      inverted: options.inverted
-    });
-    callTreeRoot.on("focus", this._onCallViewFocus);
-    callTreeRoot.on("link", this._onCallViewLink);
-    callTreeRoot.on("zoom", this._onCallViewZoom);
-    callTreeRoot.attachTo($(".call-tree-cells-container", panel));
-
-    if (!options.skipCallTreeFocus) {
-      callTreeRoot.focus();
-    }
-
-    let contentOnly = !Prefs.showPlatformData;
-    callTreeRoot.toggleCategories(!contentOnly);
-
-    this._callTreeRootByPanel.set(panel, callTreeRoot);
-  },
-
-  /**
-   * Shortcuts for accessing the recording info or widgets for a <panel>.
-   * @param nsIDOMNode panel [optional]
-   * @return object
-   */
-  _getRecordingInfo: function(panel = this._getPanel()) {
-    return this._recordingInfoByPanel.get(panel);
-  },
-  _getFramerateGraph: function(panel = this._getPanel()) {
-    return this._framerateGraphByPanel.get(panel);
-  },
-  _getCategoriesGraph: function(panel = this._getPanel()) {
-    return this._categoriesGraphByPanel.get(panel);
-  },
-  _getCallTreeRoot: function(panel = this._getPanel()) {
-    return this._callTreeRootByPanel.get(panel);
-  },
-  _getTab: function(tabIndex = this._getSelectedIndex()) {
-    return this._tabs.childNodes[tabIndex];
-  },
-  _getPanel: function(tabIndex = this._getSelectedIndex()) {
-    return this._panels.childNodes[tabIndex];
-  },
-  _getSelectedIndex: function() {
-    return $("#profile-content").selectedIndex;
-  },
-
-  /**
-   * Listener handling the tab "select" event in this container.
-   */
-  _onTabSelect: function() {
-    let categoriesGraph = this._getCategoriesGraph();
-    if (categoriesGraph) {
-      this._newtabButton.hidden = !categoriesGraph.hasSelection();
-    } else {
-      this._newtabButton.hidden = true;
-    }
-
-    this.removeTabsAfter(this._getSelectedIndex());
-  },
-
-  /**
-   * Listener handling the new tab "click" event in this container.
-   */
-  _onNewTabClick: function() {
-    this._spawnTabFromSelection();
-  },
-
-  _onInvertTree: function() {
-    this._rebuildTreeFromSelection();
-  },
-
-  /**
-   * Listener handling the "legend-selection" event for the graphs in this container.
-   */
-  _onGraphLegendSelection: function() {
-    this._rebuildTreeFromSelection({ skipCallTreeFocus: true });
-  },
-
-  /**
-   * Listener handling the "mouseup" event for the graphs in this container.
-   */
-  _onGraphMouseUp: function() {
-    this._rebuildTreeFromSelection();
-  },
-
-  /**
-   * Listener handling the "scroll" event for the graphs in this container.
-   */
-  _onGraphScroll: function() {
-    setNamedTimeout("graph-scroll", GRAPH_SCROLL_EVENTS_DRAIN, () => {
-      this._rebuildTreeFromSelection();
-    });
-  },
-
-  /**
-   * Listener handling the "focus" event for the call tree in this container.
-   */
-  _onCallViewFocus: function(event, treeItem) {
-    setNamedTimeout("graph-focus", CALL_VIEW_FOCUS_EVENTS_DRAIN, () => {
-      this._highlightAreaFromFrameNode(treeItem.frame);
-    });
-  },
-
-  /**
-   * Listener handling the "link" event for the call tree in this container.
-   */
-  _onCallViewLink: function(event, treeItem) {
-    let { url, line } = treeItem.frame.getInfo();
-    viewSourceInDebugger(url, line);
-  },
-
-  /**
-   * Listener handling the "zoom" event for the call tree in this container.
-   */
-  _onCallViewZoom: function(event, treeItem) {
-    this._spawnTabFromFrameNode(treeItem.frame);
-  }
-};
-
-/**
- * Utility functions handling recording data.
- */
-let RecordingUtils = {
-  _frameratePlotsCache: new WeakMap(),
-
-  /**
-   * Creates an appropriate data source to be displayed in a categories graph
-   * from on the provided profiler data.
-   *
-   * @param object profilerData
-   *        The profiler data received from the front.
-   * @param number beginAt
-   *        The earliest time in the profiler data to start at (in milliseconds).
-   * @param number endAt
-   *        The latest time in the profiler data to end at (in milliseconds).
-   * @return array
-   *         A data source useful for a BarGraphWidget.
-   */
-  plotCategoriesFor: function(profilerData, beginAt, endAt) {
-    let categoriesData = [];
-    let profile = profilerData.profile;
-    let samples = profile.threads[0].samples;
-
-    // Accumulate the category of each frame for every sample.
-    for (let { frames, time } of samples) {
-      if (!time || time < beginAt || time > endAt) continue;
-      let blocks = [];
-
-      for (let { category: bitmask } of frames) {
-        if (!bitmask) continue;
-        let category = CATEGORY_MAPPINGS[bitmask];
-
-        // Guard against categories that aren't found in the frontend mappings.
-        // This usually means that a new category was added in the platform,
-        // but browser/devtools/profiler/utils/global.js wasn't updated yet.
-        if (!category) {
-          category = CATEGORY_MAPPINGS[CATEGORY_OTHER];
-        }
-
-        if (!blocks[category.ordinal]) {
-          blocks[category.ordinal] = 1;
-        } else {
-          blocks[category.ordinal]++;
-        }
-      }
-
-      // If no categories were found in the frames, default to using a
-      // single block using the stack depth as height.
-      if (blocks.length == 0) {
-        blocks[CATEGORY_MAPPINGS[CATEGORY_OTHER].ordinal] = frames.length;
-      }
-
-      categoriesData.push({
-        delta: time,
-        values: blocks
-      });
-    }
-
-    return categoriesData;
-  },
-
-  /**
-   * Creates an appropriate data source to be displayed in a framerate graph
-   * from on the provided refresh driver ticks data.
-   *
-   * @param object ticksData
-   *        The refresh driver ticks received from the front.
-   * @param number beginAt
-   *        The earliest time in the ticks data to start at (in milliseconds).
-   * @param number endAt
-   *        The latest time in the ticks data to end at (in milliseconds).
-   * @return array
-   *         A data source useful for a LineGraphWidget.
-   */
-  plotFramerateFor: function(ticksData, beginAt, endAt) {
-    // Older Gecko versions don't have a framerate actor implementation,
-    // in which case the returned ticks data is null.
-    if (ticksData == null) {
-      return [];
-    }
-
-    let framerateData = this._frameratePlotsCache.get(ticksData);
-    if (framerateData == null) {
-      framerateData = FramerateFront.plotFPS(ticksData, FRAMERATE_CALC_INTERVAL);
-      this._frameratePlotsCache.set(ticksData, framerateData);
-    }
-
-    // Quickly find the earliest and oldest valid index in the plotted
-    // framerate data based on the specified beginAt and endAt time. Sure,
-    // using [].findIndex would be more elegant, but also slower.
-    let earliestValidIndex = findFirstIndex(framerateData, e => e.delta >= beginAt);
-    let oldestValidIndex = findLastIndex(framerateData, e => e.delta <= endAt);
-    let totalValues = framerateData.length;
-
-    // If all the plotted framerate data fits inside the specified time range,
-    // simply return it.
-    if (earliestValidIndex == 0 && oldestValidIndex == totalValues - 1) {
-      return framerateData;
-    }
-
-    // Otherwise, a slice will need to be made. Be very careful here, the
-    // beginAt and endAt timestamps can refer to a point in *between* two
-    // entries in the framerate data, so we'll need to insert new values where
-    // the cuts are made.
-    let slicedData = framerateData.slice(earliestValidIndex, oldestValidIndex + 1);
-    if (earliestValidIndex > 0) {
-      slicedData.unshift({
-        delta: beginAt,
-        value: framerateData[earliestValidIndex - 1].value
-      });
-    }
-    if (oldestValidIndex < totalValues - 1) {
-      slicedData.push({
-        delta: endAt,
-        value: framerateData[oldestValidIndex + 1].value
-      });
-    }
-
-    return slicedData;
-  },
-
-  /**
-   * Makes sure the data sources for the categories and framerate graphs
-   * have the same beginning and ending, time-wise.
-   *
-   * @param array categoriesData
-   *        Data source generated by `RecordingUtils.plotCategoriesFor`.
-   * @param array framerateData
-   *        Data source generated by `RecordingUtils.plotFramerateFor`.
-   */
-  syncCategoriesWithFramerate: function(categoriesData, framerateData) {
-    if (categoriesData.length < 2 || framerateData.length < 2) {
-      return;
-    }
-    let categoryBegin = categoriesData[0];
-    let categoryEnd = categoriesData[categoriesData.length - 1];
-    let framerateBegin = framerateData[0];
-    let framerateEnd = framerateData[framerateData.length - 1];
-
-    if (categoryBegin.delta > framerateBegin.delta) {
-      categoriesData.unshift({
-        delta: framerateBegin.delta,
-        values: categoryBegin.values
-      });
-    } else {
-      framerateData.unshift({
-        delta: categoryBegin.delta,
-        value: framerateBegin.value
-      });
-    }
-    if (categoryEnd.delta < framerateEnd.delta) {
-      categoriesData.push({
-        delta: framerateEnd.delta,
-        values: categoryEnd.values
-      });
-    } else {
-      framerateData.push({
-        delta: categoryEnd.delta,
-        value: framerateEnd.value
-      });
-    }
-  }
-};
-
-/**
- * Finds the index of the first element in an array that validates a predicate.
- * @param array
- * @param function predicate
- * @return number
- */
-function findFirstIndex(array, predicate) {
-  for (let i = 0, len = array.length; i < len; i++) {
-    if (predicate(array[i])) return i;
-  }
-}
-
-/**
- * Finds the last of the first element in an array that validates a predicate.
- * @param array
- * @param function predicate
- * @return number
- */
-function findLastIndex(array, predicate) {
-  for (let i = array.length - 1; i >= 0; i--) {
-    if (predicate(array[i])) return i;
-  }
-}
-
-/**
- * Opens/selects the debugger in this toolbox and jumps to the specified
- * file name and line number.
- * @param string url
- * @param number line
- */
-function viewSourceInDebugger(url, line) {
-  let showSource = ({ DebuggerView }) => {
-    let item = DebuggerView.Sources.getItemForAttachment(a => a.source.url === url);
-    if (item) {
-      DebuggerView.setEditorLocation(item.attachment.source.actor, line, { noDebug: true }).then(() => {
-        window.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER);
-      }, () => {
-        window.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER);
-      });
-    }
-  };
-
-  // If the Debugger was already open, switch to it and try to show the
-  // source immediately. Otherwise, initialize it and wait for the sources
-  // to be added first.
-  let debuggerAlreadyOpen = gToolbox.getPanel("jsdebugger");
-  gToolbox.selectTool("jsdebugger").then(({ panelWin: dbg }) => {
-    if (debuggerAlreadyOpen) {
-      showSource(dbg);
-    } else {
-      dbg.once(dbg.EVENTS.SOURCES_ADDED, () => showSource(dbg));
-    }
-  });
-}
deleted file mode 100644
--- a/browser/devtools/profiler/ui-recordings.js
+++ /dev/null
@@ -1,369 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-/**
- * Functions handling the recordings UI.
- */
-let RecordingsListView = Heritage.extend(WidgetMethods, {
-  /**
-   * Initialization function, called when the tool is started.
-   */
-  initialize: function() {
-    this.widget = new SideMenuWidget($("#recordings-list"));
-
-    this._onSelect = this._onSelect.bind(this);
-    this._onClearButtonClick = this._onClearButtonClick.bind(this);
-    this._onRecordButtonClick = this._onRecordButtonClick.bind(this);
-    this._onImportButtonClick = this._onImportButtonClick.bind(this);
-    this._onSaveButtonClick = this._onSaveButtonClick.bind(this);
-
-    this.emptyText = L10N.getStr("noRecordingsText");
-    this.widget.addEventListener("select", this._onSelect, false);
-  },
-
-  /**
-   * Destruction function, called when the tool is closed.
-   */
-  destroy: function() {
-    this.widget.removeEventListener("select", this._onSelect, false);
-  },
-
-  /**
-   * Adds an empty recording to this container.
-   *
-   * @param string profileLabel [optional]
-   *        A custom label for the newly created recording item.
-   */
-  addEmptyRecording: function(profileLabel) {
-    let titleNode = document.createElement("label");
-    titleNode.className = "plain recording-item-title";
-    titleNode.setAttribute("value", profileLabel ||
-      L10N.getFormatStr("recordingsList.itemLabel", this.itemCount + 1));
-
-    let durationNode = document.createElement("label");
-    durationNode.className = "plain recording-item-duration";
-    durationNode.setAttribute("value",
-      L10N.getStr("recordingsList.recordingLabel"));
-
-    let saveNode = document.createElement("label");
-    saveNode.className = "plain recording-item-save";
-    saveNode.addEventListener("click", this._onSaveButtonClick);
-
-    let hspacer = document.createElement("spacer");
-    hspacer.setAttribute("flex", "1");
-
-    let footerNode = document.createElement("hbox");
-    footerNode.className = "recording-item-footer";
-    footerNode.appendChild(durationNode);
-    footerNode.appendChild(hspacer);
-    footerNode.appendChild(saveNode);
-
-    let vspacer = document.createElement("spacer");
-    vspacer.setAttribute("flex", "1");
-
-    let contentsNode = document.createElement("vbox");
-    contentsNode.className = "recording-item";
-    contentsNode.setAttribute("flex", "1");
-    contentsNode.appendChild(titleNode);
-    contentsNode.appendChild(vspacer);
-    contentsNode.appendChild(footerNode);
-
-    // Append a recording item to this container.
-    return this.push([contentsNode], {
-      attachment: {
-        // The profiler and refresh driver ticks data will be available
-        // as soon as recording finishes.
-        profilerData: { profileLabel },
-        ticksData: null
-      }
-    });
-  },
-
-  /**
-   * Signals that a recording session has started.
-   *
-   * @param string profileLabel
-   *        The provided string argument if available, undefined otherwise.
-   */
-  handleRecordingStarted: function(profileLabel) {
-    // Insert a "dummy" recording item, to hint that recording has now started.
-    let recordingItem;
-
-    // If a label is specified (e.g due to a call to `console.profile`),
-    // then try reusing a pre-existing recording item, if there is one.
-    // This is symmetrical to how `this.handleRecordingEnded` works.
-    if (profileLabel) {
-      recordingItem = this.getItemForAttachment(e =>
-        e.profilerData.profileLabel == profileLabel);
-    }
-    // Otherwise, create a new empty recording item.
-    if (!recordingItem) {
-      recordingItem = this.addEmptyRecording(profileLabel);
-    }
-
-    // Mark the corresponding item as being a "record in progress".
-    recordingItem.isRecording = true;
-
-    // If this is the first item, immediately select it.
-    if (this.itemCount == 1) {
-      this.selectedItem = recordingItem;
-    }
-
-    window.emit(EVENTS.RECORDING_STARTED, profileLabel);
-  },
-
-  /**
-   * Signals that a recording session has ended.
-   *
-   * @param object recordingData
-   *        The profiler and refresh driver ticks data received from the front.
-   */
-  handleRecordingEnded: function(recordingData) {
-    let profileLabel = recordingData.profilerData.profileLabel;
-    let recordingItem;
-
-    // If a label is specified (e.g due to a call to `console.profileEnd`),
-    // then try reusing a pre-existing recording item, if there is one.
-    // This is symmetrical to how `this.handleRecordingStarted` works.
-    if (profileLabel) {
-      recordingItem = this.getItemForAttachment(e =>
-        e.profilerData.profileLabel == profileLabel);
-    }
-    // Otherwise, just use the first available recording item.
-    if (!recordingItem) {
-      recordingItem = this.getItemForPredicate(e => e.isRecording);
-    }
-
-    // Mark the corresponding item as being a "finished recording".
-    recordingItem.isRecording = false;
-
-    // Store the recording data, customize and select this recording item.
-    this.customizeRecording(recordingItem, recordingData);
-    this.forceSelect(recordingItem);
-
-    window.emit(EVENTS.RECORDING_ENDED, recordingData);
-  },
-
-  /**
-   * Signals that a recording session has ended abruptly and the accumulated
-   * data should be discarded.
-   */
-  handleRecordingCancelled: Task.async(function*() {
-    if ($("#record-button").hasAttribute("checked")) {
-      $("#record-button").removeAttribute("checked");
-      yield gFront.cancelRecording();
-    }
-    ProfileView.showEmptyNotice();
-
-    window.emit(EVENTS.RECORDING_LOST);
-  }),
-
-  /**
-   * Adds recording data to a recording item in this container.
-   *
-   * @param Item recordingItem
-   *        An item inserted via `RecordingsListView.addEmptyRecording`.
-   * @param object recordingData
-   *        The profiler and refresh driver ticks data received from the front.
-   */
-  customizeRecording: function(recordingItem, recordingData) {
-    recordingItem.attachment = recordingData;
-
-    let saveNode = $(".recording-item-save", recordingItem.target);
-    saveNode.setAttribute("value",
-      L10N.getStr("recordingsList.saveLabel"));
-
-    let durationMillis = recordingData.recordingDuration;
-    let durationNode = $(".recording-item-duration", recordingItem.target);
-    durationNode.setAttribute("value",
-      L10N.getFormatStr("recordingsList.durationLabel", durationMillis));
-  },
-
-  /**
-   * The select listener for this container.
-   */
-  _onSelect: Task.async(function*({ detail: recordingItem }) {
-    if (!recordingItem) {
-      ProfileView.showEmptyNotice();
-      return;
-    }
-    if (recordingItem.isRecording) {
-      ProfileView.showRecordingNotice();
-      return;
-    }
-
-    ProfileView.showLoadingNotice();
-    ProfileView.removeAllTabs();
-
-    let recordingData = recordingItem.attachment;
-    let durationMillis = recordingData.recordingDuration;
-    yield ProfileView.addTabAndPopulate(recordingData, 0, durationMillis);
-    ProfileView.showTabbedBrowser();
-
-    // Only clear the checked state if there's nothing recording.
-    if (!this.getItemForPredicate(e => e.isRecording)) {
-      $("#record-button").removeAttribute("checked");
-    }
-
-    // But don't leave it locked in any case.
-    $("#record-button").removeAttribute("locked");
-
-    window.emit(EVENTS.RECORDING_DISPLAYED);
-  }),
-
-  /**
-   * The click listener for the "clear" button in this container.
-   */
-  _onClearButtonClick: Task.async(function*() {
-    this.empty();
-    yield this.handleRecordingCancelled();
-  }),
-
-  /**
-   * The click listener for the "record" button in this container.
-   */
-  _onRecordButtonClick: Task.async(function*() {
-    if (!$("#record-button").hasAttribute("checked")) {
-      $("#record-button").setAttribute("checked", "true");
-      yield gFront.startRecording();
-      this.handleRecordingStarted();
-    } else {
-      $("#record-button").setAttribute("locked", "");
-      let recordingData = yield gFront.stopRecording();
-      this.handleRecordingEnded(recordingData);
-    }
-  }),
-
-  /**
-   * The click listener for the "import" button in this container.
-   */
-  _onImportButtonClick: Task.async(function*() {
-    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"), Ci.nsIFilePicker.modeOpen);
-    fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
-    fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
-
-    if (fp.show() == Ci.nsIFilePicker.returnOK) {
-      loadRecordingFromFile(fp.file);
-    }
-  }),
-
-  /**
-   * The click listener for the "save" button of each item in this container.
-   */
-  _onSaveButtonClick: function(e) {
-    let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
-    fp.init(window, L10N.getStr("recordingsList.saveDialogTitle"), Ci.nsIFilePicker.modeSave);
-    fp.appendFilter(L10N.getStr("recordingsList.saveDialogJSONFilter"), "*.json");
-    fp.appendFilter(L10N.getStr("recordingsList.saveDialogAllFilter"), "*.*");
-    fp.defaultString = "profile.json";
-
-    fp.open({ done: result => {
-      if (result == Ci.nsIFilePicker.returnCancel) {
-        return;
-      }
-      let recordingItem = this.getItemForElement(e.target);
-      saveRecordingToFile(recordingItem, fp.file);
-    }});
-  }
-});
-
-/**
- * Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
- * @return object
- */
-function getUnicodeConverter() {
-  let className = "@mozilla.org/intl/scriptableunicodeconverter";
-  let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
-  converter.charset = "UTF-8";
-  return converter;
-}
-
-/**
- * Saves a recording as JSON to a file. The provided data is assumed to be
- * acyclical, so that it can be properly serialized.
- *
- * @param Item recordingItem
- *        The recording item containing the data to stream as JSON.
- * @param nsILocalFile file
- *        The file to stream the data into.
- * @return object
- *         A promise that is resolved once streaming finishes, or rejected
- *         if there was an error.
- */
-function saveRecordingToFile(recordingItem, file) {
-  let deferred = promise.defer();
-
-  let recordingData = recordingItem.attachment;
-  recordingData.fileType = PROFILE_SERIALIZER_IDENTIFIER;
-  recordingData.version = PROFILE_SERIALIZER_VERSION;
-
-  let string = JSON.stringify(recordingData);
-  let inputStream = getUnicodeConverter().convertToInputStream(string);
-  let outputStream = FileUtils.openSafeFileOutputStream(file);
-
-  NetUtil.asyncCopy(inputStream, outputStream, status => {
-    if (!Components.isSuccessCode(status)) {
-      deferred.reject(new Error("Could not save recording data file."));
-    }
-    deferred.resolve();
-  });
-
-  return deferred.promise;
-}
-
-/**
- * Loads a recording stored as JSON from a file.
- *
- * @param nsILocalFile file
- *        The file to import the data from.
- * @return object
- *         A promise that is resolved once importing finishes, or rejected
- *         if there was an error.
- */
-function loadRecordingFromFile(file) {
-  let deferred = promise.defer();
-
-  let channel = NetUtil.newChannel2(file,
-                                    null,
-                                    null,
-                                    window.document,
-                                    null,  // aLoadingPrincipal
-                                    null,  // aTriggeringPrincipal
-                                    Ci.nsILoadInfo.SEC_NORMAL,
-                                    Ci.nsIContentPolicy.TYPE_OTHER);
-  channel.contentType = "text/plain";
-
-  NetUtil.asyncFetch2(channel, (inputStream, status) => {
-    if (!Components.isSuccessCode(status)) {
-      deferred.reject(new Error("Could not import recording data file."));
-      return;
-    }
-    try {
-      let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
-      var recordingData = JSON.parse(string);
-    } catch (e) {
-      deferred.reject(new Error("Could not read recording data file."));
-      return;
-    }
-    if (recordingData.fileType != PROFILE_SERIALIZER_IDENTIFIER) {
-      deferred.reject(new Error("Unrecognized recording data file."));
-      return;
-    }
-
-    let profileLabel = recordingData.profilerData.profileLabel;
-    let recordingItem = RecordingsListView.addEmptyRecording(profileLabel);
-    RecordingsListView.customizeRecording(recordingItem, recordingData);
-
-    // If this is the first item, immediately select it.
-    if (RecordingsListView.itemCount == 1) {
-      RecordingsListView.selectedItem = recordingItem;
-    }
-
-    deferred.resolve();
-  });
-
-  return deferred.promise;
-}
deleted file mode 100644
--- a/browser/devtools/profiler/utils/shared.js
+++ /dev/null
@@ -1,524 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-const {Cc, Ci, Cu, Cr} = require("chrome");
-
-Cu.import("resource://gre/modules/Task.jsm");
-
-loader.lazyRequireGetter(this, "Services");
-loader.lazyRequireGetter(this, "promise");
-loader.lazyRequireGetter(this, "EventEmitter",
-  "devtools/toolkit/event-emitter");
-loader.lazyRequireGetter(this, "FramerateFront",
-  "devtools/server/actors/framerate", true);
-loader.lazyRequireGetter(this, "DevToolsUtils",
-  "devtools/toolkit/DevToolsUtils");
-
-loader.lazyImporter(this, "gDevTools",
-  "resource:///modules/devtools/gDevTools.jsm");
-
-/**
- * A cache of all ProfilerConnection instances. The keys are Toolbox objects.
- */
-let SharedProfilerConnection = new WeakMap();
-
-/**
- * Instantiates a shared ProfilerConnection for the specified toolbox.
- * Consumers must yield on `open` to make sure the connection is established.
- *
- * @param Toolbox toolbox
- *        The toolbox owning this connection.
- */
-SharedProfilerConnection.forToolbox = function(toolbox) {
-  if (this.has(toolbox)) {
-    return this.get(toolbox);
-  }
-
-  let instance = new ProfilerConnection(toolbox);
-  this.set(toolbox, instance);
-  return instance;
-};
-
-/**
- * A connection to the profiler actor, along with other miscellaneous actors,
- * shared by all tools in a toolbox.
- *
- * Use `SharedProfilerConnection.forToolbox` to make sure you get the same
- * instance every time, and the `ProfilerFront` to start/stop recordings.
- *
- * @param Toolbox toolbox
- *        The toolbox owning this connection.
- */
-function ProfilerConnection(toolbox) {
-  EventEmitter.decorate(this);
-
-  this._toolbox = toolbox;
-  this._target = this._toolbox.target;
-  this._client = this._target.client;
-  this._request = this._request.bind(this);
-
-  this._pendingFramerateConsumers = 0;
-  this._pendingConsoleRecordings = [];
-  this._finishedConsoleRecordings = [];
-  this._onEventNotification = this._onEventNotification.bind(this);
-
-  Services.obs.notifyObservers(null, "profiler-connection-created", null);
-}
-
-ProfilerConnection.prototype = {
-  /**
-   * Initializes a connection to the profiler and other miscellaneous actors.
-   * If already open, nothing happens.
-   *
-   * @return object
-   *         A promise that is resolved once the connection is established.
-   */
-  open: Task.async(function*() {
-    if (this._connected) {
-      return;
-    }
-
-    // Local debugging needs to make the target remote.
-    yield this._target.makeRemote();
-
-    // Chrome and content process targets already have obtained a reference
-    // to the profiler tab actor. Use it immediately.
-    if (this._target.form && this._target.form.profilerActor) {
-      this._profiler = this._target.form.profilerActor;
-      yield this._registerEventNotifications();
-    }
-    // Check if we already have a grip to the `listTabs` response object
-    // and, if we do, use it to get to the profiler actor.
-    else if (this._target.root && this._target.root.profilerActor) {
-      this._profiler = this._target.root.profilerActor;
-      yield this._registerEventNotifications();
-    }
-    // Otherwise, call `listTabs`, but ensure not trying to fetch tab actors
-    // for AddonTarget that are chrome, but do not expose profile at all.
-    else if (!this._target.chrome) {
-      this._profiler = (yield listTabs(this._client)).profilerActor;
-      yield this._registerEventNotifications();
-    }
-
-    this._connectMiscActors();
-    this._connected = true;
-
-    Services.obs.notifyObservers(null, "profiler-connection-opened", null);
-  }),
-
-  /**
-   * Destroys this connection.
-   */
-  destroy: function() {
-    this._disconnectMiscActors();
-    this._connected = false;
-  },
-
-  /**
-   * Initializes a connection to miscellaneous actors which are going to be
-   * used in tandem with the profiler actor.
-   */
-  _connectMiscActors: function() {
-    // Only initialize the framerate front if the respective actor is available.
-    // Older Gecko versions don't have an existing implementation, in which case
-    // all the methods we need can be easily mocked.
-    if (this._target.form && this._target.form.framerateActor) {
-      this._framerate = new FramerateFront(this._target.client, this._target.form);
-    } else {
-      this._framerate = {
-        destroy: () => {},
-        startRecording: () => {},
-        stopRecording: () => {},
-        cancelRecording: () => {},
-        isRecording: () => false,
-        getPendingTicks: () => null
-      };
-    }
-  },
-
-  /**
-   * Closes the connections to miscellaneous actors.
-   * @see ProfilerConnection.prototype._connectMiscActors
-   */
-  _disconnectMiscActors: function() {
-    this._framerate.destroy();
-  },
-
-  /**
-   * Sends the request over the remote debugging protocol to the
-   * specified actor.
-   *
-   * @param string actor
-   *        The designated actor. Currently supported: "profiler", "framerate".
-   * @param string method
-   *        Method to call on the backend.
-   * @param any args [optional]
-   *        Additional data or arguments to send with the request.
-   * @return object
-   *         A promise resolved with the response once the request finishes.
-   */
-  _request: function(actor, method, ...args) {
-    // Handle requests to the profiler actor.
-    if (actor == "profiler") {
-      let deferred = promise.defer();
-      let data = args[0] || {};
-      data.to = this._profiler;
-      data.type = method;
-      this._client.request(data, deferred.resolve);
-      return deferred.promise;
-    }
-
-    // Handle requests to the framerate actor.
-    if (actor == "framerate") {
-      switch (method) {
-      // Only stop recording framerate if there are no other pending consumers.
-      // Otherwise, for example, the next time `console.profileEnd` is called
-      // there won't be any framerate data available, since we're reusing the
-      // same actor for multiple overlapping recordings.
-        case "startRecording":
-          this._pendingFramerateConsumers++;
-          break;
-        case "stopRecording":
-        case "cancelRecording":
-          if (--this._pendingFramerateConsumers > 0) return;
-          break;
-        // Some versions of the framerate actor don't have a 'getPendingTicks'
-        // method available, in which case it's impossible to get all the
-        // accumulated framerate data without stopping the recording. Bail out.
-        case "getPendingTicks":
-          if (method in this._framerate) break;
-          return null;
-      }
-      checkPendingFramerateConsumers(this);
-      return this._framerate[method].apply(this._framerate, args);
-    }
-  },
-
-  /**
-   * Starts listening to certain events emitted by the profiler actor.
-   *
-   * @return object
-   *         A promise that is resolved once the notifications are registered.
-   */
-  _registerEventNotifications: Task.async(function*() {
-    let events = ["console-api-profiler", "profiler-stopped"];
-    yield this._request("profiler", "registerEventNotifications", { events });
-    this._client.addListener("eventNotification", this._onEventNotification);
-  }),
-
-  /**
-   * Invoked whenever a registered event was emitted by the profiler actor.
-   *
-   * @param object response
-   *        The data received from the backend.
-   */
-  _onEventNotification: function(event, response) {
-    let toolbox = gDevTools.getToolbox(this._target);
-    if (toolbox == null) {
-      return;
-    }
-    if (response.topic == "console-api-profiler") {
-      let action = response.subject.action;
-      let details = response.details;
-      if (action == "profile") {
-        this.emit("invoked-console-profile", details.profileLabel); // used in tests
-        this._onConsoleProfileStart(details);
-      } else if (action == "profileEnd") {
-        this.emit("invoked-console-profileEnd", details.profileLabel); // used in tests
-        this._onConsoleProfileEnd(details);
-      }
-    } else if (response.topic == "profiler-stopped") {
-      this._onProfilerUnexpectedlyStopped();
-    }
-  },
-
-  /**
-   * Invoked whenever `console.profile` is called.
-   *
-   * @param string profileLabel
-   *        The provided string argument if available, undefined otherwise.
-   * @param number currentTime
-   *        The time (in milliseconds) when the call was made, relative to
-   *        when the nsIProfiler module was started.
-   */
-  _onConsoleProfileStart: Task.async(function*({ profileLabel, currentTime }) {
-    let pending = this._pendingConsoleRecordings;
-    if (pending.find(e => e.profileLabel == profileLabel)) {
-      return;
-    }
-    // Push this unique `console.profile` call to the stack.
-    pending.push({
-      profileLabel: profileLabel,
-      profilingStartTime: currentTime
-    });
-
-    // Profiling was automatically started when `console.profile` was invoked,
-    // so we all we have to do is make sure the framerate actor is recording.
-    yield this._request("framerate", "startRecording");
-
-    // Signal that a call to `console.profile` actually created a new recording.
-    this.emit("profile", profileLabel);
-  }),
-
-  /**
-   * Invoked whenever `console.profileEnd` is called.
-   *
-   * @param object profilerData
-   *        The profiler data received from the backend.
-   */
-  _onConsoleProfileEnd: Task.async(function*(profilerData) {
-    let pending = this._pendingConsoleRecordings;
-    if (pending.length == 0) {
-      return;
-    }
-    // Try to find the corresponding `console.profile` call in the stack
-    // with the specified label (be it undefined, or a string).
-    let info = pending.find(e => e.profileLabel == profilerData.profileLabel);
-    if (info) {
-      pending.splice(pending.indexOf(info), 1);
-    }
-    // If no corresponding `console.profile` call was found, and if no label
-    // is specified, pop the most recent entry from the stack.
-    else if (!profilerData.profileLabel) {
-      info = pending.pop();
-      profilerData.profileLabel = info.profileLabel;
-    }
-    // ...Otherwise this is a call to `console.profileEnd` with a label that
-    // doesn't exist on the stack. Bail out.
-    else {
-      return;
-    }
-
-    // The profile is already available when console.profileEnd() was invoked,
-    // but we need to filter out all samples that fall out of current profile's
-    // range. This is necessary because the profiler is continuously running.
-    filterSamples(profilerData, info.profilingStartTime);
-    offsetSampleTimes(profilerData, info.profilingStartTime);
-
-    // Fetch the recorded refresh driver ticks, during the same time window
-    // as the filtered profiler data.
-    let beginAt = findEarliestSampleTime(profilerData);
-    let endAt = findOldestSampleTime(profilerData);
-    let ticksData = yield this._request("framerate", "getPendingTicks", beginAt, endAt);
-    yield this._request("framerate", "cancelRecording");
-
-    // Join all the acquired data and emit it for outside consumers.
-    let recordingData = {
-      recordingDuration: profilerData.currentTime - info.profilingStartTime,
-      profilerData: profilerData,
-      ticksData: ticksData
-    };
-    this._finishedConsoleRecordings.push(recordingData);
-
-    // Signal that a call to `console.profileEnd` actually finished a recording.
-    this.emit("profileEnd", recordingData);
-  }),
-
-  /**
-   * Invoked whenever the built-in profiler module is deactivated. Since this
-   * should *never* happen while there's a consumer (i.e. "toolbox") available,
-   * treat this notification as being unexpected.
-   *
-   * This may happen, for example, if the Gecko Profiler add-on is installed
-   * (and isn't using the profiler actor over the remote protocol). There's no
-   * way to prevent it from stopping the profiler and affecting our tool.
-   */
-  _onProfilerUnexpectedlyStopped: function() {
-    // Pop all pending `console.profile` calls from the stack.
-    this._pendingConsoleRecordings.length = 0;
-    this.emit("profiler-unexpectedly-stopped");
-  }
-};
-
-/**
- * A thin wrapper around a shared ProfilerConnection for the parent toolbox.
- * Handles manually starting and stopping a recording.
- *
- * @param ProfilerConnection connection
- *        The shared instance for the parent toolbox.
- */
-function ProfilerFront(connection) {
-  EventEmitter.decorate(this);
-
-  this._request = connection._request;
-  this.pendingConsoleRecordings = connection._pendingConsoleRecordings;
-  this.finishedConsoleRecordings = connection._finishedConsoleRecordings;
-
-  connection.on("profile", (e, args) => this.emit(e, args));
-  connection.on("profileEnd", (e, args) => this.emit(e, args));
-  connection.on("profiler-unexpectedly-stopped", (e, args) => this.emit(e, args));
-}
-
-ProfilerFront.prototype = {
-  /**
-   * Manually begins a recording session.
-   *
-   * @return object
-   *         A promise that is resolved once recording has started.
-   */
-  startRecording: Task.async(function*() {
-    let { isActive, currentTime } = yield this._request("profiler", "isActive");
-
-    // Start the profiler only if it wasn't already active. The built-in
-    // nsIProfiler module will be kept recording, because it's the same instance
-    // for all toolboxes and interacts with the whole platform, so we don't want
-    // to affect other clients by stopping (or restarting) it.
-    if (!isActive) {
-      // Make a copy of the options, because eventually _request wants
-      // to freeze the packet.
-      let localOptions = Cu.cloneInto(this._customProfilerOptions, {});
-      yield this._request("profiler", "startProfiler", localOptions);
-      this._profilingStartTime = 0;
-      this.emit("profiler-activated");
-    } else {
-      this._profilingStartTime = currentTime;
-      this.emit("profiler-already-active");
-    }
-
-    // The framerate actor is target-dependent, so just make sure
-    // it's recording.
-    yield this._request("framerate", "startRecording");
-  }),
-
-  /**
-   * Manually ends the current recording session.
-   *
-   * @return object
-   *         A promise that is resolved once recording has stopped,
-   *         with the profiler and framerate data.
-   */
-  stopRecording: Task.async(function*() {
-    // We'll need to filter out all samples that fall out of current profile's
-    // range. This is necessary because the profiler is continuously running.
-    let profilerData = yield this._request("profiler", "getProfile");
-    filterSamples(profilerData, this._profilingStartTime);
-    offsetSampleTimes(profilerData, this._profilingStartTime);
-
-    // Fetch the recorded refresh driver ticks, during the same time window
-    // as the filtered profiler data.
-    let beginAt = findEarliestSampleTime(profilerData);
-    let endAt = findOldestSampleTime(profilerData);
-    let ticksData = yield this._request("framerate", "getPendingTicks", beginAt, endAt);
-    yield this._request("framerate", "cancelRecording");
-
-    // Join all the aquired data and return it for outside consumers.
-    return {
-      recordingDuration: profilerData.currentTime - this._profilingStartTime,
-      profilerData: profilerData,
-      ticksData: ticksData
-    };
-  }),
-
-  /**
-   * Ends the current recording session without trying to retrieve any data.
-   */
-  cancelRecording: Task.async(function*() {
-    yield this._request("framerate", "cancelRecording");
-  }),
-
-  /**
-   * Overrides the options sent to the built-in profiler module when activating,
-   * such as the maximum entries count, the sampling interval etc.
-   *
-   * Used in tests and for older backend implementations.
-   */
-  _customProfilerOptions: {
-    entries: 1000000,
-    interval: 1,
-    features: ["js"],
-    threadFilters: ["GeckoMain"]
-  }
-};
-
-/**
- * Filters all the samples in the provided profiler data to be more recent
- * than the specified start time.
- *
- * @param object profilerData
- *        The profiler data received from the backend.
- * @param number profilingStartTime
- *        The earliest acceptable sample time (in milliseconds).
- */
-function filterSamples(profilerData, profilingStartTime) {
-  let firstThread = profilerData.profile.threads[0];
-
-  firstThread.samples = firstThread.samples.filter(e => {
-    return e.time >= profilingStartTime;
-  });
-}
-
-/**
- * Offsets all the samples in the provided profiler data by the specified time.
- *
- * @param object profilerData
- *        The profiler data received from the backend.
- * @param number timeOffset
- *        The amount of time to offset by (in milliseconds).
- */
-function offsetSampleTimes(profilerData, timeOffset) {
-  let firstThreadSamples = profilerData.profile.threads[0].samples;
-
-  for (let sample of firstThreadSamples) {
-    sample.time -= timeOffset;
-  }
-}
-
-/**
- * Finds the earliest sample time in the provided profiler data.
- *
- * @param object profilerData
- *        The profiler data received from the backend.
- * @return number
- *         The earliest sample time (in milliseconds).
- */
-function findEarliestSampleTime(profilerData) {
-  let firstThreadSamples = profilerData.profile.threads[0].samples;
-
-  for (let sample of firstThreadSamples) {
-    if ("time" in sample) {