Merge fx-team to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 22 Oct 2015 16:45:45 -0700
changeset 304195 1f03a14106e59280761ac53904340f389674337f
parent 304161 3888eea6aaf2329e5f5f44fa2b56346627ebdc7e (current diff)
parent 304194 0e104df4b5e921276668c761b3d8922e8dd5d614 (diff)
child 304241 5a6ef7c09c12ef427fc0ce5faedf26c4e63da1f7
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to central, a=merge
toolkit/themes/linux/mozapps/extensions/extensionGeneric.png
toolkit/themes/osx/mozapps/extensions/alerticon-error.png
toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png
toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png
toolkit/themes/osx/mozapps/extensions/alerticon-warning.png
toolkit/themes/osx/mozapps/extensions/extensionGeneric.png
toolkit/themes/osx/mozapps/extensions/navigation.png
toolkit/themes/osx/mozapps/extensions/stripes-error.png
toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png
toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png
toolkit/themes/osx/mozapps/extensions/stripes-warning.png
toolkit/themes/windows/mozapps/extensions/alerticon-error.png
toolkit/themes/windows/mozapps/extensions/alerticon-info-negative.png
toolkit/themes/windows/mozapps/extensions/alerticon-info-positive.png
toolkit/themes/windows/mozapps/extensions/alerticon-warning.png
toolkit/themes/windows/mozapps/extensions/extensionGeneric-XP.png
toolkit/themes/windows/mozapps/extensions/extensionGeneric.png
toolkit/themes/windows/mozapps/extensions/stripes-error.png
toolkit/themes/windows/mozapps/extensions/stripes-info-negative.png
toolkit/themes/windows/mozapps/extensions/stripes-info-positive.png
toolkit/themes/windows/mozapps/extensions/stripes-warning.png
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -362,16 +362,22 @@ Site.prototype = {
     }
     else if (target.parentElement.classList.contains("suggested-explain")) {
       action = "suggested_link";
     }
     // Only handle primary clicks for the remaining targets
     else if (button == 0) {
       aEvent.preventDefault();
       if (target.classList.contains("newtab-control-block")) {
+        // Notify DirectoryLinksProvider of suggested tile block, this may
+        // affect if and how suggested tiles are recommended and needs to
+        // be reported before pages are updated inside block() call
+        if (this.link.targetedSite) {
+          DirectoryLinksProvider.handleSuggestedTileBlock();
+        }
         this.block();
         action = "block";
       }
       else if (target.classList.contains("sponsored-explain") ||
                target.classList.contains("newtab-sponsored")) {
         this._toggleLegalText(".newtab-sponsored", ".sponsored-explain");
         action = "sponsored";
       }
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -1,13 +1,20 @@
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
+XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
+                                  "resource://gre/modules/MatchPattern.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+                                  "resource://gre/modules/Services.jsm");
+
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
+
 var {
   EventManager,
   ignoreEvent,
   runSafe,
 } = ExtensionUtils;
 
 // This function is pretty tightly tied to Extension.jsm.
 // Its job is to fill in the |tab| property of the sender.
@@ -376,18 +383,23 @@ extensions.registerAPI((extension, conte
         return self.tabs.query({windowId: WindowManager.getId(window)}, callback);
       },
 
       query: function(queryInfo, callback) {
         if (!queryInfo) {
           queryInfo = {};
         }
 
+        let pattern = null;
+        if (queryInfo.url) {
+          pattern = new MatchPattern(queryInfo.url);
+        }
+
         function matches(window, tab) {
-          let props = ["active", "pinned", "highlighted", "status", "title", "url", "index"];
+          let props = ["active", "pinned", "highlighted", "status", "title", "index"];
           for (let prop of props) {
             if (prop in queryInfo && queryInfo[prop] != tab[prop]) {
               return false;
             }
           }
 
           let lastFocused = window == WindowManager.topWindow;
           if ("lastFocusedWindow" in queryInfo && queryInfo.lastFocusedWindow != lastFocused) {
@@ -413,16 +425,20 @@ extensions.registerAPI((extension, conte
 
           if ("currentWindow" in queryInfo) {
             let eq = window == currentWindow(context);
             if (queryInfo.currentWindow != eq) {
               return false;
             }
           }
 
+          if (pattern && !pattern.matches(Services.io.newURI(tab.url, null, null))) {
+            return false;
+          }
+
           return true;
         }
 
         let result = [];
         let e = Services.wm.getEnumerator("navigator:browser");
         while (e.hasMoreElements()) {
           let window = e.getNext();
           if (window.document.readyState != "complete") {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_query.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_query.js
@@ -40,9 +40,93 @@ add_task(function* () {
   });
 
   yield extension.startup();
   yield extension.awaitFinish("tabs.query");
   yield extension.unload();
 
   yield BrowserTestUtils.removeTab(tab1);
   yield BrowserTestUtils.removeTab(tab2);
+
+  tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+  tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
+  let tab3 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "http://test1.example.org/MochiKit/");
+
+  // test simple queries
+  extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"]
+    },
+
+    background: function() {
+      browser.tabs.query({
+        url: "<all_urls>"
+      }, function(tabs) {
+        browser.test.assertEq(tabs.length, 3, "should have three tabs");
+
+        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+
+        browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
+        browser.test.assertEq(tabs[1].url, "http://example.net/", "tab 1 url correct");
+        browser.test.assertEq(tabs[2].url, "http://test1.example.org/MochiKit/", "tab 2 url correct");
+
+        browser.test.notifyPass("tabs.query");
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("tabs.query");
+  yield extension.unload();
+
+  // match pattern
+  extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"]
+    },
+
+    background: function() {
+      browser.tabs.query({
+        url: "http://*/MochiKit*"
+      }, function(tabs) {
+        browser.test.assertEq(tabs.length, 1, "should have one tab");
+
+        browser.test.assertEq(tabs[0].url, "http://test1.example.org/MochiKit/", "tab 0 url correct");
+
+        browser.test.notifyPass("tabs.query");
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("tabs.query");
+  yield extension.unload();
+
+  // match array of patterns
+  extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"]
+    },
+
+    background: function() {
+      browser.tabs.query({
+        url: ["http://*/MochiKit*", "http://*.com/*"]
+      }, function(tabs) {
+        browser.test.assertEq(tabs.length, 2, "should have two tabs");
+
+        tabs.sort(function (tab1, tab2) { return tab1.index - tab2.index; });
+
+        browser.test.assertEq(tabs[0].url, "http://example.com/", "tab 0 url correct");
+        browser.test.assertEq(tabs[1].url, "http://test1.example.org/MochiKit/", "tab 1 url correct");
+
+        browser.test.notifyPass("tabs.query");
+      });
+    },
+  });
+
+  yield extension.startup();
+  yield extension.awaitFinish("tabs.query");
+  yield extension.unload();
+
+  yield BrowserTestUtils.removeTab(tab1);
+  yield BrowserTestUtils.removeTab(tab2);
+  yield BrowserTestUtils.removeTab(tab3);
 });
--- a/browser/components/loop/.eslintrc
+++ b/browser/components/loop/.eslintrc
@@ -29,53 +29,58 @@
   },
   "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/>
+    "array-bracket-spacing": [2, "never"],
     "block-spacing": [2, "always"],
     "callback-return": 0,         // TBD
     "camelcase": 0,               // TODO: set to 2
     "comma-spacing": 2,
+    "comma-style": 2,
     "computed-property-spacing": [2, "never"],
-    "consistent-return": 0,       // TODO: set to 2
+    "consistent-return": 2,
     "curly": [2, "all"],
     "dot-location": [2, "property"],
     "eol-last": 2,
     "eqeqeq": [2, "smart"],
     "jsx-quotes": [2, "prefer-double"],
     "key-spacing": [2, {"beforeColon": false, "afterColon": true }],
     "linebreak-style": [2, "unix"],
     "new-cap": 0,                 // TODO: set to 2
     "new-parens": 2,
     "no-alert": 2,
     "no-array-constructor": 2,
     "no-caller": 2,
-    "no-catch-shadow": 0,         // TODO: set to 2
+    "no-catch-shadow": 2,
     "no-class-assign": 2,
     "no-const-assign": 2,
     "no-console": 0,              // Leave as 0. We use console logging in content code.
     "no-empty": 2,
     "no-empty-label": 2,
     "no-eval": 2,
     "no-extend-native": 2, // XXX
     "no-extra-bind": 0,           // Leave as 0
     "no-extra-parens": 0,         // TODO: (bug?) [2, "functions"],
+    "no-extra-semi": 2,
     "no-implied-eval": 2,
     "no-invalid-this": 0,         // TBD
     "no-iterator": 2,
     "no-label-var": 2,
     "no-labels": 2,
     "no-lone-blocks": 2,
     "no-loop-func": 2,
-    "no-multi-spaces": 0,         // TBD.
+    "no-mixed-spaces-and-tabs": 2,
+    "no-multi-spaces": 2,
     "no-multi-str": 2,
+    "no-multiple-empty-lines": 2,
     "no-native-reassign": 2,
     "no-new": 2,
     "no-new-func": 2,
     "no-new-object": 2,
     "no-new-wrappers": 2,
     "no-octal-escape": 2,
     "no-process-exit": 2,
     "no-proto": 2,
@@ -90,43 +95,46 @@
     "no-underscore-dangle": 0,    // Leave as 0. Commonly used for private variables.
     "no-unexpected-multiline": 2,
     "no-unneeded-ternary": 2,
     "no-unused-expressions": 0,   // TODO: Set to 2
     "no-unused-vars": 0,          // TODO: Set to 2
     "no-use-before-define": 0,    // TODO: Set to 2
     "no-useless-call": 2,
     "no-with": 2,
-    "object-curly-spacing": 0,    // [2, "always"],
+    "object-curly-spacing": [2, "always"],
+    "operator-assignment": [2, "always"],
     "quotes": [2, "double", "avoid-escape"],
     "semi": 2,
     "semi-spacing": [2, {"before": false, "after": true}],
+    "space-after-keywords": 2,
+    "space-before-blocks": 2,
+    "space-before-function-paren": [2, "never"],
+    "space-before-keywords": 2,
     "space-infix-ops": 2,
+    "space-in-parens": [2, "never"],
     "space-return-throw-case": 2,
     "space-unary-ops": [2, {"words": true, "nonwords": false}],
     "spaced-comment": [2, "always"],
     "strict": [2, "function"],
     "yoda": [2, "never"],
     // eslint-plugin-react rules. These are documented at
     // <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
     "react/jsx-no-undef": 2,
     "react/jsx-sort-props": 2,
     "react/jsx-sort-prop-types": 2,
     "react/jsx-uses-vars": 2,
     "react/jsx-no-duplicate-props": 2,
-    // Need to fix the couple of instances which don't
-    // currently pass this rule.
-    "react/no-did-mount-set-state": 0,
+    "react/no-did-mount-set-state": 2,
     "react/no-did-update-set-state": 2,
     "react/no-unknown-property": 2,
     "react/prop-types": 2,
     "react/self-closing-comp": 2,
     "react/wrap-multilines": 2,
-    // We would probably want to go with a variant of never.
-    "react/jsx-curly-spacing": 0,
+    "react/jsx-curly-spacing": [2, "never"],
     // Not worth it: React is defined globally
     "react/jsx-uses-react": 0,
     "react/react-in-jsx-scope": 0,
     // These ones we don't want to ever enable
     "react/display-name": 0,
     "react/jsx-boolean-value": 0,
     "react/no-danger": 0,
     "react/no-multi-comp": 0
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -62,17 +62,17 @@ loop.conversation = (function(mozL10n) {
       this.closeWindow();
     },
 
     render: function() {
       if (this.state.showFeedbackForm) {
         return this._renderFeedbackForm();
       }
 
-      switch(this.state.windowType) {
+      switch (this.state.windowType) {
         case "room": {
           return (React.createElement(DesktopRoomConversationView, {
             chatWindowDetached: this.state.chatWindowDetached, 
             dispatcher: this.props.dispatcher, 
             mozLoop: this.props.mozLoop, 
             onCallTerminated: this.handleCallTerminated, 
             roomStore: this.props.roomStore}));
         }
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -62,17 +62,17 @@ loop.conversation = (function(mozL10n) {
       this.closeWindow();
     },
 
     render: function() {
       if (this.state.showFeedbackForm) {
         return this._renderFeedbackForm();
       }
 
-      switch(this.state.windowType) {
+      switch (this.state.windowType) {
         case "room": {
           return (<DesktopRoomConversationView
             chatWindowDetached={this.state.chatWindowDetached}
             dispatcher={this.props.dispatcher}
             mozLoop={this.props.mozLoop}
             onCallTerminated={this.handleCallTerminated}
             roomStore={this.props.roomStore} />);
         }
--- a/browser/components/loop/content/js/conversationAppStore.js
+++ b/browser/components/loop/content/js/conversationAppStore.js
@@ -102,24 +102,24 @@ loop.store.ConversationAppStore = (funct
      *
      * @param {sharedActions.GetWindowData} actionData The action data
      */
     getWindowData: function(actionData) {
       var windowData = this._mozLoop.getConversationWindowData(actionData.windowId);
 
       if (!windowData) {
         console.error("Failed to get the window data");
-        this.setStoreState({windowType: "failed"});
+        this.setStoreState({ windowType: "failed" });
         return;
       }
 
-      this.setStoreState({windowType: windowData.type});
+      this.setStoreState({ windowType: windowData.type });
 
       this._dispatcher.dispatch(new loop.shared.actions.SetupWindowData(_.extend({
-        windowId: actionData.windowId}, windowData)));
+        windowId: actionData.windowId }, windowData)));
     },
 
     /**
      * Event handler; invoked when the 'unload' event is dispatched from the
      * window object.
      * It will dispatch a 'WindowUnload' action that other stores may listen to
      * and will remove all event handlers attached to the window object.
      */
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -137,17 +137,17 @@ loop.panel = (function(_, mozL10n) {
       return (
         React.createElement("div", {className: "powered-by-wrapper", id: "powered-by-wrapper"}, 
           React.createElement("p", {className: "powered-by", id: "powered-by"}, 
             mozL10n.get("powered_by_beforeLogo"), 
             React.createElement("span", {className: locale, id: "powered-by-logo"}), 
             mozL10n.get("powered_by_afterLogo")
           ), 
           React.createElement("p", {className: "terms-service", 
-             dangerouslySetInnerHTML: {__html: tosHTML}, 
+             dangerouslySetInnerHTML: { __html: tosHTML}, 
              onClick: this.handleLinkClick})
          )
       );
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
@@ -156,17 +156,17 @@ loop.panel = (function(_, mozL10n) {
     propTypes: {
       displayed: React.PropTypes.bool,
       extraCSSClass: React.PropTypes.string,
       label: React.PropTypes.string.isRequired,
       onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
-      return {displayed: true};
+      return { displayed: true };
     },
 
     render: function() {
       var cx = React.addons.classSet;
 
       if (!this.props.displayed) {
         return null;
       }
@@ -252,17 +252,17 @@ loop.panel = (function(_, mozL10n) {
                                                                  "settings_menu_item_turnnotificationsoff";
 
       return (
         React.createElement("div", {className: "settings-menu dropdown"}, 
           React.createElement("button", {className: "button-settings", 
              onClick: this.toggleDropdownMenu, 
              ref: "menu-button", 
              title: mozL10n.get("settings_menu_button_tooltip")}), 
-          React.createElement("ul", {className: cx({"dropdown-menu": true, hide: !this.state.showMenu})}, 
+          React.createElement("ul", {className: cx({ "dropdown-menu": true, hide: !this.state.showMenu })}, 
             React.createElement(SettingsDropdownEntry, {
                 extraCSSClass: "entry-settings-notifications entries-divider", 
                 label: mozL10n.get(notificationsLabel), 
                 onClick: this.handleToggleNotifications}), 
             React.createElement(SettingsDropdownEntry, {
                 displayed: this._isSignedIn() && this.props.mozLoop.fxAEnabled, 
                 extraCSSClass: "entry-settings-account", 
                 label: mozL10n.get("settings_menu_item_account"), 
@@ -854,19 +854,19 @@ loop.panel = (function(_, mozL10n) {
     },
 
     _onStatusChanged: function() {
       var profile = this.props.mozLoop.userProfile;
       var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
       var newUid = profile ? profile.uid : null;
       if (currUid === newUid) {
         // Update the state of hasEncryptionKey as this might have changed now.
-        this.setState({hasEncryptionKey: this.props.mozLoop.hasEncryptionKey});
+        this.setState({ hasEncryptionKey: this.props.mozLoop.hasEncryptionKey });
       } else {
-        this.setState({userProfile: profile});
+        this.setState({ userProfile: profile });
       }
       this.updateServiceErrors();
     },
 
     _gettingStartedSeen: function() {
       this.setState({
         gettingStartedSeen: this.props.mozLoop.getLoopPref("gettingStarted.seen")
       });
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -137,17 +137,17 @@ loop.panel = (function(_, mozL10n) {
       return (
         <div className="powered-by-wrapper" id="powered-by-wrapper">
           <p className="powered-by" id="powered-by">
             {mozL10n.get("powered_by_beforeLogo")}
             <span className={locale} id="powered-by-logo"/>
             {mozL10n.get("powered_by_afterLogo")}
           </p>
           <p className="terms-service"
-             dangerouslySetInnerHTML={{__html: tosHTML}}
+             dangerouslySetInnerHTML={{ __html: tosHTML }}
              onClick={this.handleLinkClick}></p>
          </div>
       );
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
@@ -156,17 +156,17 @@ loop.panel = (function(_, mozL10n) {
     propTypes: {
       displayed: React.PropTypes.bool,
       extraCSSClass: React.PropTypes.string,
       label: React.PropTypes.string.isRequired,
       onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
-      return {displayed: true};
+      return { displayed: true };
     },
 
     render: function() {
       var cx = React.addons.classSet;
 
       if (!this.props.displayed) {
         return null;
       }
@@ -252,17 +252,17 @@ loop.panel = (function(_, mozL10n) {
                                                                  "settings_menu_item_turnnotificationsoff";
 
       return (
         <div className="settings-menu dropdown">
           <button className="button-settings"
              onClick={this.toggleDropdownMenu}
              ref="menu-button"
              title={mozL10n.get("settings_menu_button_tooltip")} />
-          <ul className={cx({"dropdown-menu": true, hide: !this.state.showMenu})}>
+          <ul className={cx({ "dropdown-menu": true, hide: !this.state.showMenu })}>
             <SettingsDropdownEntry
                 extraCSSClass="entry-settings-notifications entries-divider"
                 label={mozL10n.get(notificationsLabel)}
                 onClick={this.handleToggleNotifications} />
             <SettingsDropdownEntry
                 displayed={this._isSignedIn() && this.props.mozLoop.fxAEnabled}
                 extraCSSClass="entry-settings-account"
                 label={mozL10n.get("settings_menu_item_account")}
@@ -854,19 +854,19 @@ loop.panel = (function(_, mozL10n) {
     },
 
     _onStatusChanged: function() {
       var profile = this.props.mozLoop.userProfile;
       var currUid = this.state.userProfile ? this.state.userProfile.uid : null;
       var newUid = profile ? profile.uid : null;
       if (currUid === newUid) {
         // Update the state of hasEncryptionKey as this might have changed now.
-        this.setState({hasEncryptionKey: this.props.mozLoop.hasEncryptionKey});
+        this.setState({ hasEncryptionKey: this.props.mozLoop.hasEncryptionKey });
       } else {
-        this.setState({userProfile: profile});
+        this.setState({ userProfile: profile });
       }
       this.updateServiceErrors();
     },
 
     _gettingStartedSeen: function() {
       this.setState({
         gettingStartedSeen: this.props.mozLoop.getLoopPref("gettingStarted.seen")
       });
--- a/browser/components/loop/content/js/roomStore.js
+++ b/browser/components/loop/content/js/roomStore.js
@@ -130,17 +130,17 @@ loop.store = loop.store || {};
       this._mozLoop.rooms.on("delete", this._onRoomRemoved.bind(this));
       this._mozLoop.rooms.on("refresh", this._onRoomsRefresh.bind(this));
     },
 
     /**
      * Updates active room store state.
      */
     _onActiveRoomStoreChange: function() {
-      this.setStoreState({activeRoom: this.activeRoomStore.getStoreState()});
+      this.setStoreState({ activeRoom: this.activeRoomStore.getStoreState() });
     },
 
     /**
      * Updates current room list when a new room is available.
      *
      * @param {String} eventName     The event name (unused).
      * @param {Object} addedRoomData The added room data.
      */
@@ -272,17 +272,17 @@ loop.store = loop.store || {};
       }
 
       this._notifications.remove("create-room-error");
 
       this._mozLoop.rooms.create(roomCreationData, function(err, createdRoom) {
         var buckets = this._mozLoop.ROOM_CREATE;
         if (err) {
           this._mozLoop.telemetryAddValue("LOOP_ROOM_CREATE", buckets.CREATE_FAIL);
-          this.dispatchAction(new sharedActions.CreateRoomError({error: err}));
+          this.dispatchAction(new sharedActions.CreateRoomError({ error: err }));
           return;
         }
 
         this.dispatchAction(new sharedActions.CreatedRoom({
           roomToken: createdRoom.roomToken
         }));
         this._mozLoop.telemetryAddValue("LOOP_ROOM_CREATE", buckets.CREATE_SUCCESS);
 
@@ -296,17 +296,17 @@ loop.store = loop.store || {};
         }
       }.bind(this));
     },
 
     /**
      * Executed when a room has been created
      */
     createdRoom: function(actionData) {
-      this.setStoreState({pendingCreation: false});
+      this.setStoreState({ pendingCreation: false });
 
       // Opens the newly created room
       this.dispatchAction(new sharedActions.OpenRoom({
         roomToken: actionData.roomToken
       }));
     },
 
     /**
@@ -401,62 +401,62 @@ loop.store = loop.store || {};
      * Creates a new room.
      *
      * @param {sharedActions.DeleteRoom} actionData The action data.
      */
     deleteRoom: function(actionData) {
       this._mozLoop.rooms.delete(actionData.roomToken, function(err) {
         var buckets = this._mozLoop.ROOM_DELETE;
         if (err) {
-          this.dispatchAction(new sharedActions.DeleteRoomError({error: err}));
+          this.dispatchAction(new sharedActions.DeleteRoomError({ error: err }));
         }
         this._mozLoop.telemetryAddValue("LOOP_ROOM_DELETE", buckets[err ?
           "DELETE_FAIL" : "DELETE_SUCCESS"]);
       }.bind(this));
     },
 
     /**
      * Executed when a room deletion error occurs.
      *
      * @param {sharedActions.DeleteRoomError} actionData The action data.
      */
     deleteRoomError: function(actionData) {
-      this.setStoreState({error: actionData.error});
+      this.setStoreState({ error: actionData.error });
     },
 
     /**
      * Gather the list of all available rooms from the MozLoop API.
      */
     getAllRooms: function() {
       this._mozLoop.rooms.getAll(null, function(err, rawRoomList) {
         var action;
 
-        this.setStoreState({pendingInitialRetrieval: false});
+        this.setStoreState({ pendingInitialRetrieval: false });
 
         if (err) {
-          action = new sharedActions.GetAllRoomsError({error: err});
+          action = new sharedActions.GetAllRoomsError({ error: err });
         } else {
-          action = new sharedActions.UpdateRoomList({roomList: rawRoomList});
+          action = new sharedActions.UpdateRoomList({ roomList: rawRoomList });
         }
 
         this.dispatchAction(action);
 
         // We can only start listening to room events after getAll() has been
         // called executed first.
         this.startListeningToRoomEvents();
       }.bind(this));
     },
 
     /**
      * Updates current error state in case getAllRooms failed.
      *
      * @param {sharedActions.GetAllRoomsError} actionData The action data.
      */
     getAllRoomsError: function(actionData) {
-      this.setStoreState({error: actionData.error});
+      this.setStoreState({ error: actionData.error });
     },
 
     /**
      * Updates current room list.
      *
      * @param {sharedActions.UpdateRoomList} actionData The action data.
      */
     updateRoomList: function(actionData) {
@@ -512,17 +512,17 @@ loop.store = loop.store || {};
         // 2) a new URL is provided as of now,
         // 3) the URL data has changed.
         var diff = loop.shared.utils.objectDiff(oldRoomURL, newRoomURL);
         if (diff.added.length || diff.updated.length) {
           newRoomURL = _.extend(oldRoomURL || {}, newRoomURL);
           var isValidURL = false;
           try {
             isValidURL = new URL(newRoomURL.location);
-          } catch(ex) {
+          } catch (ex) {
             // URL may throw, default to false;
           }
           if (isValidURL) {
             roomData.urls = [newRoomURL];
           }
         }
         // TODO: there currently is no clear UX defined on what to do when all
         // context data was cleared, e.g. when diff.removed contains all the
@@ -536,17 +536,17 @@ loop.store = loop.store || {};
           setTimeout(function() {
             this.dispatchAction(new sharedActions.UpdateRoomContextDone());
           }.bind(this), 0);
           return;
         }
 
         var hadContextBefore = !!oldRoomURL;
 
-        this.setStoreState({error: null});
+        this.setStoreState({ error: null });
         this._mozLoop.rooms.update(actionData.roomToken, roomData,
           function(error, data) {
             var action = error ?
               new sharedActions.UpdateRoomContextError({ error: error }) :
               new sharedActions.UpdateRoomContextDone();
             this.dispatchAction(action);
 
             if (!err && !hadContextBefore) {
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -47,26 +47,26 @@ loop.roomViews = (function(mozL10n) {
       }
     },
 
     _onRoomError: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
-        this.setState({error: this.props.roomStore.getStoreState("error")});
+        this.setState({ error: this.props.roomStore.getStoreState("error") });
       }
     },
 
     _onRoomSavingContext: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
-        this.setState({savingContext: this.props.roomStore.getStoreState("savingContext")});
+        this.setState({ savingContext: this.props.roomStore.getStoreState("savingContext") });
       }
     },
 
     getInitialState: function() {
       var storeState = this.props.roomStore.getStoreState("activeRoom");
       return _.extend({
         // Used by the UI showcase.
         roomState: this.props.roomState || storeState.roomState,
@@ -112,17 +112,17 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
    */
   var RoomFailureView = React.createClass({displayName: "RoomFailureView",
-    mixins: [ sharedMixins.AudioMixin ],
+    mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       failureReason: React.PropTypes.string,
       mozLoop: React.PropTypes.object.isRequired
     },
 
     componentDidMount: function() {
@@ -279,26 +279,26 @@ loop.roomViews = (function(mozL10n) {
     handleCopyButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
         roomUrl: this.props.roomData.roomUrl,
         from: "conversation"
       }));
 
-      this.setState({copiedUrl: true});
+      this.setState({ copiedUrl: true });
       setTimeout(this.resetTriggeredButtons, this.constructor.TRIGGERED_RESET_DELAY);
     },
 
     /**
      * Reset state of triggered buttons if necessary
      */
     resetTriggeredButtons: function() {
       if (this.state.copiedUrl) {
-        this.setState({copiedUrl: false});
+        this.setState({ copiedUrl: false });
       }
     },
 
     handleShareButtonClick: function(event) {
       event.preventDefault();
 
       var providers = this.props.socialShareProviders;
       // If there are no providers available currently, save a click by dispatching
@@ -321,17 +321,17 @@ loop.roomViews = (function(mozL10n) {
       if (!this.props.show) {
         return null;
       }
 
       var cx = React.addons.classSet;
       return (
         React.createElement("div", {className: "room-invitation-overlay"}, 
           React.createElement("div", {className: "room-invitation-content"}, 
-            React.createElement("p", {className: cx({hide: this.props.showEditContext})}, 
+            React.createElement("p", {className: cx({ hide: this.props.showEditContext })}, 
               mozL10n.get("invite_header_text2")
             )
           ), 
           React.createElement("div", {className: cx({
             "btn-group": true,
             "call-action-group": true,
             hide: this.props.showEditContext
           })}, 
@@ -537,18 +537,18 @@ loop.roomViews = (function(mozL10n) {
       var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
       var urlDescription = url && url.description || "";
       var location = url && url.location || "";
 
       var cx = React.addons.classSet;
       var availableContext = this.state.availableContext;
       return (
         React.createElement("div", {className: "room-context"}, 
-          React.createElement("p", {className: cx({"error": !!this.props.error,
-                            "error-display-area": true})}, 
+          React.createElement("p", {className: cx({ "error": !!this.props.error,
+                            "error-display-area": true })}, 
             mozL10n.get("rooms_change_failed_label")
           ), 
           React.createElement("h2", {className: "room-context-header"}, mozL10n.get("context_inroom_header")), 
           React.createElement("form", {onSubmit: this.handleFormSubmit}, 
             React.createElement("input", {className: "room-context-name", 
               maxLength: this.maxRoomNameLength, 
               onKeyDown: this.handleTextareaKeyDown, 
               placeholder: mozL10n.get("context_edit_name_placeholder"), 
@@ -669,17 +669,17 @@ loop.roomViews = (function(mozL10n) {
      * room state and other flags.
      *
      * @return {Boolean} True if remote video should be rended.
      *
      * XXX Refactor shouldRenderRemoteVideo & shouldRenderLoading into one fn
      *     that returns an enum
      */
     shouldRenderRemoteVideo: function() {
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.HAS_PARTICIPANTS:
           if (this.state.remoteVideoEnabled) {
             return true;
           }
 
           if (this.state.mediaConnected) {
             // since the remoteVideo hasn't yet been enabled, if the
             // media is connected, then we should be displaying an avatar.
@@ -711,17 +711,17 @@ loop.roomViews = (function(mozL10n) {
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a local
      * stream is on its way from the camera?
      *
      * @returns {boolean}
      * @private
      */
-    _isLocalLoading: function () {
+    _isLocalLoading: function() {
       return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
              !this.state.localSrcMediaElement;
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
@@ -763,17 +763,17 @@ loop.roomViews = (function(mozL10n) {
         state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
         visible: true
       };
 
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return (
             React.createElement(RoomFailureView, {
               dispatcher: this.props.dispatcher, 
               failureReason: this.state.failureReason, 
@@ -810,26 +810,26 @@ loop.roomViews = (function(mozL10n) {
                 remotePosterUrl: this.props.remotePosterUrl, 
                 remoteSrcMediaElement: this.state.remoteSrcMediaElement, 
                 renderRemoteVideo: this.shouldRenderRemoteVideo(), 
                 screenShareMediaElement: this.state.screenShareMediaElement, 
                 screenSharePosterUrl: null, 
                 showContextRoomName: false, 
                 useDesktopPaths: true}, 
                 React.createElement(sharedViews.ConversationToolbar, {
-                  audio: {enabled: !this.state.audioMuted, visible: true}, 
+                  audio: { enabled: !this.state.audioMuted, visible: true}, 
                   dispatcher: this.props.dispatcher, 
                   hangup: this.leaveRoom, 
                   mozLoop: this.props.mozLoop, 
                   publishStream: this.publishStream, 
                   screenShare: screenShareData, 
                   settingsMenuItems: settingsMenuItems, 
                   show: !shouldRenderEditContextView, 
                   showHangup: this.props.chatWindowDetached, 
-                  video: {enabled: !this.state.videoMuted, visible: true}}), 
+                  video: { enabled: !this.state.videoMuted, visible: true}}), 
                 React.createElement(DesktopRoomInvitationView, {
                   dispatcher: this.props.dispatcher, 
                   error: this.state.error, 
                   mozLoop: this.props.mozLoop, 
                   onAddContextClick: this.handleAddContextClick, 
                   onEditContextClose: this.handleEditContextClose, 
                   roomData: roomData, 
                   savingContext: this.state.savingContext, 
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -47,26 +47,26 @@ loop.roomViews = (function(mozL10n) {
       }
     },
 
     _onRoomError: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
-        this.setState({error: this.props.roomStore.getStoreState("error")});
+        this.setState({ error: this.props.roomStore.getStoreState("error") });
       }
     },
 
     _onRoomSavingContext: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
-        this.setState({savingContext: this.props.roomStore.getStoreState("savingContext")});
+        this.setState({ savingContext: this.props.roomStore.getStoreState("savingContext") });
       }
     },
 
     getInitialState: function() {
       var storeState = this.props.roomStore.getStoreState("activeRoom");
       return _.extend({
         // Used by the UI showcase.
         roomState: this.props.roomState || storeState.roomState,
@@ -112,17 +112,17 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
    */
   var RoomFailureView = React.createClass({
-    mixins: [ sharedMixins.AudioMixin ],
+    mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       failureReason: React.PropTypes.string,
       mozLoop: React.PropTypes.object.isRequired
     },
 
     componentDidMount: function() {
@@ -279,26 +279,26 @@ loop.roomViews = (function(mozL10n) {
     handleCopyButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
         roomUrl: this.props.roomData.roomUrl,
         from: "conversation"
       }));
 
-      this.setState({copiedUrl: true});
+      this.setState({ copiedUrl: true });
       setTimeout(this.resetTriggeredButtons, this.constructor.TRIGGERED_RESET_DELAY);
     },
 
     /**
      * Reset state of triggered buttons if necessary
      */
     resetTriggeredButtons: function() {
       if (this.state.copiedUrl) {
-        this.setState({copiedUrl: false});
+        this.setState({ copiedUrl: false });
       }
     },
 
     handleShareButtonClick: function(event) {
       event.preventDefault();
 
       var providers = this.props.socialShareProviders;
       // If there are no providers available currently, save a click by dispatching
@@ -321,17 +321,17 @@ loop.roomViews = (function(mozL10n) {
       if (!this.props.show) {
         return null;
       }
 
       var cx = React.addons.classSet;
       return (
         <div className="room-invitation-overlay">
           <div className="room-invitation-content">
-            <p className={cx({hide: this.props.showEditContext})}>
+            <p className={cx({ hide: this.props.showEditContext })}>
               {mozL10n.get("invite_header_text2")}
             </p>
           </div>
           <div className={cx({
             "btn-group": true,
             "call-action-group": true,
             hide: this.props.showEditContext
           })}>
@@ -537,18 +537,18 @@ loop.roomViews = (function(mozL10n) {
       var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
       var urlDescription = url && url.description || "";
       var location = url && url.location || "";
 
       var cx = React.addons.classSet;
       var availableContext = this.state.availableContext;
       return (
         <div className="room-context">
-          <p className={cx({"error": !!this.props.error,
-                            "error-display-area": true})}>
+          <p className={cx({ "error": !!this.props.error,
+                            "error-display-area": true })}>
             {mozL10n.get("rooms_change_failed_label")}
           </p>
           <h2 className="room-context-header">{mozL10n.get("context_inroom_header")}</h2>
           <form onSubmit={this.handleFormSubmit}>
             <input className="room-context-name"
               maxLength={this.maxRoomNameLength}
               onKeyDown={this.handleTextareaKeyDown}
               placeholder={mozL10n.get("context_edit_name_placeholder")}
@@ -669,17 +669,17 @@ loop.roomViews = (function(mozL10n) {
      * room state and other flags.
      *
      * @return {Boolean} True if remote video should be rended.
      *
      * XXX Refactor shouldRenderRemoteVideo & shouldRenderLoading into one fn
      *     that returns an enum
      */
     shouldRenderRemoteVideo: function() {
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.HAS_PARTICIPANTS:
           if (this.state.remoteVideoEnabled) {
             return true;
           }
 
           if (this.state.mediaConnected) {
             // since the remoteVideo hasn't yet been enabled, if the
             // media is connected, then we should be displaying an avatar.
@@ -711,17 +711,17 @@ loop.roomViews = (function(mozL10n) {
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a local
      * stream is on its way from the camera?
      *
      * @returns {boolean}
      * @private
      */
-    _isLocalLoading: function () {
+    _isLocalLoading: function() {
       return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
              !this.state.localSrcMediaElement;
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
@@ -763,17 +763,17 @@ loop.roomViews = (function(mozL10n) {
         state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
         visible: true
       };
 
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return (
             <RoomFailureView
               dispatcher={this.props.dispatcher}
               failureReason={this.state.failureReason}
@@ -810,26 +810,26 @@ loop.roomViews = (function(mozL10n) {
                 remotePosterUrl={this.props.remotePosterUrl}
                 remoteSrcMediaElement={this.state.remoteSrcMediaElement}
                 renderRemoteVideo={this.shouldRenderRemoteVideo()}
                 screenShareMediaElement={this.state.screenShareMediaElement}
                 screenSharePosterUrl={null}
                 showContextRoomName={false}
                 useDesktopPaths={true}>
                 <sharedViews.ConversationToolbar
-                  audio={{enabled: !this.state.audioMuted, visible: true}}
+                  audio={{ enabled: !this.state.audioMuted, visible: true }}
                   dispatcher={this.props.dispatcher}
                   hangup={this.leaveRoom}
                   mozLoop={this.props.mozLoop}
                   publishStream={this.publishStream}
                   screenShare={screenShareData}
                   settingsMenuItems={settingsMenuItems}
                   show={!shouldRenderEditContextView}
                   showHangup={this.props.chatWindowDetached}
-                  video={{enabled: !this.state.videoMuted, visible: true}} />
+                  video={{ enabled: !this.state.videoMuted, visible: true }} />
                 <DesktopRoomInvitationView
                   dispatcher={this.props.dispatcher}
                   error={this.state.error}
                   mozLoop={this.props.mozLoop}
                   onAddContextClick={this.handleAddContextClick}
                   onEditContextClose={this.handleEditContextClose}
                   roomData={roomData}
                   savingContext={this.state.savingContext}
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -345,17 +345,17 @@ loop.store.ActiveRoomStore = (function()
      * @param {sharedActions.FetchServerData} actionData
      * @return {Promise} For testing purposes, returns a promise that is resolved
      *                   once data is received from the server, and it is determined
      *                   if Firefox handles the room or not.
      */
     fetchServerData: function(actionData) {
       if (actionData.windowType !== "room") {
         // Nothing for us to do here, leave it to other stores.
-        return;
+        return Promise.resolve();
       }
 
       this.setStoreState({
         roomState: ROOM_STATES.GATHER,
         roomToken: actionData.token,
         standalone: true
       });
 
@@ -591,17 +591,17 @@ loop.store.ActiveRoomStore = (function()
     _checkDevicesAndJoinRoom: function() {
       // XXX Ideally we'd do this check before joining a room, but we're waiting
       // for the UX for that. See bug 1166824. In the meantime this gives us
       // additional information for analysis.
       loop.shared.utils.hasAudioOrVideoDevices(function(hasDevices) {
         if (hasDevices) {
           // MEDIA_WAIT causes the views to dispatch sharedActions.SetupStreamElements,
           // which in turn starts the sdk obtaining the device permission.
-          this.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+          this.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
         } else {
           this.dispatchAction(new sharedActions.ConnectionFailure({
             reason: FAILURE_DETAILS.NO_MEDIA
           }));
         }
       }.bind(this));
     },
 
@@ -682,17 +682,17 @@ loop.store.ActiveRoomStore = (function()
       this._checkDevicesAndJoinRoom();
     },
 
     /**
      * Handles the action that signifies when media permission has been
      * granted and starts joining the room.
      */
     gotMediaPermission: function() {
-      this.setStoreState({roomState: ROOM_STATES.JOINING});
+      this.setStoreState({ roomState: ROOM_STATES.JOINING });
 
       this._mozLoop.rooms.join(this._storeState.roomToken,
         function(error, responseData) {
           if (error) {
             this.dispatchAction(new sharedActions.RoomFailure({
               error: error,
               // This is an explicit flag to avoid the leave happening if join
               // fails. We can't track it on ROOM_STATES.JOINING as the user
@@ -836,24 +836,24 @@ loop.store.ActiveRoomStore = (function()
         remoteVideoEnabled: actionData.videoEnabled
       });
     },
 
     /**
      * Records when the remote media has been connected.
      */
     mediaConnected: function() {
-      this.setStoreState({mediaConnected: true});
+      this.setStoreState({ mediaConnected: true });
     },
 
     /**
      * Used to note the current screensharing state.
      */
     screenSharingState: function(actionData) {
-      this.setStoreState({screenSharingState: actionData.state});
+      this.setStoreState({ screenSharingState: actionData.state });
 
       this._mozLoop.setScreenShareState(
         this.getStoreState().windowId,
         actionData.state === SCREEN_SHARE_STATES.ACTIVE);
     },
 
     /**
      * Used to note the current state of receiving screenshare data.
@@ -1114,17 +1114,17 @@ loop.store.ActiveRoomStore = (function()
           (this._storeState.roomState === ROOM_STATES.JOINING ||
            this._storeState.roomState === ROOM_STATES.JOINED ||
            this._storeState.roomState === ROOM_STATES.SESSION_CONNECTED ||
            this._storeState.roomState === ROOM_STATES.HAS_PARTICIPANTS)) {
         this._mozLoop.rooms.leave(this._storeState.roomToken,
           this._storeState.sessionToken);
       }
 
-      this.setStoreState({roomState: nextState});
+      this.setStoreState({ roomState: nextState });
     },
 
     /**
      * When feedback is complete, we go back to the ready state, rather than
      * init or gather, as we don't need to get the data from the server again.
      */
     feedbackComplete: function() {
       this.setStoreState({
--- a/browser/components/loop/content/shared/js/crypto.js
+++ b/browser/components/loop/content/shared/js/crypto.js
@@ -63,17 +63,17 @@ var inChrome = typeof Components != "und
    */
   function generateKey() {
     if (!isSupported()) {
       throw new Error("Web Crypto is not supported");
     }
 
     return new Promise(function(resolve, reject) {
       // First get a crypto key.
-      rootObject.crypto.subtle.generateKey({name: ALGORITHM, length: KEY_LENGTH },
+      rootObject.crypto.subtle.generateKey({ name: ALGORITHM, length: KEY_LENGTH },
         // `true` means that the key can be extracted from the CryptoKey object.
         true,
         // Usages for the key.
         ["encrypt", "decrypt"]
       ).then(function(cryptoKey) {
         // Now extract the key in the JSON web key format.
         return rootObject.crypto.subtle.exportKey(KEY_FORMAT, cryptoKey);
       }).then(function(exportedKey) {
@@ -100,17 +100,17 @@ var inChrome = typeof Components != "und
       throw new Error("Web Crypto is not supported");
     }
 
     var iv = new Uint8Array(INITIALIZATION_VECTOR_LENGTH);
 
     return new Promise(function(resolve, reject) {
       // First import the key to a format we can use.
       rootObject.crypto.subtle.importKey(KEY_FORMAT,
-        {k: key, kty: KEY_TYPE},
+        { k: key, kty: KEY_TYPE },
         ALGORITHM,
         // If the key is extractable.
         true,
         // What we're using it for.
         ["encrypt"]
       ).then(function(cryptoKey) {
         // Now we've got the cryptoKey, we can do the actual encryption.
 
@@ -153,17 +153,17 @@ var inChrome = typeof Components != "und
   function decryptBytes(key, encryptedData) {
     if (!isSupported()) {
       throw new Error("Web Crypto is not supported");
     }
 
     return new Promise(function(resolve, reject) {
       // First import the key to a format we can use.
       rootObject.crypto.subtle.importKey(KEY_FORMAT,
-        {k: key, kty: KEY_TYPE},
+        { k: key, kty: KEY_TYPE },
         ALGORITHM,
         // If the key is extractable.
         true,
         // What we're using it for.
         ["decrypt"]
       ).then(function(cryptoKey) {
         // Now we've got the key, start the decryption.
         var splitData = _splitIVandCipherText(encryptedData);
--- a/browser/components/loop/content/shared/js/linkifiedTextView.js
+++ b/browser/components/loop/content/shared/js/linkifiedTextView.js
@@ -99,16 +99,18 @@ loop.shared.views.LinkifiedTextView = (f
 
       if (s) {
         elements.push(s);
       }
 
       return elements;
     },
 
-    render: function () {
-      return ( React.createElement("p", null,  this.parseStringToElements(this.props.rawText) ) );
+    render: function() {
+      return (
+        React.createElement("p", null, this.parseStringToElements(this.props.rawText))
+      );
     }
   });
 
   return LinkifiedTextView;
 
 })(navigator.mozL10n || document.mozL10n);
--- a/browser/components/loop/content/shared/js/linkifiedTextView.jsx
+++ b/browser/components/loop/content/shared/js/linkifiedTextView.jsx
@@ -99,16 +99,18 @@ loop.shared.views.LinkifiedTextView = (f
 
       if (s) {
         elements.push(s);
       }
 
       return elements;
     },
 
-    render: function () {
-      return ( <p>{ this.parseStringToElements(this.props.rawText) }</p> );
+    render: function() {
+      return (
+        <p>{this.parseStringToElements(this.props.rawText)}</p>
+      );
     }
   });
 
   return LinkifiedTextView;
 
 })(navigator.mozL10n || document.mozL10n);
--- a/browser/components/loop/content/shared/js/mixins.js
+++ b/browser/components/loop/content/shared/js/mixins.js
@@ -222,17 +222,17 @@ loop.shared.mixins = (function() {
             // Set the maximum dimensions that the menu DOMNode may grow to. The
             // content overflow style should be defined in CSS.
             menu.style.maxHeight = (boundingRect.height + y) + "px";
             // Since we just adjusted the max-height of the menu - thus its actual
             // height as well - we need to adjust its vertical offset with the same
             // amount.
             y += menuNodeRect.height - (boundingRect.height + y);
           }
-          menu.style.marginTop =  y + "px";
+          menu.style.marginTop = y + "px";
         } else if (!menu.style.marginLeft) {
           menu.style.marginTop = "auto";
         }
 
         // Added call to _repositionMenu() if it exists, to allow a component to
         // add specific repositioning to a menu.
         if (this._repositionMenu) {
           this._repositionMenu();
@@ -246,21 +246,21 @@ loop.shared.mixins = (function() {
       },
 
       componentWillUnmount: function() {
         this.documentBody.removeEventListener("click", this._onBodyClick);
         rootObject.removeEventListener("blur", this.hideDropdownMenu);
       },
 
       showDropdownMenu: function() {
-        this.setState({showMenu: true}, this._correctMenuPosition);
+        this.setState({ showMenu: true }, this._correctMenuPosition);
       },
 
       hideDropdownMenu: function() {
-        this.setState({showMenu: false}, function() {
+        this.setState({ showMenu: false }, function() {
           var menu = this.refs.menu && this.refs.menu.getDOMNode();
           if (menu) {
             menu.style.visibility = "hidden";
           }
         });
       },
 
       toggleDropdownMenu: function() {
@@ -394,17 +394,17 @@ loop.shared.mixins = (function() {
         var error;
         if (request.status < 200 || request.status >= 300) {
           error = new Error(request.status + " " + request.statusText);
           callback(error);
           return;
         }
 
         var type = request.getResponseHeader("Content-Type");
-        var blob = new Blob([request.response], {type: type});
+        var blob = new Blob([request.response], { type: type });
         callback(null, blob);
       }.bind(this);
 
       this._audioRequest.send(null);
     },
 
     /**
      * Ensures audio is stopped playing, and removes the object from memory.
--- a/browser/components/loop/content/shared/js/models.js
+++ b/browser/components/loop/content/shared/js/models.js
@@ -27,17 +27,17 @@ loop.shared.models = (function(l10n) {
     model: NotificationModel,
 
     /**
      * Adds a warning notification to the stack and renders it.
      *
      * @return {String} message
      */
     warn: function(message) {
-      this.add({level: "warning", message: message});
+      this.add({ level: "warning", message: message });
     },
 
     /**
      * Adds a l10n warning notification to the stack and renders it.
      *
      * @param  {String} messageId L10n message id
      */
     warnL10n: function(messageId) {
@@ -45,17 +45,17 @@ loop.shared.models = (function(l10n) {
     },
 
     /**
      * Adds an error notification to the stack and renders it.
      *
      * @return {String} message
      */
     error: function(message) {
-      this.add({level: "error", message: message});
+      this.add({ level: "error", message: message });
     },
 
     /**
      * Adds a l10n error notification to the stack and renders it.
      *
      * @param  {String} messageId L10n message id
      * @param  {Object} [l10nProps] An object with variables to be interpolated
      *                  into the translation. All members' values must be
@@ -66,17 +66,17 @@ loop.shared.models = (function(l10n) {
     },
 
     /**
      * Adds a success notification to the stack and renders it.
      *
      * @return {String} message
      */
     success: function(message) {
-      this.add({level: "success", message: message});
+      this.add({ level: "success", message: message });
     },
 
     /**
      * Adds a l10n success notification to the stack and renders it.
      *
      * @param  {String} messageId L10n message id
      * @param  {Object} [l10nProps] An object with variables to be interpolated
      *                  into the translation. All members' values must be
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -64,20 +64,20 @@ loop.OTSdkDriver = (function() {
       // If there's no getSources function, the sdk defines its own and caches
       // the result. So here we define our own one which wraps around the
       // real device enumeration api.
       window.MediaStreamTrack.getSources = function(callback) {
         navigator.mediaDevices.enumerateDevices().then(function(devices) {
           var result = [];
           devices.forEach(function(device) {
             if (device.kind === "audioinput") {
-              result.push({kind: "audio"});
+              result.push({ kind: "audio" });
             }
             if (device.kind === "videoinput") {
-              result.push({kind: "video"});
+              result.push({ kind: "video" });
             }
           });
           callback(result);
         });
       };
     }
   };
 
--- a/browser/components/loop/content/shared/js/textChatView.js
+++ b/browser/components/loop/content/shared/js/textChatView.js
@@ -36,18 +36,18 @@ loop.shared.views.chat = (function(mozL1
     _renderTimestamp: function() {
       var date = new Date(this.props.timestamp);
       var language = mozL10n.language ? mozL10n.language.code
                                       : mozL10n.getLanguage();
 
       return (
         React.createElement("span", {className: "text-chat-entry-timestamp"}, 
           date.toLocaleTimeString(language,
-                                   {hour: "numeric", minute: "numeric",
-                                   hour12: false})
+                                   { hour: "numeric", minute: "numeric",
+                                   hour12: false })
         )
       );
     },
 
     render: function() {
       var classes = React.addons.classSet({
         "text-chat-entry": true,
         "received": this.props.type === CHAT_MESSAGE_TYPES.RECEIVED,
@@ -77,17 +77,17 @@ loop.shared.views.chat = (function(mozL1
 
     propTypes: {
       message: React.PropTypes.string.isRequired
     },
 
     render: function() {
       return (
         React.createElement("div", {className: "text-chat-header special room-name"}, 
-          React.createElement("p", null, mozL10n.get("rooms_welcome_title", {conversationName: this.props.message}))
+          React.createElement("p", null, mozL10n.get("rooms_welcome_title", { conversationName: this.props.message }))
         )
       );
     }
   });
 
   /**
    * Manages the text entries in the chat entries view. This is split out from
    * TextChatView so that scrolling can be managed more efficiently - this
@@ -135,17 +135,17 @@ loop.shared.views.chat = (function(mozL1
     componentWillReceiveProps: function(nextProps) {
       var receivedMessageCount = nextProps.messageList.filter(function(message) {
         return message.type === CHAT_MESSAGE_TYPES.RECEIVED;
       }).length;
 
       // If the number of received messages has increased, we play a sound.
       if (receivedMessageCount > this.state.receivedMessageCount) {
         this.play("message");
-        this.setState({receivedMessageCount: receivedMessageCount});
+        this.setState({ receivedMessageCount: receivedMessageCount });
       }
     },
 
     componentDidUpdate: function() {
       // Don't scroll if we haven't got any chat messages yet - e.g. for context
       // display, we want to display starting at the top.
       if (this.shouldScroll && this._hasChatMessages()) {
         // This ensures the paint is complete.
--- a/browser/components/loop/content/shared/js/textChatView.jsx
+++ b/browser/components/loop/content/shared/js/textChatView.jsx
@@ -36,18 +36,18 @@ loop.shared.views.chat = (function(mozL1
     _renderTimestamp: function() {
       var date = new Date(this.props.timestamp);
       var language = mozL10n.language ? mozL10n.language.code
                                       : mozL10n.getLanguage();
 
       return (
         <span className="text-chat-entry-timestamp">
           {date.toLocaleTimeString(language,
-                                   {hour: "numeric", minute: "numeric",
-                                   hour12: false})}
+                                   { hour: "numeric", minute: "numeric",
+                                   hour12: false })}
         </span>
       );
     },
 
     render: function() {
       var classes = React.addons.classSet({
         "text-chat-entry": true,
         "received": this.props.type === CHAT_MESSAGE_TYPES.RECEIVED,
@@ -77,17 +77,17 @@ loop.shared.views.chat = (function(mozL1
 
     propTypes: {
       message: React.PropTypes.string.isRequired
     },
 
     render: function() {
       return (
         <div className="text-chat-header special room-name">
-          <p>{mozL10n.get("rooms_welcome_title", {conversationName: this.props.message})}</p>
+          <p>{mozL10n.get("rooms_welcome_title", { conversationName: this.props.message })}</p>
         </div>
       );
     }
   });
 
   /**
    * Manages the text entries in the chat entries view. This is split out from
    * TextChatView so that scrolling can be managed more efficiently - this
@@ -135,17 +135,17 @@ loop.shared.views.chat = (function(mozL1
     componentWillReceiveProps: function(nextProps) {
       var receivedMessageCount = nextProps.messageList.filter(function(message) {
         return message.type === CHAT_MESSAGE_TYPES.RECEIVED;
       }).length;
 
       // If the number of received messages has increased, we play a sound.
       if (receivedMessageCount > this.state.receivedMessageCount) {
         this.play("message");
-        this.setState({receivedMessageCount: receivedMessageCount});
+        this.setState({ receivedMessageCount: receivedMessageCount });
       }
     },
 
     componentDidUpdate: function() {
       // Don't scroll if we haven't got any chat messages yet - e.g. for context
       // display, we want to display starting at the top.
       if (this.shouldScroll && this._hasChatMessages()) {
         // This ensures the paint is complete.
--- a/browser/components/loop/content/shared/js/utils.js
+++ b/browser/components/loop/content/shared/js/utils.js
@@ -37,17 +37,17 @@ var inChrome = typeof Components != "und
     rootNavigator = navigatorObj || navigator;
   }
 
   var mozL10n;
   if (inChrome) {
     this.EXPORTED_SYMBOLS = ["utils"];
     mozL10n = { get: function() {
       throw new Error("mozL10n.get not availabled from chrome!");
-    }};
+    } };
   } else {
     mozL10n = document.mozL10n || navigator.mozL10n;
   }
 
   /**
    * Call types used for determining if a call is audio/video or audio-only.
    */
   var CALL_TYPES = {
@@ -111,17 +111,17 @@ var inChrome = typeof Components != "und
   /**
    * Format a given date into an l10n-friendly string.
    *
    * @param {Integer} The timestamp in seconds to format.
    * @return {String} The formatted string.
    */
   function formatDate(timestamp) {
     var date = (new Date(timestamp * 1000));
-    var options = {year: "numeric", month: "long", day: "numeric"};
+    var options = { year: "numeric", month: "long", day: "numeric" };
     return date.toLocaleDateString(navigator.language, options);
   }
 
   /**
    * Used for getting a boolean preference. It will either use the browser preferences
    * (if navigator.mozLoop is defined) or try to get them from localStorage.
    *
    * @param {String} prefName The name of the preference. Note that mozLoop adds
@@ -400,17 +400,17 @@ var inChrome = typeof Components != "und
         title: contextDescription
       });
     } else {
       subject = mozL10n.get("share_email_subject6");
       body = mozL10n.get("share_email_body6", {
         callUrl: callUrl
       });
     }
-    var bodyFooter =  body + footer;
+    var bodyFooter = body + footer;
     bodyFooter = bodyFooter.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n");
     mozLoop.composeEmail(
       subject,
       bodyFooter,
       recipient
     );
 
     var bucket = mozLoop.SHARING_ROOM_URL["EMAIL_FROM_" + (from || "").toUpperCase()];
@@ -501,38 +501,38 @@ var inChrome = typeof Components != "und
   /**
    * Utility function to decode a base64 character into an integer.
    *
    * Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
    *
    * @param {Number} chr The character code to decode.
    * @return {Number} The decoded value.
    */
-  function _b64ToUint6 (chr) {
-    return chr > 64 && chr < 91  ? chr - 65 :
+  function _b64ToUint6(chr) {
+    return chr > 64 && chr < 91 ? chr - 65 :
            chr > 96 && chr < 123 ? chr - 71 :
-           chr > 47 && chr < 58  ? chr + 4  :
-           chr === 43            ? 62       :
-           chr === 47            ? 63       : 0;
+           chr > 47 && chr < 58 ? chr + 4 :
+           chr === 43 ? 62 :
+           chr === 47 ? 63 : 0;
   }
 
   /**
    * Utility function to encode an integer into a base64 character code.
    *
    * Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
    *
    * @param {Number} uint6 The number to encode.
    * @return {Number} The encoded value.
    */
-  function _uint6ToB64 (uint6) {
-    return uint6 < 26   ? uint6 + 65 :
-           uint6 < 52   ? uint6 + 71 :
-           uint6 < 62   ? uint6 - 4  :
-           uint6 === 62 ? 43         :
-           uint6 === 63 ? 47         : 65;
+  function _uint6ToB64(uint6) {
+    return uint6 < 26 ? uint6 + 65 :
+           uint6 < 52 ? uint6 + 71 :
+           uint6 < 62 ? uint6 - 4 :
+           uint6 === 62 ? 43 :
+           uint6 === 63 ? 47 : 65;
   }
 
   /**
    * Utility function to convert a string into a uint8 array.
    *
    * Taken from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding
    *
    * @param {String} inString The string to convert.
@@ -541,20 +541,20 @@ var inChrome = typeof Components != "und
   function strToUint8Array(inString) {
     var inLength = inString.length;
     var arrayLength = 0;
     var chr;
 
     // Mapping.
     for (var mapIndex = 0; mapIndex < inLength; mapIndex++) {
       chr = inString.charCodeAt(mapIndex);
-      arrayLength += chr < 0x80      ? 1 :
-                     chr < 0x800     ? 2 :
-                     chr < 0x10000   ? 3 :
-                     chr < 0x200000  ? 4 :
+      arrayLength += chr < 0x80 ? 1 :
+                     chr < 0x800 ? 2 :
+                     chr < 0x10000 ? 3 :
+                     chr < 0x200000 ? 4 :
                      chr < 0x4000000 ? 5 : 6;
     }
 
     var result = new Uint8Array(arrayLength);
     var index = 0;
 
     // Transcription.
     for (var chrIndex = 0; index < arrayLength; chrIndex++) {
--- a/browser/components/loop/content/shared/js/validate.js
+++ b/browser/components/loop/content/shared/js/validate.js
@@ -108,19 +108,19 @@ loop.validate = (function() {
      * @param  {Object} value  The value to check
      * @param  {Array}  types  The list of types to check the value against
      * @return {Boolean}
      * @throws {TypeError} If the value doesn't match any types.
      */
     _dependencyMatchTypes: function(value, types) {
       return types.some(function(Type) {
         try {
-          return typeof Type === "undefined"         || // skip checking
-                 Type === null && value === null     || // null type
-                 value.constructor === Type          || // native type
+          return typeof Type === "undefined" || // skip checking
+                 Type === null && value === null || // null type
+                 value.constructor === Type || // native type
                  Type.prototype.isPrototypeOf(value) || // custom type
                  typeName(value) === typeName(Type);    // type string eq.
         } catch (e) {
           return false;
         }
       });
     }
   };
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -57,17 +57,17 @@ loop.shared.views = (function(_, mozL10n
       enabled: React.PropTypes.bool.isRequired,
       scope: React.PropTypes.string.isRequired,
       title: React.PropTypes.string,
       type: React.PropTypes.string.isRequired,
       visible: React.PropTypes.bool.isRequired
     },
 
     getDefaultProps: function() {
-      return {enabled: true, visible: true};
+      return { enabled: true, visible: true };
     },
 
     handleClick: function() {
       this.props.action();
     },
 
     _getClasses: function() {
       var cx = React.addons.classSet;
@@ -384,19 +384,19 @@ loop.shared.views = (function(_, mozL10n
   });
 
   /**
    * Conversation controls.
    */
   var ConversationToolbar = React.createClass({displayName: "ConversationToolbar",
     getDefaultProps: function() {
       return {
-        video: {enabled: true, visible: true},
-        audio: {enabled: true, visible: true},
-        screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
+        video: { enabled: true, visible: true },
+        audio: { enabled: true, visible: true },
+        screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: false },
         settingsMenuItems: null,
         showHangup: true
       };
     },
 
     getInitialState: function() {
       return {
         idle: false
@@ -441,17 +441,17 @@ loop.shared.views = (function(_, mozL10n
     },
 
     /**
      * If the conversation toolbar is idle, update its state and initialize the countdown
      * to return of the idle state. If the toolbar is active, only it's updated the userActivity flag.
      */
     _onBodyMouseMove: function() {
       if (this.state.idle) {
-        this.setState({idle: false});
+        this.setState({ idle: false });
         this.startIdleCountDown();
       } else {
         this.userActivity = true;
       }
     },
 
     /**
      * Instead of resetting the timeout for every mousemove (this event is called to many times,
@@ -479,17 +479,17 @@ loop.shared.views = (function(_, mozL10n
      * Launchs the process to check the user activity and the inactivity countdown to change
      * the toolbar to idle.
      * When the toolbar changes to idle, we remove the procces to check the user activity,
      * because the toolbar is going to be updated directly when the user moves the mouse.
      */
     startIdleCountDown: function() {
       this.checkUserActivity();
       this.inactivityTimeout = setTimeout(function() {
-        this.setState({idle: true});
+        this.setState({ idle: true });
         clearInterval(this.inactivityPollInterval);
       }.bind(this), 6000);
     },
 
     render: function() {
       if (!this.props.show) {
         return null;
       }
@@ -578,17 +578,17 @@ loop.shared.views = (function(_, mozL10n
     mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
 
     propTypes: {
       clearOnDocumentHidden: React.PropTypes.bool,
       notifications: React.PropTypes.object.isRequired
     },
 
     getDefaultProps: function() {
-      return {clearOnDocumentHidden: false};
+      return { clearOnDocumentHidden: false };
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
         this.forceUpdate();
       });
     },
 
@@ -602,17 +602,17 @@ loop.shared.views = (function(_, mozL10n
      * true and the collection isn't empty.
      */
     onDocumentHidden: function() {
       if (this.props.clearOnDocumentHidden &&
           this.props.notifications.length > 0) {
         // Note: The `silent` option prevents the `reset` event to be triggered
         // here, preventing the UI to "jump" a little because of the event
         // callback being processed in another tick (I think).
-        this.props.notifications.reset([], {silent: true});
+        this.props.notifications.reset([], { silent: true });
         this.forceUpdate();
       }
     },
 
     render: function() {
       return (
         React.createElement("div", {className: "messages"}, 
           this.props.notifications.map(function(notification, key) {
@@ -1116,33 +1116,33 @@ loop.shared.views = (function(_, mozL10n
               mozL10n.get("self_view_hidden_message")
             ), 
             React.createElement("div", {className: remoteStreamClasses}, 
               React.createElement(MediaView, {displayAvatar: !this.props.renderRemoteVideo, 
                 isLoading: this.props.isRemoteLoading, 
                 mediaType: "remote", 
                 posterUrl: this.props.remotePosterUrl, 
                 srcMediaElement: this.props.remoteSrcMediaElement}), 
-               this.state.localMediaAboslutelyPositioned ?
+              this.state.localMediaAboslutelyPositioned ?
                 this.renderLocalVideo() : null, 
-               this.props.displayScreenShare ? null : this.props.children
+              this.props.displayScreenShare ? null : this.props.children
             ), 
             React.createElement("div", {className: screenShareStreamClasses}, 
               React.createElement(MediaView, {displayAvatar: false, 
                 isLoading: this.props.isScreenShareLoading, 
                 mediaType: "screen-share", 
                 posterUrl: this.props.screenSharePosterUrl, 
                 srcMediaElement: this.props.screenShareMediaElement}), 
-               this.props.displayScreenShare ? this.props.children : null
+              this.props.displayScreenShare ? this.props.children : null
             ), 
             React.createElement(loop.shared.views.chat.TextChatView, {
               dispatcher: this.props.dispatcher, 
               showRoomName: this.props.showContextRoomName, 
               useDesktopPaths: this.props.useDesktopPaths}), 
-             this.state.localMediaAboslutelyPositioned ?
+            this.state.localMediaAboslutelyPositioned ?
               null : this.renderLocalVideo()
           )
         )
       );
     }
   });
 
   return {
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -57,17 +57,17 @@ loop.shared.views = (function(_, mozL10n
       enabled: React.PropTypes.bool.isRequired,
       scope: React.PropTypes.string.isRequired,
       title: React.PropTypes.string,
       type: React.PropTypes.string.isRequired,
       visible: React.PropTypes.bool.isRequired
     },
 
     getDefaultProps: function() {
-      return {enabled: true, visible: true};
+      return { enabled: true, visible: true };
     },
 
     handleClick: function() {
       this.props.action();
     },
 
     _getClasses: function() {
       var cx = React.addons.classSet;
@@ -384,19 +384,19 @@ loop.shared.views = (function(_, mozL10n
   });
 
   /**
    * Conversation controls.
    */
   var ConversationToolbar = React.createClass({
     getDefaultProps: function() {
       return {
-        video: {enabled: true, visible: true},
-        audio: {enabled: true, visible: true},
-        screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
+        video: { enabled: true, visible: true },
+        audio: { enabled: true, visible: true },
+        screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: false },
         settingsMenuItems: null,
         showHangup: true
       };
     },
 
     getInitialState: function() {
       return {
         idle: false
@@ -441,17 +441,17 @@ loop.shared.views = (function(_, mozL10n
     },
 
     /**
      * If the conversation toolbar is idle, update its state and initialize the countdown
      * to return of the idle state. If the toolbar is active, only it's updated the userActivity flag.
      */
     _onBodyMouseMove: function() {
       if (this.state.idle) {
-        this.setState({idle: false});
+        this.setState({ idle: false });
         this.startIdleCountDown();
       } else {
         this.userActivity = true;
       }
     },
 
     /**
      * Instead of resetting the timeout for every mousemove (this event is called to many times,
@@ -479,17 +479,17 @@ loop.shared.views = (function(_, mozL10n
      * Launchs the process to check the user activity and the inactivity countdown to change
      * the toolbar to idle.
      * When the toolbar changes to idle, we remove the procces to check the user activity,
      * because the toolbar is going to be updated directly when the user moves the mouse.
      */
     startIdleCountDown: function() {
       this.checkUserActivity();
       this.inactivityTimeout = setTimeout(function() {
-        this.setState({idle: true});
+        this.setState({ idle: true });
         clearInterval(this.inactivityPollInterval);
       }.bind(this), 6000);
     },
 
     render: function() {
       if (!this.props.show) {
         return null;
       }
@@ -578,17 +578,17 @@ loop.shared.views = (function(_, mozL10n
     mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
 
     propTypes: {
       clearOnDocumentHidden: React.PropTypes.bool,
       notifications: React.PropTypes.object.isRequired
     },
 
     getDefaultProps: function() {
-      return {clearOnDocumentHidden: false};
+      return { clearOnDocumentHidden: false };
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
         this.forceUpdate();
       });
     },
 
@@ -602,17 +602,17 @@ loop.shared.views = (function(_, mozL10n
      * true and the collection isn't empty.
      */
     onDocumentHidden: function() {
       if (this.props.clearOnDocumentHidden &&
           this.props.notifications.length > 0) {
         // Note: The `silent` option prevents the `reset` event to be triggered
         // here, preventing the UI to "jump" a little because of the event
         // callback being processed in another tick (I think).
-        this.props.notifications.reset([], {silent: true});
+        this.props.notifications.reset([], { silent: true });
         this.forceUpdate();
       }
     },
 
     render: function() {
       return (
         <div className="messages">
           {this.props.notifications.map(function(notification, key) {
@@ -1116,34 +1116,34 @@ loop.shared.views = (function(_, mozL10n
               {mozL10n.get("self_view_hidden_message")}
             </span>
             <div className={remoteStreamClasses}>
               <MediaView displayAvatar={!this.props.renderRemoteVideo}
                 isLoading={this.props.isRemoteLoading}
                 mediaType="remote"
                 posterUrl={this.props.remotePosterUrl}
                 srcMediaElement={this.props.remoteSrcMediaElement} />
-              { this.state.localMediaAboslutelyPositioned ?
-                this.renderLocalVideo() : null }
-              { this.props.displayScreenShare ? null : this.props.children }
+              {this.state.localMediaAboslutelyPositioned ?
+                this.renderLocalVideo() : null}
+              {this.props.displayScreenShare ? null : this.props.children}
             </div>
             <div className={screenShareStreamClasses}>
               <MediaView displayAvatar={false}
                 isLoading={this.props.isScreenShareLoading}
                 mediaType="screen-share"
                 posterUrl={this.props.screenSharePosterUrl}
                 srcMediaElement={this.props.screenShareMediaElement} />
-              { this.props.displayScreenShare ? this.props.children : null }
+              {this.props.displayScreenShare ? this.props.children : null}
             </div>
             <loop.shared.views.chat.TextChatView
               dispatcher={this.props.dispatcher}
               showRoomName={this.props.showContextRoomName}
               useDesktopPaths={this.props.useDesktopPaths} />
-            { this.state.localMediaAboslutelyPositioned ?
-              null : this.renderLocalVideo() }
+            {this.state.localMediaAboslutelyPositioned ?
+              null : this.renderLocalVideo()}
           </div>
         </div>
       );
     }
   });
 
   return {
     AvatarView: AvatarView,
--- a/browser/components/loop/modules/CardDavImporter.jsm
+++ b/browser/components/loop/modules/CardDavImporter.jsm
@@ -85,17 +85,17 @@ this.CardDavImporter.prototype = {
       auth = { method: "basic",
                user: options.user,
                password: options.password };
     } else {
       callback(new Error("Unknown authentication method"));
       return;
     }
 
-    if (!("host" in options)){
+    if (!("host" in options)) {
       callback(new Error("Missing host for CardDav import"));
       return;
     }
     let host = options.host;
 
     Task.spawn(function* () {
       log.info("Starting CardDAV import from " + host);
       let baseURL = "https://" + host;
@@ -215,17 +215,17 @@ this.CardDavImporter.prototype = {
    * @return {Object}  a LoopContacts-style contact object containing
    *                   the relevant fields from the vcard.
    */
 
   _convertVcard: function(vcard) {
     let contact = {};
     let nickname;
     vcard.split(/[\r\n]+(?! )/).forEach(
-      function (contentline) {
+      function(contentline) {
         contentline = contentline.replace(/[\r\n]+ /g, "");
         let match = /^(.*?[^\\]):(.*)$/.exec(contentline);
         if (match) {
           let nameparam = match[1];
           let value = match[2];
 
           // Poor-man's unescaping
           value = value.replace(/\\:/g, ":");
--- a/browser/components/loop/modules/GoogleImporter.jsm
+++ b/browser/components/loop/modules/GoogleImporter.jsm
@@ -165,17 +165,17 @@ this.GoogleImporter.prototype = {
    *                                 open a window for the OAuth process with chrome
    *                                 privileges.
    */
   startImport: function(options, callback, db, windowRef) {
     Task.spawn(function* () {
       let code = yield this._promiseAuthCode(windowRef);
       let tokenSet = yield this._promiseTokenSet(code);
       let contactEntries = yield this._getContactEntries(tokenSet);
-      let {total, success, ids} = yield this._processContacts(contactEntries, db, tokenSet);
+      let { total, success, ids } = yield this._processContacts(contactEntries, db, tokenSet);
       yield this._purgeContacts(ids, db);
 
       return {
         total: total,
         success: success
       };
     }.bind(this)).then(stats => callback(null, stats),
                        error => callback(error))
--- a/browser/components/loop/modules/LoopContacts.jsm
+++ b/browser/components/loop/modules/LoopContacts.jsm
@@ -1,28 +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";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "console",
                                   "resource://gre/modules/Console.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "LoopStorage",
                                   "resource:///modules/loop/LoopStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CardDavImporter",
                                   "resource:///modules/loop/CardDavImporter.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "GoogleImporter",
                                   "resource:///modules/loop/GoogleImporter.jsm");
 XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
-  const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
+  const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {});
   return new EventEmitter();
 });
 
 this.EXPORTED_SYMBOLS = ["LoopContacts"];
 
 const kObjectStoreName = "contacts";
 
 /*
@@ -308,17 +308,17 @@ LoopStorage.on("upgrade", function(e, db
     return;
   }
 
   // Create the 'contacts' store as it doesn't exist yet.
   let store = db.createObjectStore(kObjectStoreName, {
     keyPath: kKeyPath,
     autoIncrement: true
   });
-  store.createIndex(kServiceIdIndex, kServiceIdIndex, {unique: false});
+  store.createIndex(kServiceIdIndex, kServiceIdIndex, { unique: false });
 });
 
 /**
  * The Contacts class.
  *
  * Each method that is a member of this class requires the last argument to be a
  * callback Function. MozLoopAPI will cause things to break if this invariant is
  * violated. You'll notice this as well in the documentation for each method.
--- a/browser/components/loop/modules/LoopRooms.jsm
+++ b/browser/components/loop/modules/LoopRooms.jsm
@@ -1,30 +1,30 @@
 /* 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} = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
-const {MozLoopService, LOOP_SESSION_TYPE} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+const { MozLoopService, LOOP_SESSION_TYPE } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
                                   "resource://gre/modules/WebChannel.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
-  const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
+  const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {});
   return new EventEmitter();
 });
 XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/loop/loop.properties");
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache",
   "resource:///modules/loop/LoopRoomsCache.jsm");
@@ -651,17 +651,17 @@ var LoopRoomsInternal = {
       return;
     }
 
     if (!("roomOwner" in room)) {
       room.roomOwner = "-";
     }
 
     Task.spawn(function* () {
-      let {all, encrypted} = yield this.promiseEncryptRoomData(room);
+      let { all, encrypted } = yield this.promiseEncryptRoomData(room);
 
       // Save both sets of data...
       room = all;
       // ...but only send the encrypted data.
       let response = yield MozLoopService.hawkRequest(this.sessionType, "/rooms",
         "POST", encrypted);
 
       extend(room, JSON.parse(response.body));
@@ -880,17 +880,17 @@ var LoopRoomsInternal = {
                                        room.roomName;
     }
     if (roomData.urls && roomData.urls.length) {
       // For now we only support adding one URL to the room context.
       room.decryptedContext.urls = [roomData.urls[0]];
     }
 
     Task.spawn(function* () {
-      let {all, encrypted} = yield this.promiseEncryptRoomData(room);
+      let { all, encrypted } = yield this.promiseEncryptRoomData(room);
 
       // For patch, we only send the context data.
       let sendData = {
         context: encrypted.context
       };
 
       // This might be an upgrade to encrypted rename, so store the key
       // just in case.
@@ -912,17 +912,17 @@ var LoopRoomsInternal = {
    *
    * @param {String} version   Version number assigned to this change set.
    * @param {String} channelID Notification channel identifier.
    */
   onNotification: function(version, channelID) {
     // See if we received a notification for the channel that's currently active:
     let channelIDs = MozLoopService.channelIDs;
     if ((this.sessionType == LOOP_SESSION_TYPE.GUEST && channelID != channelIDs.roomsGuest) ||
-        (this.sessionType == LOOP_SESSION_TYPE.FXA   && channelID != channelIDs.roomsFxA)) {
+        (this.sessionType == LOOP_SESSION_TYPE.FXA && channelID != channelIDs.roomsFxA)) {
       return;
     }
 
     let oldDirty = gDirty;
     gDirty = true;
     // If we were already dirty, then get the full set of rooms. For example,
     // we'd already be dirty if we had started up but not got the list of rooms
     // yet.
--- a/browser/components/loop/modules/LoopRoomsCache.jsm
+++ b/browser/components/loop/modules/LoopRoomsCache.jsm
@@ -1,19 +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/. */
 "use strict";
 
-const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
-const {MozLoopService, LOOP_SESSION_TYPE} =
+const { MozLoopService, LOOP_SESSION_TYPE } =
   Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 this.EXPORTED_SYMBOLS = ["LoopRoomsCache"];
 
 const LOOP_ROOMS_CACHE_FILENAME = "loopRoomsCache.json";
@@ -58,17 +58,17 @@ LoopRoomsCache.prototype = {
    * Updates the local copy of the cache and saves it to disk.
    *
    * @param  {Object} contents An object to be saved in json format.
    * @return {Promise} A promise that is resolved once the save is complete.
    */
   _setCache: function(contents) {
     this._cache = contents;
 
-    return OS.File.makeDir(this.baseDir, {ignoreExisting: true}).then(() => {
+    return OS.File.makeDir(this.baseDir, { ignoreExisting: true }).then(() => {
         return CommonUtils.writeJSON(contents, this.path);
       });
   },
 
   /**
    * Returns the local copy of the cache if there is one, otherwise it reads
    * it from the disk.
    *
@@ -76,17 +76,17 @@ LoopRoomsCache.prototype = {
    */
   _getCache: Task.async(function* () {
     if (this._cache) {
       return this._cache;
     }
 
     try {
       return (this._cache = yield CommonUtils.readJSON(this.path));
-    } catch(error) {
+    } catch (error) {
       if (!error.becauseNoSuchFile) {
         MozLoopService.log.debug("Error reading the cache:", error);
       }
       return (this._cache = {});
     }
   }),
 
   /**
@@ -126,17 +126,17 @@ LoopRoomsCache.prototype = {
    *
    * @param {LOOP_SESSION_TYPE} sessionType  The session type for the room.
    * @param {String}            roomToken    The token for the room.
    * @param {String}            roomKey      The encryption key for the room.
    * @return {Promise} A promise that is resolved when the data has been stored.
    */
   setKey: Task.async(function* (sessionType, roomToken, roomKey) {
     if (sessionType != LOOP_SESSION_TYPE.FXA) {
-      return;
+      return Promise.resolve();
     }
 
     let cache = yield this._getCache();
 
     // Create these objects if they don't exist.
     // We aim to do this creation and setting of the room key in a
     // forwards-compatible way so that if new fields are added to rooms later
     // then we don't mess them up (if there's no keys).
--- a/browser/components/loop/modules/LoopStorage.jsm
+++ b/browser/components/loop/modules/LoopStorage.jsm
@@ -1,30 +1,30 @@
 /* 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} = Components;
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 // Make it possible to load LoopStorage.jsm in xpcshell tests
 try {
   Cu.importGlobalProperties(["indexedDB"]);
 } catch (ex) {
   // don't write this is out in xpcshell, since it's expected there
   if (typeof window !== "undefined" && "console" in window) {
     console.log("Failed to import indexedDB; if this isn't a unit test," +
                 " something is wrong", ex);
   }
 }
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
-  const {EventEmitter} = Cu.import("resource://devtools/shared/event-emitter.js", {});
+  const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {});
   return new EventEmitter();
 });
 
 this.EXPORTED_SYMBOLS = ["LoopStorage"];
 
 const kDatabasePrefix = "loop-";
 const kDefaultDatabaseName = "default";
 var gDatabaseName = kDatabasePrefix + kDefaultDatabaseName;
@@ -157,17 +157,17 @@ const getTransaction = function(store, c
     if (err) {
       callback(err);
       return;
     }
 
     let trans;
     try {
       trans = db.transaction(store, mode);
-    } catch(ex) {
+    } catch (ex) {
       callback(ex);
       return;
     }
     callback(null, trans);
   });
 };
 
 /**
--- a/browser/components/loop/modules/MozLoopPushHandler.jsm
+++ b/browser/components/loop/modules/MozLoopPushHandler.jsm
@@ -217,17 +217,17 @@ PushSocket.prototype = {
  * Create a RetryManager object. Class to handle retrying a UserAgent
  * to PushServer request following a retry back-off scheme managed by
  * this class. The current delay mechanism is to double the delay
  * each time an operation to be retried until a maximum is met.
  *
  * @param {Integer} startDelay The initial delay interval in milliseconds.
  * @param {Integer} maxDelay Maximum time delay value in milliseconds.
  */
-function RetryManager (startDelay, maxDelay) {
+function RetryManager(startDelay, maxDelay) {
   if (!startDelay || !maxDelay) {
     throw new Error("RetryManager: missing required parameters(s)" +
                      (startDelay ? "" : " startDelay") +
                      (maxDelay ? "" : " maxDelay"));
   }
 
   this._startDelay = startDelay;
   // The maximum delay cannot be less than the starting delay.
@@ -290,33 +290,33 @@ function PingMonitor(pingFunc, onTimeout
   this._pingInterval = interval;
   this._pingTimeout = timeout;
 }
 
 PingMonitor.prototype = {
   /**
    * Function to restart the ping timeout and cancel any current timeout operation.
    */
-  restart: function () {
+  restart: function() {
     consoleLog.info("PushHandler: ping timeout restart");
     this.stop();
     this._pingTimerID = setTimeout(() => { this._pingSend(); }, this._pingInterval);
   },
 
   /**
    * Function to stop the PingMonitor.
    */
   stop: function() {
-    if (this._pingTimerID){
+    if (this._pingTimerID) {
       clearTimeout(this._pingTimerID);
       this._pingTimerID = undefined;
     }
   },
 
-  _pingSend: function () {
+  _pingSend: function() {
     consoleLog.info("PushHandler: ping sent");
     this._pingTimerID = setTimeout(this._onTimeout, this._pingTimeout);
     this._pingFunc();
   }
 };
 
 
 /**
@@ -421,18 +421,18 @@ var MozLoopPushHandler = {
 
     this._initDone = false;
     this._retryManager.reset();
     this._pingMonitor.stop();
 
     // Un-register each active notification channel
     if (this.connectionState === CONNECTION_STATE_OPEN) {
       Object.keys(this.registeredChannels).forEach((id) => {
-        let unRegMsg = {messageType: "unregister",
-                        channelID: id};
+        let unRegMsg = { messageType: "unregister",
+                        channelID: id };
         this._pushSocket.send(unRegMsg);
       });
       this.registeredChannels = {};
     }
 
     this.connectionState = CONNECTION_STATE_CLOSED;
     this.serviceState = SERVICE_STATE_OFFLINE;
     this._pushSocket.close();
@@ -485,23 +485,23 @@ var MozLoopPushHandler = {
     consoleLog.info("PushHandler: channel registration: ", channelID);
     if (this.channels.has(channelID)) {
       // If this channel has an active registration with the PushServer
       // call the onRegister callback with the URL.
       if (this.registeredChannels[channelID]) {
         onRegistered(null, this.registeredChannels[channelID], channelID);
       }
       // Update the channel record.
-      this.channels.set(channelID, {onRegistered: onRegistered,
-                        onNotification: onNotification});
+      this.channels.set(channelID, { onRegistered: onRegistered,
+                        onNotification: onNotification });
       return;
     }
 
-    this.channels.set(channelID, {onRegistered: onRegistered,
-                                  onNotification: onNotification});
+    this.channels.set(channelID, { onRegistered: onRegistered,
+                                  onNotification: onNotification });
     this._channelsToRegister.push(channelID);
     this._registerChannels();
   },
 
   /**
    * Un-register a notification channel.
    *
    * @param {String} channelID Notification channel ID.
@@ -512,18 +512,18 @@ var MozLoopPushHandler = {
       return;
     }
 
     this.channels.delete(channelID);
 
     if (this.registeredChannels[channelID]) {
       delete this.registeredChannels[channelID];
       if (this.connectionState === CONNECTION_STATE_OPEN) {
-        this._pushSocket.send({messageType: "unregister",
-                               channelID: channelID});
+        this._pushSocket.send({ messageType: "unregister",
+                               channelID: channelID });
       }
     }
   },
 
   /**
    * Handles the start of the websocket stream.
    * Sends a hello message to the server.
    *
@@ -592,17 +592,17 @@ var MozLoopPushHandler = {
     // The recommended response to a ping message when the push server has nothing
     // else to send is a blank JSON message body: {}
     if (!aMsg.messageType && this.serviceState === SERVICE_STATE_ACTIVE) {
       // Treat this as a ping response
       this._pingMonitor.restart();
       return;
     }
 
-    switch(aMsg.messageType) {
+    switch (aMsg.messageType) {
       case "hello":
         this._onHello(aMsg);
         break;
 
       case "register":
         this._onRegister(aMsg);
         break;
 
@@ -685,18 +685,18 @@ var MozLoopPushHandler = {
           ackChannels.push(update);
         } else {
           consoleLog.error("PushHandler: notification received for unknown channelID: ",
                            update.channelID);
         }
       });
 
       consoleLog.log("PushHandler: PusherServer 'ack': ", ackChannels);
-      this._pushSocket.send({messageType: "ack",
-                             updates: ackChannels});
+      this._pushSocket.send({ messageType: "ack",
+                             updates: ackChannels });
      }
    },
 
   /**
    * Handles the PushServer registration response.
    *
    * @param {Object} msg PushServer to UserAgent registration response (parsed from JSON).
    */
@@ -861,13 +861,13 @@ var MozLoopPushHandler = {
 
   /**
    * Handles registering a service
    *
    * @param {string} channelID - identification token to use in registration for this channel.
    */
   _sendRegistration: function(channelID) {
     if (channelID) {
-      this._pushSocket.send({messageType: "register",
-                             channelID: channelID});
+      this._pushSocket.send({ messageType: "register",
+                             channelID: channelID });
     }
   }
 };
--- a/browser/components/loop/modules/MozLoopService.jsm
+++ b/browser/components/loop/modules/MozLoopService.jsm
@@ -501,17 +501,17 @@ var MozLoopServiceInternal = {
       pushURLs = { rooms: undefined };
       this.pushURLs.set(sessionType, pushURLs);
     }
 
     if (pushURLs[serviceType] == pushURL) {
       return Promise.resolve(pushURL);
     }
 
-    let newURLs = {rooms: pushURLs.rooms};
+    let newURLs = { rooms: pushURLs.rooms };
     newURLs[serviceType] = pushURL;
 
     return this.hawkRequestInternal(sessionType, "/registration", "POST",
                                     { simplePushURLs: newURLs }).then(
       (response) => {
         // If this failed we got an invalid token.
         if (!this.storeSessionToken(sessionType, response.headers)) {
           throw new Error("session-token-wrong-size");
@@ -988,17 +988,17 @@ var MozLoopServiceInternal = {
             var pair = pc.id.split("(");
             if (pair.length == 2) {
               pc.id = pair[0] + "(session=" + context.sessionId +
                   (context.callId ? " call=" + context.callId : "") + " " + pair[1];
             }
           }
 
           if (type == "iceconnectionstatechange") {
-            switch(pc.iceConnectionState) {
+            switch (pc.iceConnectionState) {
               case "failed":
               case "disconnected":
                 if (Services.telemetry.canRecordExtended) {
                   this.stageForTelemetryUpload(window, pc);
                 }
                 break;
             }
           }
@@ -1748,17 +1748,17 @@ this.MozLoopService = {
     }
     if (aSrc) {
       url.searchParams.set("utm_source", "firefox-browser");
       url.searchParams.set("utm_medium", "firefox-browser");
       url.searchParams.set("utm_campaign", aSrc);
     }
 
     // Find the most recent pageID that has the Loop prefix.
-    let mostRecentLoopPageID = {id: null, lastSeen: null};
+    let mostRecentLoopPageID = { id: null, lastSeen: null };
     for (let pageID of UITour.pageIDsForSession) {
       if (pageID[0] && pageID[0].startsWith("hello-tour_OpenPanel_") &&
           pageID[1] && pageID[1].lastSeen > mostRecentLoopPageID.lastSeen) {
         mostRecentLoopPageID.id = pageID[0];
         mostRecentLoopPageID.lastSeen = pageID[1].lastSeen;
       }
     }
 
--- a/browser/components/loop/standalone/content/js/standaloneMetricsStore.js
+++ b/browser/components/loop/standalone/content/js/standaloneMetricsStore.js
@@ -120,17 +120,17 @@ loop.store.StandaloneMetricsStore = (fun
     },
 
     /**
      * Handles connection failures for various reasons.
      *
      * @param {sharedActions.ConnectionFailure} actionData
      */
     connectionFailure: function(actionData) {
-      switch(actionData.reason) {
+      switch (actionData.reason) {
         case FAILURE_DETAILS.MEDIA_DENIED:
           this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
             "Media denied");
           break;
         case FAILURE_DETAILS.NO_MEDIA:
           this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
             "No media");
           break;
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -47,17 +47,17 @@ loop.standaloneRoomViews = (function(moz
         }));
       }
     },
 
     render: function() {
       return (
         React.createElement("p", {
           className: "terms-service", 
-          dangerouslySetInnerHTML: {__html: this._getContent()}, 
+          dangerouslySetInnerHTML: { __html: this._getContent()}, 
           onClick: this.recordClick})
       );
     }
   });
 
   var StandaloneHandleUserAgentView = React.createClass({displayName: "StandaloneHandleUserAgentView",
     mixins: [
       loop.store.StoreMixin("activeRoomStore")
@@ -92,30 +92,30 @@ loop.standaloneRoomViews = (function(moz
           onClick: this.handleJoinButton}, 
           buttonMessage
         )
       );
     },
 
     _renderFailureText: function() {
       return (
-        React.createElement("p", {className: "failure"},  mozL10n.get("rooms_already_joined") )
+        React.createElement("p", {className: "failure"}, mozL10n.get("rooms_already_joined"))
       );
     },
 
     render: function() {
       // The extra scroller div here is for providing a scroll view for shorter
       // screens, as the common.css specifies overflow:hidden for the body which
       // we need in some places.
       return (
         React.createElement("div", {className: "handle-user-agent-view-scroller"}, 
           React.createElement("div", {className: "handle-user-agent-view"}, 
             React.createElement("div", {className: "info-panel"}, 
-              React.createElement("p", {className: "loop-logo-text", title:  mozL10n.get("clientShortname2") }), 
-              React.createElement("p", {className: "roomName"},  this.state.roomName), 
+              React.createElement("p", {className: "loop-logo-text", title: mozL10n.get("clientShortname2")}), 
+              React.createElement("p", {className: "roomName"}, this.state.roomName), 
               React.createElement("p", {className: "loop-logo"}), 
               
                 this.state.failureReason ?
                   this._renderFailureText() :
                   this._renderJoinButton()
               
             ), 
             React.createElement(ToSView, {dispatcher: this.props.dispatcher}), 
@@ -143,17 +143,17 @@ loop.standaloneRoomViews = (function(moz
     handleRetryButton: function() {
       this.props.dispatcher.dispatch(new sharedActions.RetryAfterRoomFailure());
     },
 
     /**
      * @return String An appropriate string according to the failureReason.
      */
     getFailureString: function() {
-      switch(this.props.failureReason) {
+      switch (this.props.failureReason) {
         case FAILURE_DETAILS.MEDIA_DENIED:
         // XXX Bug 1166824 should provide a better string for this.
         case FAILURE_DETAILS.NO_MEDIA:
           return mozL10n.get("rooms_media_denied_message");
         case FAILURE_DETAILS.EXPIRED_OR_INVALID:
           return mozL10n.get("rooms_unavailable_notification_message");
         case FAILURE_DETAILS.TOS_FAILURE:
           return mozL10n.get("tos_failure_message",
@@ -297,32 +297,32 @@ loop.standaloneRoomViews = (function(moz
           mozL10n.get("rooms_room_full_call_to_action_nonFx_label", {
             brandShortname: mozL10n.get("brandShortname")
           })
         )
       );
     },
 
     render: function() {
-      switch(this.props.roomState) {
+      switch (this.props.roomState) {
         case ROOM_STATES.ENDED:
         case ROOM_STATES.READY: {
           return (
             React.createElement("div", {className: "room-inner-info-area"}, 
               React.createElement("button", {className: "btn btn-join btn-info", 
                       onClick: this.props.joinRoom}, 
                 mozL10n.get("rooms_room_join_label")
               ), 
               React.createElement(ToSView, {dispatcher: this.props.dispatcher})
             )
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
-                                {clientShortname: mozL10n.get("clientShortname2")});
+                                { clientShortname: mozL10n.get("clientShortname2") });
           var utils = loop.shared.utils;
           var isChrome = utils.isChrome(navigator.userAgent);
           var isFirefox = utils.isFirefox(navigator.userAgent);
           var isOpera = utils.isOpera(navigator.userAgent);
           var promptMediaMessageClasses = React.addons.classSet({
             "prompt-media-message": true,
             "chrome": isChrome,
             "firefox": isFirefox,
@@ -454,17 +454,17 @@ loop.standaloneRoomViews = (function(moz
         } else {
           this.setTitle(mozL10n.get("clientShortname2"));
         }
       }
 
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
-          publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
+          publisherConfig: this.getDefaultPublisherConfig({ publishVideo: true })
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
       // needs to do something to fix them, when if they're having a conversation
       // they just need to connect). However, we do want there to be somewhere to
       // find reasonably easily, in case there's issues raised.
       if (!this.state.roomInfoFailure && nextState.roomInfoFailure) {
@@ -498,32 +498,32 @@ loop.standaloneRoomViews = (function(moz
     },
 
     /**
      * Checks if current room is active.
      *
      * @return {Boolean}
      */
     _roomIsActive: function() {
-      return this.state.roomState === ROOM_STATES.JOINED            ||
+      return this.state.roomState === ROOM_STATES.JOINED ||
              this.state.roomState === ROOM_STATES.SESSION_CONNECTED ||
              this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
     },
 
     /**
      * Works out if remote video should be rended or not, depending on the
      * room state and other flags.
      *
      * @return {Boolean} True if remote video should be rended.
      *
      * XXX Refactor shouldRenderRemoteVideo & shouldRenderLoading to remove
      *     overlapping cases.
      */
     shouldRenderRemoteVideo: function() {
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.HAS_PARTICIPANTS:
           if (this.state.remoteVideoEnabled) {
             return true;
           }
 
           if (this.state.mediaConnected) {
             // since the remoteVideo hasn't yet been enabled, if the
             // media is connected, then we should be displaying an avatar.
@@ -560,17 +560,17 @@ loop.standaloneRoomViews = (function(moz
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a local
      * stream is on its way from the camera?
      *
      * @returns {boolean}
      * @private
      */
-    _isLocalLoading: function () {
+    _isLocalLoading: function() {
       return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
              !this.state.localSrcMediaElement;
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
@@ -623,23 +623,23 @@ loop.standaloneRoomViews = (function(moz
             React.createElement(StandaloneRoomInfoArea, {activeRoomStore: this.props.activeRoomStore, 
               dispatcher: this.props.dispatcher, 
               failureReason: this.state.failureReason, 
               isFirefox: this.props.isFirefox, 
               joinRoom: this.joinRoom, 
               roomState: this.state.roomState, 
               roomUsed: this.state.used}), 
             React.createElement(sharedViews.ConversationToolbar, {
-              audio: {enabled: !this.state.audioMuted,
+              audio: { enabled: !this.state.audioMuted,
                       visible: this._roomIsActive()}, 
               dispatcher: this.props.dispatcher, 
               hangup: this.leaveRoom, 
               publishStream: this.publishStream, 
               show: true, 
-              video: {enabled: !this.state.videoMuted,
+              video: { enabled: !this.state.videoMuted,
                       visible: this._roomIsActive()}})
           )
         )
       );
     }
   });
 
   var StandaloneOverlayWrapper = React.createClass({displayName: "StandaloneOverlayWrapper",
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -47,17 +47,17 @@ loop.standaloneRoomViews = (function(moz
         }));
       }
     },
 
     render: function() {
       return (
         <p
           className="terms-service"
-          dangerouslySetInnerHTML={{__html: this._getContent()}}
+          dangerouslySetInnerHTML={{ __html: this._getContent() }}
           onClick={this.recordClick}></p>
       );
     }
   });
 
   var StandaloneHandleUserAgentView = React.createClass({
     mixins: [
       loop.store.StoreMixin("activeRoomStore")
@@ -92,30 +92,30 @@ loop.standaloneRoomViews = (function(moz
           onClick={this.handleJoinButton}>
           {buttonMessage}
         </button>
       );
     },
 
     _renderFailureText: function() {
       return (
-        <p className="failure">{ mozL10n.get("rooms_already_joined") }</p>
+        <p className="failure">{mozL10n.get("rooms_already_joined")}</p>
       );
     },
 
     render: function() {
       // The extra scroller div here is for providing a scroll view for shorter
       // screens, as the common.css specifies overflow:hidden for the body which
       // we need in some places.
       return (
         <div className="handle-user-agent-view-scroller">
           <div className="handle-user-agent-view">
             <div className="info-panel">
-              <p className="loop-logo-text" title={ mozL10n.get("clientShortname2") }></p>
-              <p className="roomName">{ this.state.roomName }</p>
+              <p className="loop-logo-text" title={mozL10n.get("clientShortname2")}></p>
+              <p className="roomName">{this.state.roomName}</p>
               <p className="loop-logo" />
               {
                 this.state.failureReason ?
                   this._renderFailureText() :
                   this._renderJoinButton()
               }
             </div>
             <ToSView dispatcher={this.props.dispatcher} />
@@ -143,17 +143,17 @@ loop.standaloneRoomViews = (function(moz
     handleRetryButton: function() {
       this.props.dispatcher.dispatch(new sharedActions.RetryAfterRoomFailure());
     },
 
     /**
      * @return String An appropriate string according to the failureReason.
      */
     getFailureString: function() {
-      switch(this.props.failureReason) {
+      switch (this.props.failureReason) {
         case FAILURE_DETAILS.MEDIA_DENIED:
         // XXX Bug 1166824 should provide a better string for this.
         case FAILURE_DETAILS.NO_MEDIA:
           return mozL10n.get("rooms_media_denied_message");
         case FAILURE_DETAILS.EXPIRED_OR_INVALID:
           return mozL10n.get("rooms_unavailable_notification_message");
         case FAILURE_DETAILS.TOS_FAILURE:
           return mozL10n.get("tos_failure_message",
@@ -297,32 +297,32 @@ loop.standaloneRoomViews = (function(moz
           {mozL10n.get("rooms_room_full_call_to_action_nonFx_label", {
             brandShortname: mozL10n.get("brandShortname")
           })}
         </a>
       );
     },
 
     render: function() {
-      switch(this.props.roomState) {
+      switch (this.props.roomState) {
         case ROOM_STATES.ENDED:
         case ROOM_STATES.READY: {
           return (
             <div className="room-inner-info-area">
               <button className="btn btn-join btn-info"
                       onClick={this.props.joinRoom}>
                 {mozL10n.get("rooms_room_join_label")}
               </button>
               <ToSView dispatcher={this.props.dispatcher} />
             </div>
           );
         }
         case ROOM_STATES.MEDIA_WAIT: {
           var msg = mozL10n.get("call_progress_getting_media_description",
-                                {clientShortname: mozL10n.get("clientShortname2")});
+                                { clientShortname: mozL10n.get("clientShortname2") });
           var utils = loop.shared.utils;
           var isChrome = utils.isChrome(navigator.userAgent);
           var isFirefox = utils.isFirefox(navigator.userAgent);
           var isOpera = utils.isOpera(navigator.userAgent);
           var promptMediaMessageClasses = React.addons.classSet({
             "prompt-media-message": true,
             "chrome": isChrome,
             "firefox": isFirefox,
@@ -454,17 +454,17 @@ loop.standaloneRoomViews = (function(moz
         } else {
           this.setTitle(mozL10n.get("clientShortname2"));
         }
       }
 
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
-          publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
+          publisherConfig: this.getDefaultPublisherConfig({ publishVideo: true })
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
       // needs to do something to fix them, when if they're having a conversation
       // they just need to connect). However, we do want there to be somewhere to
       // find reasonably easily, in case there's issues raised.
       if (!this.state.roomInfoFailure && nextState.roomInfoFailure) {
@@ -498,32 +498,32 @@ loop.standaloneRoomViews = (function(moz
     },
 
     /**
      * Checks if current room is active.
      *
      * @return {Boolean}
      */
     _roomIsActive: function() {
-      return this.state.roomState === ROOM_STATES.JOINED            ||
+      return this.state.roomState === ROOM_STATES.JOINED ||
              this.state.roomState === ROOM_STATES.SESSION_CONNECTED ||
              this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
     },
 
     /**
      * Works out if remote video should be rended or not, depending on the
      * room state and other flags.
      *
      * @return {Boolean} True if remote video should be rended.
      *
      * XXX Refactor shouldRenderRemoteVideo & shouldRenderLoading to remove
      *     overlapping cases.
      */
     shouldRenderRemoteVideo: function() {
-      switch(this.state.roomState) {
+      switch (this.state.roomState) {
         case ROOM_STATES.HAS_PARTICIPANTS:
           if (this.state.remoteVideoEnabled) {
             return true;
           }
 
           if (this.state.mediaConnected) {
             // since the remoteVideo hasn't yet been enabled, if the
             // media is connected, then we should be displaying an avatar.
@@ -560,17 +560,17 @@ loop.standaloneRoomViews = (function(moz
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a local
      * stream is on its way from the camera?
      *
      * @returns {boolean}
      * @private
      */
-    _isLocalLoading: function () {
+    _isLocalLoading: function() {
       return this.state.roomState === ROOM_STATES.MEDIA_WAIT &&
              !this.state.localSrcMediaElement;
     },
 
     /**
      * Should we render a visual cue to the user (e.g. a spinner) that a remote
      * stream is on its way from the other user?
      *
@@ -623,24 +623,24 @@ loop.standaloneRoomViews = (function(moz
             <StandaloneRoomInfoArea activeRoomStore={this.props.activeRoomStore}
               dispatcher={this.props.dispatcher}
               failureReason={this.state.failureReason}
               isFirefox={this.props.isFirefox}
               joinRoom={this.joinRoom}
               roomState={this.state.roomState}
               roomUsed={this.state.used} />
             <sharedViews.ConversationToolbar
-              audio={{enabled: !this.state.audioMuted,
-                      visible: this._roomIsActive()}}
+              audio={{ enabled: !this.state.audioMuted,
+                      visible: this._roomIsActive() }}
               dispatcher={this.props.dispatcher}
               hangup={this.leaveRoom}
               publishStream={this.publishStream}
               show={true}
-              video={{enabled: !this.state.videoMuted,
-                      visible: this._roomIsActive()}} />
+              video={{ enabled: !this.state.videoMuted,
+                      visible: this._roomIsActive() }} />
           </sharedViews.MediaLayoutView>
         </div>
       );
     }
   });
 
   var StandaloneOverlayWrapper = React.createClass({
     propTypes: {
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -17,17 +17,17 @@ loop.webapp = (function(_, OT, mozL10n) 
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   /**
    * Homepage view.
    */
   var HomeView = React.createClass({displayName: "HomeView",
     render: function() {
       return (
-        React.createElement("p", null, mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")}))
+        React.createElement("p", null, mozL10n.get("welcome", { clientShortname: mozL10n.get("clientShortname2") }))
       );
     }
   });
 
   /**
    * Unsupported Browsers view.
    */
   var UnsupportedBrowserView = React.createClass({displayName: "UnsupportedBrowserView",
@@ -58,17 +58,17 @@ loop.webapp = (function(_, OT, mozL10n) 
     },
 
     render: function() {
       var unsupportedDeviceParams = {
         clientShortname: mozL10n.get("clientShortname2"),
         platform: mozL10n.get("unsupported_platform_" + this.props.platform)
       };
       var unsupportedLearnMoreText = mozL10n.get("unsupported_platform_learn_more_link",
-        {clientShortname: mozL10n.get("clientShortname2")});
+        { clientShortname: mozL10n.get("clientShortname2") });
 
       return (
         React.createElement("div", {className: "highlight-issue-box"}, 
           React.createElement("div", {className: "info-panel"}, 
             React.createElement("div", {className: "firefox-logo"}), 
             React.createElement("h1", null, mozL10n.get("unsupported_platform_heading")), 
             React.createElement("h4", null, mozL10n.get("unsupported_platform_message", unsupportedDeviceParams))
           ), 
@@ -89,17 +89,17 @@ loop.webapp = (function(_, OT, mozL10n) 
     },
 
     render: function() {
       if (this.props.isFirefox) {
         return null;
       }
       return (
         React.createElement("div", {className: "promote-firefox"}, 
-          React.createElement("h3", null, mozL10n.get("promote_firefox_hello_heading", {brandShortname: mozL10n.get("brandShortname")})), 
+          React.createElement("h3", null, mozL10n.get("promote_firefox_hello_heading", { brandShortname: mozL10n.get("brandShortname") })), 
           React.createElement("p", null, 
             React.createElement("a", {className: "btn btn-large btn-accept", 
                href: loop.config.downloadFirefoxUrl}, 
               mozL10n.get("get_firefox_button", {
                 brandShortname: mozL10n.get("brandShortname")
               })
             )
           )
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -17,17 +17,17 @@ loop.webapp = (function(_, OT, mozL10n) 
   var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   /**
    * Homepage view.
    */
   var HomeView = React.createClass({
     render: function() {
       return (
-        <p>{mozL10n.get("welcome", {clientShortname: mozL10n.get("clientShortname2")})}</p>
+        <p>{mozL10n.get("welcome", { clientShortname: mozL10n.get("clientShortname2") })}</p>
       );
     }
   });
 
   /**
    * Unsupported Browsers view.
    */
   var UnsupportedBrowserView = React.createClass({
@@ -58,17 +58,17 @@ loop.webapp = (function(_, OT, mozL10n) 
     },
 
     render: function() {
       var unsupportedDeviceParams = {
         clientShortname: mozL10n.get("clientShortname2"),
         platform: mozL10n.get("unsupported_platform_" + this.props.platform)
       };
       var unsupportedLearnMoreText = mozL10n.get("unsupported_platform_learn_more_link",
-        {clientShortname: mozL10n.get("clientShortname2")});
+        { clientShortname: mozL10n.get("clientShortname2") });
 
       return (
         <div className="highlight-issue-box">
           <div className="info-panel">
             <div className="firefox-logo" />
             <h1>{mozL10n.get("unsupported_platform_heading")}</h1>
             <h4>{mozL10n.get("unsupported_platform_message", unsupportedDeviceParams)}</h4>
           </div>
@@ -89,17 +89,17 @@ loop.webapp = (function(_, OT, mozL10n) 
     },
 
     render: function() {
       if (this.props.isFirefox) {
         return null;
       }
       return (
         <div className="promote-firefox">
-          <h3>{mozL10n.get("promote_firefox_hello_heading", {brandShortname: mozL10n.get("brandShortname")})}</h3>
+          <h3>{mozL10n.get("promote_firefox_hello_heading", { brandShortname: mozL10n.get("brandShortname") })}</h3>
           <p>
             <a className="btn btn-large btn-accept"
                href={loop.config.downloadFirefoxUrl}>
               {mozL10n.get("get_firefox_button", {
                 brandShortname: mozL10n.get("brandShortname")
               })}
             </a>
           </p>
--- a/browser/components/loop/standalone/server.js
+++ b/browser/components/loop/standalone/server.js
@@ -109,17 +109,17 @@ console.log("Static contents are availab
 console.log("Tests are viewable at " + baseUrl + "test/");
 console.log("Use this for development only.");
 
 // Handle SIGTERM signal.
 function shutdown(cb) {
   "use strict";
 
   try {
-    server.close(function () {
+    server.close(function() {
       process.exit(0);
       if (cb !== undefined) {
         cb();
       }
     });
 
   } catch (ex) {
     console.log(ex + " while calling server.close)");
--- a/browser/components/loop/test/desktop-local/conversationAppStore_test.js
+++ b/browser/components/loop/test/desktop-local/conversationAppStore_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.store.ConversationAppStore", function () {
+describe("loop.store.ConversationAppStore", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sandbox, activeRoomStore, dispatcher, roomUsed;
 
   beforeEach(function() {
     roomUsed = false;
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -15,17 +15,17 @@ describe("loop.conversation", function()
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     setLoopPrefStub = sandbox.stub();
 
     navigator.mozLoop = {
       doNotDisturb: true,
       getStrings: function() {
-        return JSON.stringify({textContent: "fakeText"});
+        return JSON.stringify({ textContent: "fakeText" });
       },
       get locale() {
         return "en-US";
       },
       setLoopPref: setLoopPrefStub,
       getLoopPref: function(prefName) {
         switch (prefName) {
           case "debug.sdk":
@@ -44,17 +44,17 @@ describe("loop.conversation", function()
       get appVersionInfo() {
         return {
           version: "42",
           channel: "test",
           platform: "test"
         };
       },
       getAudioBlob: sinon.spy(function(name, callback) {
-        callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
+        callback(null, new Blob([new ArrayBuffer(10)], { type: "audio/ogg" }));
       }),
       getSelectedTabMetadata: function(callback) {
         callback({});
       }
     };
 
     fakeWindow = {
       navigator: { mozLoop: navigator.mozLoop },
@@ -169,18 +169,18 @@ describe("loop.conversation", function()
       });
     });
 
     afterEach(function() {
       ccView = undefined;
     });
 
     it("should display the RoomView for rooms", function() {
-      conversationAppStore.setStoreState({windowType: "room"});
-      activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+      conversationAppStore.setStoreState({ windowType: "room" });
+      activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
       ccView = mountTestComponent();
 
       TestUtils.findRenderedComponentWithType(ccView,
         loop.roomViews.DesktopRoomConversationView);
     });
 
     it("should display the RoomFailureView for failures", function() {
@@ -191,35 +191,35 @@ describe("loop.conversation", function()
 
       ccView = mountTestComponent();
 
       TestUtils.findRenderedComponentWithType(ccView,
         loop.roomViews.RoomFailureView);
     });
 
     it("should set the correct title when rendering feedback view", function() {
-      conversationAppStore.setStoreState({showFeedbackForm: true});
+      conversationAppStore.setStoreState({ showFeedbackForm: true });
 
       ccView = mountTestComponent();
 
       sinon.assert.calledWithExactly(mozL10nGet, "conversation_has_ended");
     });
 
     it("should render FeedbackView if showFeedbackForm state is true",
        function() {
-         conversationAppStore.setStoreState({showFeedbackForm: true});
+         conversationAppStore.setStoreState({ showFeedbackForm: true });
 
          ccView = mountTestComponent();
 
          TestUtils.findRenderedComponentWithType(ccView, FeedbackView);
        });
 
     it("should dispatch a ShowFeedbackForm action if timestamp is 0",
        function() {
-         conversationAppStore.setStoreState({feedbackTimestamp: 0});
+         conversationAppStore.setStoreState({ feedbackTimestamp: 0 });
          sandbox.stub(dispatcher, "dispatch");
 
          ccView = mountTestComponent();
 
          ccView.handleCallTerminated();
 
          sinon.assert.calledOnce(dispatcher.dispatch);
          sinon.assert.calledWithExactly(dispatcher.dispatch,
--- a/browser/components/loop/test/desktop-local/l10n_test.js
+++ b/browser/components/loop/test/desktop-local/l10n_test.js
@@ -27,15 +27,15 @@ describe("document.mozL10n", function() 
     document.mozL10n.initialize(fakeMozLoop);
   });
 
   it("should get a simple string", function() {
     expect(document.mozL10n.get("test")).eql("test");
   });
 
   it("should get a plural form", function() {
-    expect(document.mozL10n.get("plural", {num: 10})).eql("10 plural forms");
+    expect(document.mozL10n.get("plural", { num: 10 })).eql("10 plural forms");
   });
 
   it("should correctly get a plural form for num = 0", function() {
-    expect(document.mozL10n.get("plural", {num: 0})).eql("0 plural form");
+    expect(document.mozL10n.get("plural", { num: 0 })).eql("0 plural form");
   });
 });
--- a/browser/components/loop/test/desktop-local/panel_test.js
+++ b/browser/components/loop/test/desktop-local/panel_test.js
@@ -15,47 +15,47 @@ describe("loop.panel", function() {
   var requests = [];
   var mozL10nGetSpy;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     fakeXHR = sandbox.useFakeXMLHttpRequest();
     requests = [];
     // https://github.com/cjohansen/Sinon.JS/issues/393
-    fakeXHR.xhr.onCreate = function (xhr) {
+    fakeXHR.xhr.onCreate = function(xhr) {
       requests.push(xhr);
     };
 
     fakeEvent = {
       preventDefault: sandbox.stub(),
       stopPropagation: sandbox.stub(),
       pageY: 42
     };
 
     fakeWindow = {
       close: sandbox.stub(),
       addEventListener: function() {},
-      document: { addEventListener: function(){} },
+      document: { addEventListener: function() {} },
       setTimeout: function(callback) { callback(); }
     };
     loop.shared.mixins.setRootObject(fakeWindow);
 
     notifications = new loop.shared.models.NotificationCollection();
 
     fakeMozLoop = navigator.mozLoop = {
       doNotDisturb: true,
       fxAEnabled: true,
       getStrings: function() {
-        return JSON.stringify({textContent: "fakeText"});
+        return JSON.stringify({ textContent: "fakeText" });
       },
       get locale() {
         return "en-US";
       },
       setLoopPref: sandbox.stub(),
-      getLoopPref: function (prefName) {
+      getLoopPref: function(prefName) {
         return "unseen";
       },
       getPluralForm: function() {
         return "fakeText";
       },
       rooms: {
         getAll: function(version, callback) {
           callback(null, []);
@@ -149,17 +149,17 @@ describe("loop.panel", function() {
           showTabButtons: true,
           mozLoop: fakeMozLoop,
           dispatcher: dispatcher,
           roomStore: roomStore
         }));
     }
 
     it("should hide the account entry when FxA is not enabled", function() {
-      navigator.mozLoop.userProfile = {email: "test@example.com"};
+      navigator.mozLoop.userProfile = { email: "test@example.com" };
       navigator.mozLoop.fxAEnabled = false;
 
       var view = TestUtils.renderIntoDocument(
         React.createElement(loop.panel.SettingsDropdown, {
           mozLoop: fakeMozLoop
         }));
 
       expect(view.getDOMNode().querySelectorAll(".icon-account"))
@@ -300,49 +300,49 @@ describe("loop.panel", function() {
           TestUtils.Simulate.click(view.getDOMNode()
                                      .querySelector(".entry-settings-signin"));
 
           sinon.assert.calledOnce(navigator.mozLoop.logInToFxA);
         });
       });
 
       it("should show a signout entry when user is authenticated", function() {
-        navigator.mozLoop.userProfile = {email: "test@example.com"};
+        navigator.mozLoop.userProfile = { email: "test@example.com" };
 
         var view = mountTestComponent();
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
                                        "settings_menu_item_signout");
         sinon.assert.neverCalledWith(document.mozL10n.get,
                                      "settings_menu_item_signin");
       });
 
       it("should show an account entry when user is authenticated", function() {
-        navigator.mozLoop.userProfile = {email: "test@example.com"};
+        navigator.mozLoop.userProfile = { email: "test@example.com" };
 
         var view = mountTestComponent();
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
                                        "settings_menu_item_settings");
       });
 
       it("should open the FxA settings when the account entry is clicked",
          function() {
-           navigator.mozLoop.userProfile = {email: "test@example.com"};
+           navigator.mozLoop.userProfile = { email: "test@example.com" };
 
            var view = mountTestComponent();
 
            TestUtils.Simulate.click(view.getDOMNode()
                                       .querySelector(".entry-settings-account"));
 
            sinon.assert.calledOnce(navigator.mozLoop.openFxASettings);
          });
 
       it("should sign out the user on click when authenticated", function() {
-        navigator.mozLoop.userProfile = {email: "test@example.com"};
+        navigator.mozLoop.userProfile = { email: "test@example.com" };
         var view = mountTestComponent();
 
         TestUtils.Simulate.click(view.getDOMNode()
                                    .querySelector(".entry-settings-signout"));
 
         sinon.assert.calledOnce(navigator.mozLoop.logOutFromFxA);
       });
 
@@ -570,17 +570,17 @@ describe("loop.panel", function() {
     describe("handleContextChevronClick", function() {
       var view;
 
       beforeEach(function() {
         // Stub to prevent warnings due to stores not being set up to handle
         // the actions we are triggering.
         sandbox.stub(dispatcher, "dispatch");
 
-        view = mountRoomEntry({room: new loop.store.Room(roomData)});
+        view = mountRoomEntry({ room: new loop.store.Room(roomData) });
       });
 
       // XXX Current version of React cannot use TestUtils.Simulate, please
       // enable when we upgrade.
       it.skip("should close the menu when you move out the cursor", function() {
         expect(view.refs.contextActions.state.showMenu).to.eql(false);
       });
 
@@ -626,25 +626,25 @@ describe("loop.panel", function() {
       });
 
       describe("OpenRoom", function() {
         it("should dispatch an OpenRoom action when button is clicked", function() {
           TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
-            new sharedActions.OpenRoom({roomToken: roomData.roomToken}));
+            new sharedActions.OpenRoom({ roomToken: roomData.roomToken }));
         });
 
         it("should dispatch an OpenRoom action when callback is called", function() {
           roomEntry.handleClickEntry(fakeEvent);
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
-            new sharedActions.OpenRoom({roomToken: roomData.roomToken}));
+            new sharedActions.OpenRoom({ roomToken: roomData.roomToken }));
         });
 
         it("should call window.close", function() {
           roomEntry.handleClickEntry(fakeEvent);
 
           sinon.assert.calledOnce(fakeWindow.close);
         });
       });
@@ -730,17 +730,17 @@ describe("loop.panel", function() {
         });
         var updatedRoom = new loop.store.Room(_.extend({}, roomData, {
           decryptedContext: {
             roomName: "New room name"
           },
           ctime: new Date().getTime()
         }));
 
-        roomEntry.setProps({room: updatedRoom});
+        roomEntry.setProps({ room: updatedRoom });
 
         expect(
           roomEntry.getDOMNode().textContent)
         .eql("New room name");
       });
     });
   });
 
@@ -797,21 +797,21 @@ describe("loop.panel", function() {
 
       sinon.assert.calledOnce(dispatch);
       sinon.assert.calledWithExactly(dispatch, new sharedActions.GetAllRooms());
     });
 
     it("should close the panel once a room is created and there is no error", function() {
       var view = createTestComponent();
 
-      roomStore.setStoreState({pendingCreation: true});
+      roomStore.setStoreState({ pendingCreation: true });
 
       sinon.assert.notCalled(fakeWindow.close);
 
-      roomStore.setStoreState({pendingCreation: false});
+      roomStore.setStoreState({ pendingCreation: false });
 
       sinon.assert.calledOnce(fakeWindow.close);
     });
 
     it("should render the no rooms view when no rooms available", function() {
       var view = createTestComponent();
       var node = view.getDOMNode();
 
@@ -824,17 +824,17 @@ describe("loop.panel", function() {
       sinon.assert.calledWithExactly(document.mozL10n.get,
                                      "no_conversations_message_heading2");
       sinon.assert.calledWithExactly(document.mozL10n.get,
                                      "no_conversations_start_message2");
     });
 
     it("should display a loading animation when rooms are pending", function() {
       var view = createTestComponent();
-      roomStore.setStoreState({pendingInitialRetrieval: true});
+      roomStore.setStoreState({ pendingInitialRetrieval: true });
 
       expect(view.getDOMNode().querySelectorAll(".room-list-loading").length).to.eql(1);
     });
   });
 
   describe("loop.panel.NewRoomView", function() {
     var roomStore, dispatcher, fakeEmail, dispatch;
 
@@ -860,19 +860,19 @@ describe("loop.panel", function() {
           mozLoop: fakeMozLoop,
           pendingOperation: pendingOperation,
           userDisplayName: fakeEmail
         }));
     }
 
     it("should dispatch a CreateRoom action with context when clicking on the " +
        "Start a conversation button", function() {
-      fakeMozLoop.userProfile = {email: fakeEmail};
+      fakeMozLoop.userProfile = { email: fakeEmail };
       var favicon = "data:image/x-icon;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
-      fakeMozLoop.getSelectedTabMetadata = function (callback) {
+      fakeMozLoop.getSelectedTabMetadata = function(callback) {
         callback({
           url: "http://invalid.com",
           description: "fakeSite",
           favicon: favicon,
           previews: ["fakeimage.png"]
         });
       };
 
@@ -1009,23 +1009,23 @@ describe("loop.panel", function() {
 
       dispatcher = new loop.Dispatcher();
       sandbox.stub(dispatcher, "dispatch");
 
       view = createTestComponent();
     });
 
     it("should render ConversationDropdown if state.showMenu=true", function() {
-      view = createTestComponent({showMenu: true});
+      view = createTestComponent({ showMenu: true });
 
       expect(view.refs.menu).to.not.eql(undefined);
     });
 
     it("should not render ConversationDropdown by default", function() {
-      view = createTestComponent({showMenu: false});
+      view = createTestComponent({ showMenu: false });
 
       expect(view.refs.menu).to.eql(undefined);
     });
 
     it("should call toggleDropdownMenu after link is emailed", function() {
       view.handleEmailButtonClick(fakeEvent);
 
       sinon.assert.calledOnce(view.props.toggleDropdownMenu);
@@ -1052,17 +1052,17 @@ describe("loop.panel", function() {
         from: "panel"
       }));
     });
 
     it("should dispatch a delete action when callback is called", function() {
       view.handleDeleteButtonClick(fakeEvent);
 
       sinon.assert.calledWithExactly(dispatcher.dispatch,
-        new sharedActions.DeleteRoom({roomToken: roomData.roomToken}));
+        new sharedActions.DeleteRoom({ roomToken: roomData.roomToken }));
     });
 
     it("should trigger handleClickEntry when button is clicked", function() {
       TestUtils.Simulate.click(view.refs.callButton.getDOMNode());
 
       sinon.assert.calledOnce(view.props.handleClickEntry);
     });
   });
--- a/browser/components/loop/test/desktop-local/roomStore_test.js
+++ b/browser/components/loop/test/desktop-local/roomStore_test.js
@@ -1,26 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var expect = chai.expect;
 
-describe("loop.store.Room", function () {
+describe("loop.store.Room", function() {
   "use strict";
 
   describe("#constructor", function() {
     it("should validate room values", function() {
       expect(function() {
         new loop.store.Room();
       }).to.Throw(Error, /missing required/);
     });
   });
 });
 
-describe("loop.store.RoomStore", function () {
+describe("loop.store.RoomStore", function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sandbox, dispatcher;
   var fakeRoomList;
 
   beforeEach(function() {
@@ -197,25 +197,25 @@ describe("loop.store.RoomStore", functio
       });
     });
 
     describe("#findNextAvailableRoomNumber", function() {
       var fakeNameTemplate = "RoomWord {{conversationLabel}}";
 
       it("should find next available room number from an empty room list",
         function() {
-          store.setStoreState({rooms: []});
+          store.setStoreState({ rooms: [] });
 
           expect(store.findNextAvailableRoomNumber(fakeNameTemplate)).eql(1);
         });
 
       it("should find next available room number from a non empty room list",
         function() {
           store.setStoreState({
-            rooms: [{decryptedContext: {roomName: "RoomWord 1"}}]
+            rooms: [{ decryptedContext: { roomName: "RoomWord 1" } }]
           });
 
           expect(store.findNextAvailableRoomNumber(fakeNameTemplate)).eql(2);
         });
 
       it("should not be sensitive to initial list order", function() {
         store.setStoreState({
           rooms: [{
@@ -236,17 +236,17 @@ describe("loop.store.RoomStore", functio
     describe("#createRoom", function() {
       var fakeNameTemplate = "Conversation {{conversationLabel}}";
       var fakeLocalRoomId = "777";
       var fakeOwner = "fake@invalid";
       var fakeRoomCreationData;
 
       beforeEach(function() {
         sandbox.stub(dispatcher, "dispatch");
-        store.setStoreState({pendingCreation: false, rooms: []});
+        store.setStoreState({ pendingCreation: false, rooms: [] });
         fakeRoomCreationData = {
           nameTemplate: fakeNameTemplate
         };
       });
 
       it("should clear any existing room errors", function() {
         sandbox.stub(fakeMozLoop.rooms, "create");
 
@@ -254,17 +254,17 @@ describe("loop.store.RoomStore", functio
 
         sinon.assert.calledOnce(fakeNotifications.remove);
         sinon.assert.calledWithExactly(fakeNotifications.remove,
           "create-room-error");
       });
 
       it("should log a telemetry event when the operation with context is successful", function() {
         sandbox.stub(fakeMozLoop.rooms, "create", function(data, cb) {
-          cb(null, {roomToken: "fakeToken"});
+          cb(null, { roomToken: "fakeToken" });
         });
 
         fakeRoomCreationData.urls = [{
           location: "http://invalid.com",
           description: "fakeSite",
           thumbnail: "fakeimage.png"
         }];
 
@@ -318,17 +318,17 @@ describe("loop.store.RoomStore", functio
         store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
 
         expect(store.getStoreState().pendingCreation).eql(true);
       });
 
       it("should dispatch a CreatedRoom action once the operation is done",
         function() {
           sandbox.stub(fakeMozLoop.rooms, "create", function(data, cb) {
-            cb(null, {roomToken: "fakeToken"});
+            cb(null, { roomToken: "fakeToken" });
           });
 
           store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
             new sharedActions.CreatedRoom({
               roomToken: "fakeToken"
@@ -348,17 +348,17 @@ describe("loop.store.RoomStore", functio
           sinon.assert.calledWithExactly(dispatcher.dispatch,
             new sharedActions.CreateRoomError({
               error: err
             }));
         });
 
       it("should log a telemetry event when the operation is successful", function() {
         sandbox.stub(fakeMozLoop.rooms, "create", function(data, cb) {
-          cb(null, {roomToken: "fakeToken"});
+          cb(null, { roomToken: "fakeToken" });
         });
 
         store.createRoom(new sharedActions.CreateRoom(fakeRoomCreationData));
 
         sinon.assert.calledOnce(fakeMozLoop.telemetryAddValue);
         sinon.assert.calledWithExactly(fakeMozLoop.telemetryAddValue,
           "LOOP_ROOM_CREATE", 0);
       });
@@ -378,17 +378,17 @@ describe("loop.store.RoomStore", functio
    });
 
    describe("#createdRoom", function() {
       beforeEach(function() {
         sandbox.stub(dispatcher, "dispatch");
       });
 
       it("should switch the pendingCreation state flag to false", function() {
-        store.setStoreState({pendingCreation: true});
+        store.setStoreState({ pendingCreation: true });
 
         store.createdRoom(new sharedActions.CreatedRoom({
           roomToken: "fakeToken"
         }));
 
         expect(store.getStoreState().pendingCreation).eql(false);
       });
 
@@ -403,17 +403,17 @@ describe("loop.store.RoomStore", functio
             new sharedActions.OpenRoom({
               roomToken: "fakeToken"
             }));
         });
     });
 
     describe("#createRoomError", function() {
       it("should switch the pendingCreation state flag to false", function() {
-        store.setStoreState({pendingCreation: true});
+        store.setStoreState({ pendingCreation: true });
 
         store.createRoomError({
           error: new Error("fake")
         });
 
         expect(store.getStoreState().pendingCreation).eql(false);
       });
 
@@ -604,35 +604,35 @@ describe("loop.store.RoomStore", functio
         store.addSocialShareProvider(new sharedActions.AddSocialShareProvider());
 
         sinon.assert.calledOnce(fakeMozLoop.addSocialShareProvider);
       });
     });
 
     describe("#setStoreState", function() {
       it("should update store state data", function() {
-        store.setStoreState({pendingCreation: true});
+        store.setStoreState({ pendingCreation: true });
 
         expect(store.getStoreState().pendingCreation).eql(true);
       });
 
       it("should trigger a `change` event", function(done) {
         store.once("change", function() {
           done();
         });
 
-        store.setStoreState({pendingCreation: true});
+        store.setStoreState({ pendingCreation: true });
       });
 
       it("should trigger a `change:<prop>` event", function(done) {
         store.once("change:pendingCreation", function() {
           done();
         });
 
-        store.setStoreState({pendingCreation: true});
+        store.setStoreState({ pendingCreation: true });
       });
     });
 
     describe("#getAllRooms", function() {
       it("should fetch the room list from the MozLoop API", function() {
         fakeMozLoop.rooms.getAll = function(version, cb) {
           cb(null, fakeRoomList);
         };
@@ -708,49 +708,49 @@ describe("loop.store.RoomStore", functio
         });
         fakeStore = new loop.store.RoomStore(dispatcher, {
           mozLoop: fakeMozLoop,
           activeRoomStore: activeRoomStore
         });
       });
 
       it("should subscribe to substore changes", function() {
-        var fakeServerData = {fake: true};
+        var fakeServerData = { fake: true };
 
-        activeRoomStore.setStoreState({serverData: fakeServerData});
+        activeRoomStore.setStoreState({ serverData: fakeServerData });
 
         expect(fakeStore.getStoreState().activeRoom.serverData)
           .eql(fakeServerData);
       });
 
       it("should trigger a change event when the substore is updated",
         function(done) {
           fakeStore.once("change:activeRoom", function() {
             done();
           });
 
-          activeRoomStore.setStoreState({serverData: {}});
+          activeRoomStore.setStoreState({ serverData: {} });
         });
     });
   });
 
   describe("#openRoom", function() {
     var store, fakeMozLoop;
 
     beforeEach(function() {
       fakeMozLoop = {
         rooms: {
           open: sinon.spy()
         }
       };
-      store = new loop.store.RoomStore(dispatcher, {mozLoop: fakeMozLoop});
+      store = new loop.store.RoomStore(dispatcher, { mozLoop: fakeMozLoop });
     });
 
     it("should open the room via mozLoop", function() {
-      dispatcher.dispatch(new sharedActions.OpenRoom({roomToken: "42abc"}));
+      dispatcher.dispatch(new sharedActions.OpenRoom({ roomToken: "42abc" }));
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.open);
       sinon.assert.calledWithExactly(fakeMozLoop.rooms.open, "42abc");
     });
   });
 
   describe("#updateRoomContext", function() {
     var store, fakeMozLoop, clock;
@@ -763,17 +763,17 @@ describe("loop.store.RoomStore", functio
             roomToken: "42abc",
             decryptedContext: {
               roomName: "sillier name"
             }
           }),
           update: null
         }
       };
-      store = new loop.store.RoomStore(dispatcher, {mozLoop: fakeMozLoop});
+      store = new loop.store.RoomStore(dispatcher, { mozLoop: fakeMozLoop });
     });
 
     afterEach(function() {
       clock.restore();
     });
 
     it("should rename the room via mozLoop", function() {
       fakeMozLoop.rooms.update = sinon.spy();
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.roomViews", function () {
+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 sharedViews = loop.shared.views;
   var ROOM_STATES = loop.store.ROOM_STATES;
@@ -19,17 +19,17 @@ describe("loop.roomViews", function () {
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     dispatcher = new loop.Dispatcher();
 
     fakeMozLoop = {
       getAudioBlob: sinon.spy(function(name, callback) {
-        callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
+        callback(null, new Blob([new ArrayBuffer(10)], { type: "audio/ogg" }));
       }),
       getLoopPref: sinon.stub(),
       getSelectedTabMetadata: sinon.stub().callsArgWith(0, {
         favicon: favicon,
         previews: [],
         title: ""
       }),
       openURL: sinon.stub(),
@@ -99,43 +99,43 @@ describe("loop.roomViews", function () {
     view = null;
   });
 
   describe("ActiveRoomStoreMixin", function() {
     it("should merge initial state", function() {
       var TestView = React.createClass({
         mixins: [loop.roomViews.ActiveRoomStoreMixin],
         getInitialState: function() {
-          return {foo: "bar"};
+          return { foo: "bar" };
         },
         render: function() { return React.DOM.div(); }
       });
 
       var testView = TestUtils.renderIntoDocument(
         React.createElement(TestView, {
           roomStore: roomStore
         }));
 
-      var expectedState = _.extend({foo: "bar", savingContext: false},
+      var expectedState = _.extend({ foo: "bar", savingContext: false },
         activeRoomStore.getInitialStoreState());
 
       expect(testView.state).eql(expectedState);
     });
 
     it("should listen to store changes", function() {
       var TestView = React.createClass({
         mixins: [loop.roomViews.ActiveRoomStoreMixin],
         render: function() { return React.DOM.div(); }
       });
       var testView = TestUtils.renderIntoDocument(
         React.createElement(TestView, {
           roomStore: roomStore
         }));
 
-      activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+      activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
       expect(testView.state.roomState).eql(ROOM_STATES.READY);
     });
   });
 
   describe("RoomFailureView", function() {
     var fakeAudio;
 
@@ -341,17 +341,17 @@ describe("loop.roomViews", function () {
       return TestUtils.renderIntoDocument(
         React.createElement(loop.roomViews.DesktopRoomConversationView, props));
     }
 
     it("should dispatch a setMute action when the audio mute button is pressed",
       function() {
         view = mountTestComponent();
 
-        view.setState({audioMuted: true});
+        view.setState({ audioMuted: true });
 
         var muteBtn = view.getDOMNode().querySelector(".btn-mute-audio");
 
         React.addons.TestUtils.Simulate.click(muteBtn);
 
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "setMute"));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
@@ -359,54 +359,54 @@ describe("loop.roomViews", function () {
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("type", "audio"));
       });
 
     it("should dispatch a setMute action when the video mute button is pressed",
       function() {
         view = mountTestComponent();
 
-        view.setState({videoMuted: false});
+        view.setState({ videoMuted: false });
 
         var muteBtn = view.getDOMNode().querySelector(".btn-mute-video");
 
         React.addons.TestUtils.Simulate.click(muteBtn);
 
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "setMute"));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("enabled", false));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("type", "video"));
       });
 
     it("should set the mute button as mute off", function() {
       view = mountTestComponent();
 
-      view.setState({videoMuted: false});
+      view.setState({ videoMuted: false });
 
       var muteBtn = view.getDOMNode().querySelector(".btn-mute-video");
 
       expect(muteBtn.classList.contains("muted")).eql(false);
     });
 
     it("should set the mute button as mute on", function() {
       view = mountTestComponent();
 
-      view.setState({audioMuted: true});
+      view.setState({ audioMuted: true });
 
       var muteBtn = view.getDOMNode().querySelector(".btn-mute-audio");
 
       expect(muteBtn.classList.contains("muted")).eql(true);
     });
 
     it("should dispatch a `StartScreenShare` action when sharing is not active and the screen share button is pressed", function() {
       view = mountTestComponent();
 
-      view.setState({screenSharingState: SCREEN_SHARE_STATES.INACTIVE});
+      view.setState({ screenSharingState: SCREEN_SHARE_STATES.INACTIVE });
 
       var muteBtn = view.getDOMNode().querySelector(".btn-mute-video");
 
       React.addons.TestUtils.Simulate.click(muteBtn);
 
       sinon.assert.calledWithMatch(dispatcher.dispatch,
         sinon.match.hasOwn("name", "setMute"));
     });
@@ -414,39 +414,39 @@ describe("loop.roomViews", function () {
     describe("#componentWillUpdate", function() {
       function expectActionDispatched(component) {
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           sinon.match.instanceOf(sharedActions.SetupStreamElements));
       }
 
       it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state is entered", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
           var component = mountTestComponent();
 
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
           expectActionDispatched(component);
         });
 
       it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is re-entered", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.ENDED });
           var component = mountTestComponent();
 
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
           expectActionDispatched(component);
         });
     });
 
     describe("#render", function() {
       it("should set document.title to store.serverData.roomName", function() {
         mountTestComponent();
 
-        activeRoomStore.setStoreState({roomName: "fakeName"});
+        activeRoomStore.setStoreState({ roomName: "fakeName" });
 
         expect(fakeWindow.document.title).to.equal("fakeName");
       });
 
       it("should render the RoomFailureView if the roomState is `FAILED`",
         function() {
           activeRoomStore.setStoreState({
             failureReason: FAILURE_DETAILS.UNKNOWN,
@@ -469,28 +469,28 @@ describe("loop.roomViews", function () {
           view = mountTestComponent();
 
           TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.RoomFailureView);
         });
 
       it("should render the DesktopRoomInvitationView if roomState is `JOINED`",
         function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
 
           view = mountTestComponent();
 
           expect(TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.not.eql(null);
         });
 
       it("should render the DesktopRoomInvitationView if roomState is `JOINED` with just owner",
         function() {
           activeRoomStore.setStoreState({
-            participants: [{owner: true}],
+            participants: [{ owner: true }],
             roomState: ROOM_STATES.JOINED
           });
 
           view = mountTestComponent();
 
           expect(TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.not.eql(null);
         });
@@ -508,31 +508,31 @@ describe("loop.roomViews", function () {
             loop.roomViews.DesktopRoomConversationView);
           expect(TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
         });
 
       it("should render the DesktopRoomConversationView if roomState is `JOINED` with participants",
         function() {
           activeRoomStore.setStoreState({
-            participants: [{owner: true}, {}],
+            participants: [{ owner: true }, {}],
             roomState: ROOM_STATES.JOINED
           });
 
           view = mountTestComponent();
 
           TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomConversationView);
           expect(TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
         });
 
       it("should render the DesktopRoomConversationView if roomState is `HAS_PARTICIPANTS`",
         function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
 
           view = mountTestComponent();
 
           TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomConversationView);
           expect(TestUtils.findRenderedComponentWithType(view,
             loop.roomViews.DesktopRoomInvitationView).getDOMNode()).to.eql(null);
         });
@@ -836,17 +836,17 @@ describe("loop.roomViews", function () {
 
         roomNameBox = view.getDOMNode().querySelector(".room-context-name");
       });
 
       it("should dispatch a UpdateRoomContext action when the save button is clicked",
         function() {
           React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
             value: "reallyFake"
-          }});
+          } });
 
           React.addons.TestUtils.Simulate.click(view.getDOMNode().querySelector(".button-accept"));
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
             new sharedActions.UpdateRoomContext({
               roomToken: "fakeToken",
               newRoomName: "reallyFake",
@@ -855,19 +855,19 @@ describe("loop.roomViews", function () {
               newRoomThumbnail: fakeContextURL.thumbnail
             }));
         });
 
       it("should dispatch a UpdateRoomContext action when Enter key is pressed",
         function() {
           React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
             value: "reallyFake"
-          }});
+          } });
 
-          TestUtils.Simulate.keyDown(roomNameBox, {key: "Enter", which: 13});
+          TestUtils.Simulate.keyDown(roomNameBox, { key: "Enter", which: 13 });
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
             new sharedActions.UpdateRoomContext({
               roomToken: "fakeToken",
               newRoomName: "reallyFake",
               newRoomDescription: fakeContextURL.description,
               newRoomURL: fakeContextURL.location,
--- a/browser/components/loop/test/mochitest/browser_CardDavImporter.js
+++ b/browser/components/loop/test/mochitest/browser_CardDavImporter.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {CardDavImporter} = Cu.import("resource:///modules/loop/CardDavImporter.jsm", {});
+const { CardDavImporter } = Cu.import("resource:///modules/loop/CardDavImporter.jsm", {});
 
 const kAuth = {
   "method": "basic",
   "user": "username",
   "password": "p455w0rd"
 };
 
 
@@ -157,17 +157,17 @@ const monkeyPatchImporter = function(imp
           break;
         case "REPORT https://example.com/carddav/abook/ 1":
           responseXML = xmlParser.parseFromString(listReportMultiget, "text/xml");
           break;
         default:
           reject(new Error("404 Not Found"));
           return;
       }
-      resolve({"responseXML": responseXML});
+      resolve({ "responseXML": responseXML });
     });
   }.bind(importer);
   return importer;
 };
 
 add_task(function* test_CardDavImport() {
   let importer = monkeyPatchImporter(new CardDavImporter());
   yield new Promise((resolve, reject) => {
--- a/browser/components/loop/test/mochitest/browser_GoogleImporter.js
+++ b/browser/components/loop/test/mochitest/browser_GoogleImporter.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {GoogleImporter} = Cu.import("resource:///modules/loop/GoogleImporter.jsm", {});
+const { GoogleImporter } = Cu.import("resource:///modules/loop/GoogleImporter.jsm", {});
 
 var importer = new GoogleImporter();
 
 function promiseImport() {
   return new Promise(function(resolve, reject) {
     importer.startImport({}, function(err, stats) {
       if (err) {
         reject(err);
--- a/browser/components/loop/test/mochitest/browser_LoopContacts.js
+++ b/browser/components/loop/test/mochitest/browser_LoopContacts.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const {LoopContacts} = Cu.import("resource:///modules/loop/LoopContacts.jsm", {});
-const {LoopStorage} = Cu.import("resource:///modules/loop/LoopStorage.jsm", {});
+const { LoopContacts } = Cu.import("resource:///modules/loop/LoopContacts.jsm", {});
+const { LoopStorage } = Cu.import("resource:///modules/loop/LoopStorage.jsm", {});
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
 const kContacts = [{
   id: 1,
   name: ["Ally Avocado"],
@@ -142,17 +142,17 @@ const onContactUpdated = function(e, con
   Assert.ok(idx > -1, "Updated contact should be expected");
   gExpectedUpdates.splice(idx, 1);
 };
 
 LoopContacts.on("add", onContactAdded);
 LoopContacts.on("remove", onContactRemoved);
 LoopContacts.on("update", onContactUpdated);
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   LoopContacts.removeAll(() => {});
   LoopContacts.off("add", onContactAdded);
   LoopContacts.off("remove", onContactRemoved);
   LoopContacts.off("update", onContactUpdated);
 });
 
 // Test adding a contact.
 add_task(function* () {
--- a/browser/components/loop/test/mochitest/browser_LoopRooms_channel.js
+++ b/browser/components/loop/test/mochitest/browser_LoopRooms_channel.js
@@ -3,30 +3,30 @@
 
 /*
  * This file contains tests for checking the channel from the standalone to
  * LoopRooms works for checking if rooms can be opened within the conversation
  * window.
  */
 "use strict";
 
-var {WebChannel} = Cu.import("resource://gre/modules/WebChannel.jsm", {});
-var {Chat} = Cu.import("resource:///modules/Chat.jsm", {});
+var { WebChannel } = Cu.import("resource://gre/modules/WebChannel.jsm", {});
+var { Chat } = Cu.import("resource:///modules/Chat.jsm", {});
 
 const TEST_URI =
   "example.com/browser/browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html";
 const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
 const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
 
 const ROOM_TOKEN = "fake1234";
 const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
 
 var openChatOrig = Chat.open;
 
-var fakeRoomList = new Map([[ ROOM_TOKEN, { roomToken: ROOM_TOKEN } ]]);
+var fakeRoomList = new Map([[ROOM_TOKEN, { roomToken: ROOM_TOKEN }]]);
 
 function BackChannel(uri) {
   this.channel = new WebChannel("test-loop-link-clicker-backchannel", uri);
 
   this.channel.listen((id, data) => {
     if (this.pendingResolve) {
       let resolve = this.pendingResolve;
       this.pendingResolve = null;
--- a/browser/components/loop/test/mochitest/browser_fxa_login.js
+++ b/browser/components/loop/test/mochitest/browser_fxa_login.js
@@ -281,17 +281,17 @@ add_task(function* basicAuthorizationAnd
   };
   yield promiseOAuthParamsSetup(BASE_URL, params);
 
   info("registering");
   mockPushHandler.registrationPushURL = "https://localhost/pushUrl/guest";
   yield MozLoopService.promiseRegisteredWithServers();
 
   let statusChangedPromise = promiseObserverNotified("loop-status-changed");
-  yield loadLoopPanel({stayOnline: true});
+  yield loadLoopPanel({ stayOnline: true });
   yield statusChangedPromise;
   let loopDoc = document.getElementById("loop-panel-iframe").contentDocument;
   let accountLogin = loopDoc.getElementsByClassName("signin-link")[0];
   let visibleEmail = loopDoc.getElementsByClassName("user-identity");
   is(visibleEmail.length, 0, "No email should be displayed when logged out");
   is(accountLogin.textContent, "Sign In or Sign Up", "Login/Signup links when logged out");
   is(MozLoopService.userProfile, null, "profile should be null before log-in");
   let loopButton = document.getElementById("loop-button");
@@ -314,17 +314,17 @@ add_task(function* basicAuthorizationAnd
 
   let registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
   is(registrationResponse.response.simplePushURLs.rooms, "https://localhost/pushUrl/fxa-rooms",
      "Check registered push URL");
 
   let loopPanel = document.getElementById("loop-notification-panel");
   loopPanel.hidePopup();
   statusChangedPromise = promiseObserverNotified("loop-status-changed");
-  yield loadLoopPanel({stayOnline: true});
+  yield loadLoopPanel({ stayOnline: true });
   yield statusChangedPromise;
   is(loopButton.getAttribute("state"), "", "state of loop button should return to empty after panel is opened");
   loopPanel.hidePopup();
 
   info("logout");
   yield MozLoopService.logOutFromFxA();
   checkLoggedOutState();
   registrationResponse = yield promiseOAuthGetRegistration(BASE_URL);
--- a/browser/components/loop/test/mochitest/browser_mozLoop_doNotDisturb.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_doNotDisturb.js
@@ -8,17 +8,17 @@
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/Promise.jsm", this);
 
 add_task(loadLoopPanel);
 
 add_task(function* test_mozLoop_doNotDisturb() {
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("loop.do_not_disturb");
   });
 
   Assert.ok(gMozLoopAPI, "mozLoop should exist");
 
   // Test doNotDisturb (getter)
   Services.prefs.setBoolPref("loop.do_not_disturb", true);
   Assert.equal(gMozLoopAPI.doNotDisturb, true,
--- a/browser/components/loop/test/mochitest/browser_mozLoop_prefs.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_prefs.js
@@ -8,34 +8,34 @@
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/Promise.jsm", this);
 
 add_task(loadLoopPanel);
 
 add_task(function* test_mozLoop_charPref() {
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("loop.test");
   });
 
   Assert.ok(gMozLoopAPI, "mozLoop should exist");
 
   // Test setLoopPref
   gMozLoopAPI.setLoopPref("test", "foo", Ci.nsIPrefBranch.PREF_STRING);
   Assert.equal(Services.prefs.getCharPref("loop.test"), "foo",
                "should set loop pref value correctly");
 
   // Test getLoopPref
   Assert.equal(gMozLoopAPI.getLoopPref("test"), "foo",
                "should get loop pref value correctly");
 });
 
 add_task(function* test_mozLoop_boolPref() {
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.prefs.clearUserPref("loop.testBool");
   });
 
   Assert.ok(gMozLoopAPI, "mozLoop should exist");
 
   Services.prefs.setBoolPref("loop.testBool", true);
 
   // Test getLoopPref
--- a/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * This file contains tests for the window.LoopUI active tab trackers.
  */
 "use strict";
 
-const {injectLoopAPI} = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { injectLoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
 gMozLoopAPI = injectLoopAPI({});
 
 var handlers = [
   {
     resolve: null,
     windowId: null,
     listener: function(err, windowId) {
       handlers[0].windowId = windowId;
--- a/browser/components/loop/test/mochitest/browser_mozLoop_socialShare.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_socialShare.js
@@ -4,17 +4,17 @@
 /**
  * This is an integration test from navigator.mozLoop through to the end
  * effects - rather than just testing MozLoopAPI alone.
  */
 
 "use strict";
 
 Cu.import("resource://gre/modules/Promise.jsm");
-const {SocialService} = Cu.import("resource://gre/modules/SocialService.jsm", {});
+const { SocialService } = Cu.import("resource://gre/modules/SocialService.jsm", {});
 
 add_task(loadLoopPanel);
 
 const kShareProvider = {
   name: "provider 1",
   origin: "https://example.com",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
   shareURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html"
--- a/browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
@@ -12,17 +12,17 @@ Components.utils.import("resource://gre/
 add_task(loadLoopPanel);
 
 /**
  * Enable local telemetry recording for the duration of the tests.
  */
 add_task(function* test_initialize() {
   let oldCanRecord = Services.telemetry.canRecordExtended;
   Services.telemetry.canRecordExtended = true;
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     Services.telemetry.canRecordExtended = oldCanRecord;
   });
 });
 
 /**
  * Tests that enumerated bucket histograms exist and can be updated.
  */
 add_task(function* test_mozLoop_telemetryAdd_buckets() {
--- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js
+++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js
@@ -3,17 +3,17 @@
 
 /**
  * Test the toolbar button states.
  */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/Promise.jsm", this);
-const {LoopRoomsInternal} = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
+const { LoopRoomsInternal } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
 Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
 
 const fxASampleToken = {
   token_type: "bearer",
   access_token: "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
   scope: "profile"
 };
 
@@ -122,33 +122,33 @@ add_task(function* test_active() {
   MozLoopServiceInternal.notifyStatusChanged();
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext");
 });
 
 add_task(function* test_room_participants() {
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext");
-  let roomsCache = new Map([[ "test_room", {participants: [{displayName: "hugh", id: "008", owner: true}]} ]]);
+  let roomsCache = new Map([["test_room", { participants: [{ displayName: "hugh", id: "008", owner: true }] }]]);
   LoopRooms._setRoomsCache(roomsCache);
   MozLoopServiceInternal.notifyStatusChanged();
   // Since we're changing the rooms map directly, we're expecting it to be a synchronous operation.
   // But Promises have the inherent property of then-ables being async so even though the operation returns immediately,
   // because the cache is hit, the promise won't be resolved until after the next tick.
   // And that's what the line below does, waits until the next tick
   yield new Promise(resolve => executeSoon(resolve));
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Active conversation", "Check button has active tooltiptext");
-  roomsCache.set("test_room", {participants: [{displayName: "hugh", id: "008", owner: false}]});
+  roomsCache.set("test_room", { participants: [{ displayName: "hugh", id: "008", owner: false }] });
   LoopRooms._setRoomsCache(roomsCache);
   MozLoopServiceInternal.notifyStatusChanged();
   yield new Promise(resolve => executeSoon(resolve));
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "active", "Check button is in active state");
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Someone is waiting for you in a conversation", "Check button has participantswaiting tooltiptext");
-  roomsCache.set("test_room", {participants: []});
+  roomsCache.set("test_room", { participants: [] });
   LoopRooms._setRoomsCache(roomsCache);
   MozLoopServiceInternal.notifyStatusChanged();
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("state"), "", "Check button is in default state");
   Assert.strictEqual(LoopUI.toolbarButton.node.getAttribute("tooltiptext"), "Start a conversation", "Check button has default tooltiptext");
   LoopRooms._setRoomsCache();
 });
 
 add_task(function* test_panelToggle_on_click() {
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/components/loop/test/mochitest/head.js
@@ -4,17 +4,17 @@
 "use strict";
 
 const HAWK_TOKEN_LENGTH = 64;
 const {
   LOOP_SESSION_TYPE,
   MozLoopServiceInternal,
   MozLoopService
 } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
-const {LoopRooms} = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
+const { LoopRooms } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
 
 // Cache this value only once, at the beginning of a
 // test run, so that it doesn't pick up the offline=true
 // if offline mode is requested multiple times in a test run.
 const WAS_OFFLINE = Services.io.offline;
 
 
 var gMozLoopAPI;
@@ -181,17 +181,17 @@ function promiseDeletedOAuthParams(baseU
   });
 }
 
 function promiseObserverNotified(aTopic, aExpectedData = null) {
   return new Promise((resolve, reject) => {
     Services.obs.addObserver(function onNotification(aSubject, topic, aData) {
       Services.obs.removeObserver(onNotification, topic);
       is(aData, aExpectedData, "observer data should match expected data");
-      resolve({subject: aSubject, data: aData});
+      resolve({ subject: aSubject, data: aData });
     }, aTopic, false);
   });
 }
 
 /**
  * Get the last registration on the test server.
  */
 function promiseOAuthGetRegistration(baseURL) {
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.store.ActiveRoomStore", function () {
+describe("loop.store.ActiveRoomStore", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
@@ -78,17 +78,17 @@ describe("loop.store.ActiveRoomStore", f
     it("should throw an error if mozLoop is missing", function() {
       expect(function() {
         new loop.store.ActiveRoomStore(dispatcher);
       }).to.Throw(/mozLoop/);
     });
 
     it("should throw an error if sdkDriver is missing", function() {
       expect(function() {
-        new loop.store.ActiveRoomStore(dispatcher, {mozLoop: {}});
+        new loop.store.ActiveRoomStore(dispatcher, { mozLoop: {} });
       }).to.Throw(/sdkDriver/);
     });
   });
 
   describe("#roomFailure", function() {
     var fakeError;
 
     beforeEach(function() {
@@ -166,17 +166,17 @@ describe("loop.store.ActiveRoomStore", f
         error: fakeError,
         failedJoinRequest: false
       }));
 
       sinon.assert.calledOnce(fakeMultiplexGum.reset);
     });
 
     it("should set screen sharing inactive", function() {
-      store.setStoreState({windowId: "1234"});
+      store.setStoreState({ windowId: "1234" });
 
       store.roomFailure(new sharedActions.RoomFailure({
         error: fakeError,
         failedJoinRequest: false
       }));
 
       sinon.assert.calledOnce(fakeMozLoop.setScreenShareState);
       sinon.assert.calledWithExactly(fakeMozLoop.setScreenShareState, "1234", false);
@@ -817,21 +817,21 @@ describe("loop.store.ActiveRoomStore", f
       var state = store.getStoreState();
       expect(state.socialShareProviders)
         .eql(fakeSocialShareInfo.socialShareProviders);
     });
   });
 
   describe("#joinRoom", function() {
     beforeEach(function() {
-      store.setStoreState({roomState: ROOM_STATES.READY});
+      store.setStoreState({ roomState: ROOM_STATES.READY });
     });
 
     it("should reset failureReason", function() {
-      store.setStoreState({failureReason: "Test"});
+      store.setStoreState({ failureReason: "Test" });
 
       store.joinRoom();
 
       expect(store.getStoreState().failureReason).eql(undefined);
     });
 
     describe("Standalone Handles Room", function() {
       it("should dispatch a MetricsLogJoinRoom action", function() {
@@ -988,17 +988,17 @@ describe("loop.store.ActiveRoomStore", f
             reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
           }));
       });
     });
   });
 
   describe("#gotMediaPermission", function() {
     beforeEach(function() {
-      store.setStoreState({roomToken: "tokenFake"});
+      store.setStoreState({ roomToken: "tokenFake" });
     });
 
     it("should set the room state to JOINING", function() {
       store.gotMediaPermission();
 
       expect(store.getStoreState().roomState).eql(ROOM_STATES.JOINING);
     });
 
@@ -1155,17 +1155,17 @@ describe("loop.store.ActiveRoomStore", f
         sinon.assert.calledOnce(fakeMozLoop.rooms.refreshMembership);
         sinon.assert.calledWith(fakeMozLoop.rooms.refreshMembership,
           "fakeToken", "12563478");
     });
 
     it("should call mozLoop.rooms.refreshMembership before the next expiresTime",
       function() {
         fakeMozLoop.rooms.refreshMembership.callsArgWith(2,
-          null, {expires: 40});
+          null, { expires: 40 });
 
         store.joinedRoom(new sharedActions.JoinedRoom(fakeJoinedData));
 
         // Clock tick for the first expiry time (which
         // sets up the refreshMembership).
         sandbox.clock.tick(fakeJoinedData.expires * 1000);
 
         // Clock tick for expiry time in the refresh membership response.
@@ -1227,17 +1227,17 @@ describe("loop.store.ActiveRoomStore", f
 
     it("should reset the multiplexGum", function() {
       store.connectionFailure(connectionFailureAction);
 
       sinon.assert.calledOnce(fakeMultiplexGum.reset);
     });
 
     it("should set screen sharing inactive", function() {
-      store.setStoreState({windowId: "1234"});
+      store.setStoreState({ windowId: "1234" });
 
       store.connectionFailure(connectionFailureAction);
 
       sinon.assert.calledOnce(fakeMozLoop.setScreenShareState);
       sinon.assert.calledWithExactly(fakeMozLoop.setScreenShareState, "1234", false);
     });
 
     it("should disconnect from the servers via the sdk", function() {
@@ -1302,43 +1302,43 @@ describe("loop.store.ActiveRoomStore", f
 
       sinon.assert.notCalled(fakeMultiplexGum.reset);
       sinon.assert.notCalled(fakeSdkDriver.disconnectSession);
     });
   });
 
   describe("#setMute", function() {
     it("should save the mute state for the audio stream", function() {
-      store.setStoreState({audioMuted: false});
+      store.setStoreState({ audioMuted: false });
 
       store.setMute(new sharedActions.SetMute({
         type: "audio",
         enabled: true
       }));
 
       expect(store.getStoreState().audioMuted).eql(false);
     });
 
     it("should save the mute state for the video stream", function() {
-      store.setStoreState({videoMuted: true});
+      store.setStoreState({ videoMuted: true });
 
       store.setMute(new sharedActions.SetMute({
         type: "video",
         enabled: false
       }));
 
       expect(store.getStoreState().videoMuted).eql(true);
     });
   });
 
   describe("#mediaStreamCreated", function() {
     var fakeStreamElement;
 
     beforeEach(function() {
-      fakeStreamElement = {name: "fakeStreamElement"};
+      fakeStreamElement = { name: "fakeStreamElement" };
     });
 
     it("should add a local video object to the store", function() {
       expect(store.getStoreState()).to.not.have.property("localSrcMediaElement");
 
       store.mediaStreamCreated(new sharedActions.MediaStreamCreated({
         hasVideo: false,
         isLocal: true,
@@ -1394,17 +1394,17 @@ describe("loop.store.ActiveRoomStore", f
       expect(store.getStoreState().remoteVideoEnabled).eql(true);
     });
   });
 
   describe("#mediaStreamDestroyed", function() {
     var fakeStreamElement;
 
     beforeEach(function() {
-      fakeStreamElement = {name: "fakeStreamElement"};
+      fakeStreamElement = { name: "fakeStreamElement" };
 
       store.setStoreState({
         localSrcMediaElement: fakeStreamElement,
         remoteSrcMediaElement: fakeStreamElement
       });
     });
 
     it("should clear the local video object", function() {
@@ -1457,17 +1457,17 @@ describe("loop.store.ActiveRoomStore", f
       store.mediaConnected();
 
       expect(store.getStoreState().mediaConnected).eql(true);
     });
   });
 
   describe("#screenSharingState", function() {
     beforeEach(function() {
-      store.setStoreState({windowId: "1234"});
+      store.setStoreState({ windowId: "1234" });
     });
 
     it("should save the state", function() {
       store.screenSharingState(new sharedActions.ScreenSharingState({
         state: SCREEN_SHARE_STATES.ACTIVE
       }));
 
       expect(store.getStoreState().screenSharingState).eql(SCREEN_SHARE_STATES.ACTIVE);
@@ -1497,17 +1497,17 @@ describe("loop.store.ActiveRoomStore", f
       store.receivingScreenShare(new sharedActions.ReceivingScreenShare({
         receiving: true
       }));
 
       expect(store.getStoreState().receivingScreenShare).eql(true);
     });
 
     it("should add a screenShareMediaElement to the store when sharing is active", function() {
-      var fakeStreamElement = {name: "fakeStreamElement"};
+      var fakeStreamElement = { name: "fakeStreamElement" };
       expect(store.getStoreState()).to.not.have.property("screenShareMediaElement");
 
       store.receivingScreenShare(new sharedActions.ReceivingScreenShare({
         receiving: true,
         srcMediaElement: fakeStreamElement
       }));
 
       expect(store.getStoreState()).to.have.property("screenShareMediaElement",
@@ -1527,27 +1527,27 @@ describe("loop.store.ActiveRoomStore", f
       }));
 
       expect(store.getStoreState().screenShareMediaElement).eql(null);
     });
 
     it("should delete the screen remote video dimensions if screen sharing is not active", function() {
       store.setStoreState({
         remoteVideoDimensions: {
-          screen: {fake: 10},
-          camera: {fake: 20}
+          screen: { fake: 10 },
+          camera: { fake: 20 }
         }
       });
 
       store.receivingScreenShare(new sharedActions.ReceivingScreenShare({
         receiving: false
       }));
 
       expect(store.getStoreState().remoteVideoDimensions).eql({
-        camera: {fake: 20}
+        camera: { fake: 20 }
       });
     });
   });
 
   describe("#startScreenShare", function() {
     it("should set the state to 'pending'", function() {
       store.startScreenShare(new sharedActions.StartScreenShare({
         type: "window"
@@ -1698,29 +1698,29 @@ describe("loop.store.ActiveRoomStore", f
 
       store.remotePeerDisconnected();
 
       expect(store.getStoreState().remoteSrcMediaElement).eql(null);
     });
 
     it("should remove non-owner participants", function() {
       store.setStoreState({
-        participants: [{owner: true}, {}]
+        participants: [{ owner: true }, {}]
       });
 
       store.remotePeerDisconnected();
 
       var participants = store.getStoreState().participants;
       expect(participants).to.have.length.of(1);
       expect(participants[0].owner).eql(true);
     });
 
     it("should keep the owner participant", function() {
       store.setStoreState({
-        participants: [{owner: true}]
+        participants: [{ owner: true }]
       });
 
       store.remotePeerDisconnected();
 
       var participants = store.getStoreState().participants;
       expect(participants).to.have.length.of(1);
       expect(participants[0].owner).eql(true);
     });
@@ -1794,17 +1794,17 @@ describe("loop.store.ActiveRoomStore", f
 
       sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
       sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
         "fakeToken", "1627384950");
     });
 
     it("should call mozLoop.rooms.leave if the room state is JOINING",
       function() {
-        store.setStoreState({roomState: ROOM_STATES.JOINING});
+        store.setStoreState({ roomState: ROOM_STATES.JOINING });
 
         store.windowUnload();
 
         sinon.assert.calledOnce(fakeMozLoop.rooms.leave);
         sinon.assert.calledWithExactly(fakeMozLoop.rooms.leave,
           "fakeToken", "1627384950");
       });
 
--- a/browser/components/loop/test/shared/crypto_test.js
+++ b/browser/components/loop/test/shared/crypto_test.js
@@ -50,34 +50,34 @@ describe("loop.crypto", function() {
 
       expect(function() {
         loop.crypto.encryptBytes();
       }).to.Throw(/not supported/);
     });
 
     it("should encrypt an object with a specific key", function() {
       return expect(loop.crypto.encryptBytes("Wt2-bZKeHO2wnaq00ZM6Nw",
-        JSON.stringify({test: true}))).to.eventually.be.a("string");
+        JSON.stringify({ test: true }))).to.eventually.be.a("string");
     });
   });
 
   describe("#decryptBytes", function() {
     it("should throw if web crypto is not available", function() {
       loop.crypto.setRootObject({});
 
       expect(function() {
         loop.crypto.decryptBytes();
       }).to.Throw(/not supported/);
     });
 
     it("should decypt an object via a specific key", function() {
       var key = "Wt2-bZKeHO2wnaq00ZM6Nw";
       var encryptedContext = "XvN9FDEm/GtE/5Bx5ezpn7JVDeZrtwOJy2CBjTGgJ4L33HhHOqEW+5k=";
 
-      return expect(loop.crypto.decryptBytes(key, encryptedContext)).to.eventually.eql(JSON.stringify({test: true}));
+      return expect(loop.crypto.decryptBytes(key, encryptedContext)).to.eventually.eql(JSON.stringify({ test: true }));
     });
 
     it("should fail if the key didn't work", function() {
       var bad = "Bad-bZKeHO2wnaq00ZM6Nw";
       var encryptedContext = "TGZaAE3mqsBFK0GfheZXXDCaRKXJmIKJ8WzF0KBEl4Aldzf3iYlAsLQdA8XSXXvtJR2UYz+f";
 
       return expect(loop.crypto.decryptBytes(bad, encryptedContext)).to.be.rejected;
     });
@@ -85,17 +85,17 @@ describe("loop.crypto", function() {
 
   describe("Full cycle", function() {
     it("should be able to encrypt and decypt in a full cycle", function(done) {
       var context = JSON.stringify({
         contextObject: true,
         UTF8String: "对话"
       });
 
-      return loop.crypto.generateKey().then(function (key) {
+      return loop.crypto.generateKey().then(function(key) {
         loop.crypto.encryptBytes(key, context).then(function(encryptedContext) {
           loop.crypto.decryptBytes(key, encryptedContext).then(function(decryptedContext) {
             expect(decryptedContext).eql(context);
             done();
           }).catch(function(error) {
             done(error);
           });
         }).catch(function(error) {
--- a/browser/components/loop/test/shared/dispatcher_test.js
+++ b/browser/components/loop/test/shared/dispatcher_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.Dispatcher", function () {
+describe("loop.Dispatcher", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var dispatcher, sandbox;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
--- a/browser/components/loop/test/shared/linkifiedTextView_test.js
+++ b/browser/components/loop/test/shared/linkifiedTextView_test.js
@@ -23,54 +23,54 @@
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
-describe("loop.shared.views.LinkifiedTextView", function () {
+describe("loop.shared.views.LinkifiedTextView", function() {
   "use strict";
 
   var expect = chai.expect;
   var LinkifiedTextView = loop.shared.views.LinkifiedTextView;
   var TestUtils = React.addons.TestUtils;
 
-  describe("LinkifiedTextView", function () {
+  describe("LinkifiedTextView", function() {
     function renderToMarkup(string, extraProps) {
       return React.renderToStaticMarkup(
         React.createElement(
           LinkifiedTextView,
-          _.extend({rawText: string}, extraProps)));
+          _.extend({ rawText: string }, extraProps)));
     }
 
     describe("#render", function() {
       function testRender(testData) {
         it(testData.desc, function() {
           var markup = renderToMarkup(testData.rawText,
-            {suppressTarget: true, sendReferrer: true});
+            { suppressTarget: true, sendReferrer: true });
 
           expect(markup).to.equal(testData.markup);
         });
       }
 
       function testSkip(testData) {
         it.skip(testData.desc, function() {
           var markup = renderToMarkup(testData.rawText,
-            {suppressTarget: true, sendReferrer: true});
+            { suppressTarget: true, sendReferrer: true });
 
           expect(markup).to.equal(testData.markup);
         });
       }
 
       describe("this.props.suppressTarget", function() {
         it("should make links w/o a target attr if suppressTarget is true",
           function() {
-            var markup = renderToMarkup("http://example.com", {suppressTarget: true});
+            var markup = renderToMarkup("http://example.com", { suppressTarget: true });
 
             expect(markup).to.equal(
               '<p><a href="http://example.com" rel="noreferrer">http://example.com</a></p>');
           });
 
         it("should make links with target=_blank if suppressTarget is not given",
           function() {
             var markup = renderToMarkup("http://example.com", {});
@@ -78,43 +78,43 @@ describe("loop.shared.views.LinkifiedTex
             expect(markup).to.equal(
               '<p><a href="http://example.com" target="_blank" rel="noreferrer">http://example.com</a></p>');
           });
       });
 
       describe("this.props.sendReferrer", function() {
         it("should make links w/o rel=noreferrer if sendReferrer is true",
           function() {
-            var markup = renderToMarkup("http://example.com", {sendReferrer: true});
+            var markup = renderToMarkup("http://example.com", { sendReferrer: true });
 
             expect(markup).to.equal(
               '<p><a href="http://example.com" target="_blank">http://example.com</a></p>');
           });
 
         it("should make links with rel=noreferrer if sendReferrer is not given",
           function() {
             var markup = renderToMarkup("http://example.com", {});
 
             expect(markup).to.equal(
               '<p><a href="http://example.com" target="_blank" rel="noreferrer">http://example.com</a></p>');
           });
       });
 
-      describe("this.props.linkClickHandler", function () {
+      describe("this.props.linkClickHandler", function() {
         function mountTestComponent(string, extraProps) {
           return TestUtils.renderIntoDocument(
             React.createElement(
               LinkifiedTextView,
-              _.extend({rawText: string}, extraProps)));
+              _.extend({ rawText: string }, extraProps)));
         }
 
-        it("should be called when a generated link is clicked", function () {
+        it("should be called when a generated link is clicked", function() {
           var fakeUrl = "http://example.com";
           var linkClickHandler = sinon.stub();
-          var comp = mountTestComponent(fakeUrl, {linkClickHandler: linkClickHandler});
+          var comp = mountTestComponent(fakeUrl, { linkClickHandler: linkClickHandler });
 
           TestUtils.Simulate.click(comp.getDOMNode().querySelector("a"));
 
           sinon.assert.calledOnce(linkClickHandler);
         });
 
         it("should cause sendReferrer and suppressTarget props to be ignored",
           function() {
@@ -126,54 +126,54 @@ describe("loop.shared.views.LinkifiedTex
               sendReferrer: false,
               suppressTarget: false
             });
 
             expect(markup).to.equal(
               '<p><a href="http://example.com">http://example.com</a></p>');
           });
 
-        describe("#_handleClickEvent", function () {
+        describe("#_handleClickEvent", function() {
           var fakeEvent;
           var fakeUrl = "http://example.com";
 
           beforeEach(function() {
             fakeEvent = {
               currentTarget: { href: fakeUrl },
               preventDefault: sinon.stub(),
               stopPropagation: sinon.stub()
             };
           });
 
-          it("should call preventDefault on the given event", function () {
+          it("should call preventDefault on the given event", function() {
             function linkClickHandler() {}
             var comp = mountTestComponent(
-              fakeUrl, {linkClickHandler: linkClickHandler});
+              fakeUrl, { linkClickHandler: linkClickHandler });
 
             comp._handleClickEvent(fakeEvent);
 
             sinon.assert.calledOnce(fakeEvent.preventDefault);
             sinon.assert.calledWithExactly(fakeEvent.stopPropagation);
           });
 
-          it("should call stopPropagation on the given event", function () {
+          it("should call stopPropagation on the given event", function() {
             function linkClickHandler() {}
             var comp = mountTestComponent(
-              fakeUrl, {linkClickHandler: linkClickHandler});
+              fakeUrl, { linkClickHandler: linkClickHandler });
 
             comp._handleClickEvent(fakeEvent);
 
             sinon.assert.calledOnce(fakeEvent.stopPropagation);
             sinon.assert.calledWithExactly(fakeEvent.stopPropagation);
           });
 
-          it("should call this.props.linkClickHandler with event.currentTarget.href", function () {
+          it("should call this.props.linkClickHandler with event.currentTarget.href", function() {
             var linkClickHandler = sinon.stub();
             var comp = mountTestComponent(
-              fakeUrl, {linkClickHandler: linkClickHandler});
+              fakeUrl, { linkClickHandler: linkClickHandler });
 
             comp._handleClickEvent(fakeEvent);
 
             sinon.assert.calledOnce(linkClickHandler);
             sinon.assert.calledWithExactly(linkClickHandler, fakeUrl);
           });
         });
       });
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -19,17 +19,17 @@ describe("loop.shared.mixins", function(
     sandbox.restore();
     sharedMixins.setRootObject(window);
   });
 
   describe("loop.shared.mixins.UrlHashChangeMixin", function() {
     function createTestComponent(onUrlHashChange) {
       var TestComp = React.createClass({
         mixins: [loop.shared.mixins.UrlHashChangeMixin],
-        onUrlHashChange: onUrlHashChange || function(){},
+        onUrlHashChange: onUrlHashChange || function() {},
         render: function() {
           return React.DOM.div();
         }
       });
       return new React.createElement(TestComp);
     }
 
     it("should watch for hashchange event", function() {
@@ -168,27 +168,27 @@ describe("loop.shared.mixins", function(
           },
           removeEventListener: sandbox.stub()
         }
       });
     }
 
     it("should call onDocumentVisible when document visibility changes to visible",
       function() {
-        setupFakeVisibilityEventDispatcher({target: {hidden: false}});
+        setupFakeVisibilityEventDispatcher({ target: { hidden: false } });
 
         comp = TestUtils.renderIntoDocument(React.createElement(TestComp));
 
         // Twice, because it's also called when the component was mounted.
         sinon.assert.calledTwice(onDocumentVisibleStub);
       });
 
     it("should call onDocumentVisible when document visibility changes to hidden",
       function() {
-        setupFakeVisibilityEventDispatcher({target: {hidden: true}});
+        setupFakeVisibilityEventDispatcher({ target: { hidden: true } });
 
         comp = TestUtils.renderIntoDocument(React.createElement(TestComp));
 
         sinon.assert.calledOnce(onDocumentHiddenStub);
       });
   });
 
   describe("loop.shared.mixins.MediaSetupMixin", function() {
@@ -222,17 +222,17 @@ describe("loop.shared.mixins", function(
 
   describe("loop.shared.mixins.AudioMixin", function() {
     var view, fakeAudio, TestComp;
 
     beforeEach(function() {
       navigator.mozLoop = {
         doNotDisturb: true,
         getAudioBlob: sinon.spy(function(name, callback) {
-          callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
+          callback(null, new Blob([new ArrayBuffer(10)], { type: "audio/ogg" }));
         }),
         getLoopPref: sandbox.stub()
       };
 
       fakeAudio = {
         play: sinon.spy(),
         pause: sinon.spy(),
         removeAttribute: sinon.spy()
@@ -274,76 +274,76 @@ describe("loop.shared.mixins", function(
     function createTestComponent(initialState) {
       var TestComp = React.createClass({
         mixins: [loop.shared.mixins.RoomsAudioMixin],
         render: function() {
           return React.DOM.div();
         },
 
         getInitialState: function() {
-          return { roomState: initialState};
+          return { roomState: initialState };
         }
       });
 
       var renderedComp = TestUtils.renderIntoDocument(
         React.createElement(TestComp));
       sandbox.stub(renderedComp, "play");
       return renderedComp;
     }
 
     beforeEach(function() {
     });
 
     it("should play a sound when the local user joins the room", function() {
       comp = createTestComponent(ROOM_STATES.INIT);
 
-      comp.setState({roomState: ROOM_STATES.SESSION_CONNECTED});
+      comp.setState({ roomState: ROOM_STATES.SESSION_CONNECTED });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "room-joined");
     });
 
     it("should play a sound when another user joins the room", function() {
       comp = createTestComponent(ROOM_STATES.SESSION_CONNECTED);
 
-      comp.setState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+      comp.setState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "room-joined-in");
     });
 
     it("should play a sound when another user leaves the room", function() {
       comp = createTestComponent(ROOM_STATES.HAS_PARTICIPANTS);
 
-      comp.setState({roomState: ROOM_STATES.SESSION_CONNECTED});
+      comp.setState({ roomState: ROOM_STATES.SESSION_CONNECTED });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "room-left");
     });
 
     it("should play a sound when the local user leaves the room", function() {
       comp = createTestComponent(ROOM_STATES.HAS_PARTICIPANTS);
 
-      comp.setState({roomState: ROOM_STATES.READY});
+      comp.setState({ roomState: ROOM_STATES.READY });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "room-left");
     });
 
     it("should play a sound when if there is a failure", function() {
       comp = createTestComponent(ROOM_STATES.HAS_PARTICIPANTS);
 
-      comp.setState({roomState: ROOM_STATES.FAILED});
+      comp.setState({ roomState: ROOM_STATES.FAILED });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "failure");
     });
 
     it("should play a sound when if the room is full", function() {
       comp = createTestComponent(ROOM_STATES.READY);
 
-      comp.setState({roomState: ROOM_STATES.FULL});
+      comp.setState({ roomState: ROOM_STATES.FULL });
 
       sinon.assert.calledOnce(comp.play);
       sinon.assert.calledWithExactly(comp.play, "failure");
     });
   });
 });
--- a/browser/components/loop/test/shared/models_test.js
+++ b/browser/components/loop/test/shared/models_test.js
@@ -21,17 +21,17 @@ describe("loop.shared.models", function(
   describe("NotificationCollection", function() {
     var collection, notifData, testNotif;
 
     beforeEach(function() {
       collection = new sharedModels.NotificationCollection();
       sandbox.stub(l10n, "get", function(x, y) {
         return "translated:" + x + (y ? ":" + y : "");
       });
-      notifData = {level: "error", message: "plop"};
+      notifData = { level: "error", message: "plop" };
       testNotif = new sharedModels.NotificationModel(notifData);
     });
 
     describe("#warn", function() {
       it("should add a warning notification to the stack", function() {
         collection.warn("watch out");
 
         expect(collection).to.have.length.of(1);
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.OTSdkDriver", function () {
+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 CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
@@ -105,17 +105,17 @@ describe("loop.OTSdkDriver", function ()
     it("should throw an error if the dispatcher is missing", function() {
       expect(function() {
         new loop.OTSdkDriver({ sdk: sdk });
       }).to.Throw(/dispatcher/);
     });
 
     it("should throw an error if the sdk is missing", function() {
       expect(function() {
-        new loop.OTSdkDriver({dispatcher: dispatcher});
+        new loop.OTSdkDriver({ dispatcher: dispatcher });
       }).to.Throw(/sdk/);
     });
 
     it("should set the metrics to zero", function() {
       driver = new loop.OTSdkDriver({
         dispatcher: dispatcher,
         sdk: sdk
       });
@@ -335,27 +335,27 @@ describe("loop.OTSdkDriver", function ()
   describe("#connectSession", function() {
     it("should initialise a new session", function() {
       driver.connectSession(sessionData);
 
       sinon.assert.calledOnce(sdk.initSession);
       sinon.assert.calledWithExactly(sdk.initSession, "3216549870");
     });
 
-    it("should connect the session", function () {
+    it("should connect the session", function() {
       driver.connectSession(sessionData);
 
       sinon.assert.calledOnce(session.connect);
       sinon.assert.calledWith(session.connect, "1234567890", "1357924680");
     });
 
     it("should set the two-way media start time to 'uninitialized' " +
        "when sessionData.sendTwoWayMediaTelemetry is true'", function() {
       driver.connectSession(_.extend(sessionData,
-                                     {sendTwoWayMediaTelemetry: true}));
+                                     { sendTwoWayMediaTelemetry: true }));
 
       expect(driver._getTwoWayMediaStartTime()).to.eql(
         driver.CONNECTION_START_TIME_UNINITIALIZED);
     });
 
     describe("On connection complete", function() {
       beforeEach(function() {
         sandbox.stub(window.console, "error");
@@ -697,26 +697,26 @@ describe("loop.OTSdkDriver", function ()
       driver._sessionConnected = true;
 
       // Setup the right state in the driver to make `forceDisconnectAll` do
       // something.
       session.connection = {
         id: "localUser"
       };
       session.trigger("connectionCreated", {
-        connection: {id: "remoteUser"}
+        connection: { id: "remoteUser" }
       });
       expect(driver.connections).to.include.keys("remoteUser");
 
       driver.forceDisconnectAll(function() {});
       sinon.assert.calledOnce(session.forceDisconnect);
 
       // Add another remote connection.
       session.trigger("connectionCreated", {
-        connection: {id: "remoteUser2"}
+        connection: { id: "remoteUser2" }
       });
       expect(driver.connections).to.include.keys("remoteUser", "remoteUser2");
 
       driver.forceDisconnectAll(function() {});
       sinon.assert.calledThrice(session.forceDisconnect);
     });
 
     describe("#sendTextChatMessage", function() {
@@ -743,17 +743,17 @@ describe("loop.OTSdkDriver", function ()
     var fakeConnection, fakeStream, fakeSubscriberObject,
       fakeSdkContainerWithVideo, videoElement;
 
     beforeEach(function() {
       fakeConnection = "fakeConnection";
       fakeStream = {
         hasVideo: true,
         videoType: "camera",
-        videoDimensions: {width: 1, height: 2}
+        videoDimensions: { width: 1, height: 2 }
       };
 
       fakeSubscriberObject = _.extend({
         session: { connection: fakeConnection },
         stream: fakeStream
       }, Backbone.Events);
 
       fakeSdkContainerWithVideo = {
@@ -902,29 +902,29 @@ describe("loop.OTSdkDriver", function ()
       beforeEach(function() {
         driver._mockPublisherEl = document.createElement("div");
         fakeMockVideo = document.createElement("video");
 
         driver._mockPublisherEl.appendChild(fakeMockVideo);
         stream = {
           hasVideo: true,
           videoType: "camera",
-          videoDimensions: {width: 1, height: 2}
+          videoDimensions: { width: 1, height: 2 }
         };
       });
 
       it("should dispatch a VideoDimensionsChanged action", function() {
         publisher.trigger("streamCreated", { stream: stream });
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.VideoDimensionsChanged({
             isLocal: true,
             videoType: "camera",
-            dimensions: {width: 1, height: 2}
+            dimensions: { width: 1, height: 2 }
           }));
       });
 
       it("should dispatch a MediaStreamCreated action", function() {
         publisher.trigger("streamCreated", { stream: stream });
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
@@ -947,17 +947,17 @@ describe("loop.OTSdkDriver", function ()
             srcMediaElement: fakeMockVideo
           }));
       });
 
       it("should dispatch a ConnectionStatus action", function() {
         driver._metrics.recvStreams = 1;
         driver._metrics.connections = 2;
 
-        publisher.trigger("streamCreated", {stream: stream});
+        publisher.trigger("streamCreated", { stream: stream });
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.ConnectionStatus({
             event: "Publisher.streamCreated",
             state: "sendrecv",
             connections: 2,
             recvStreams: 1,
@@ -1127,20 +1127,20 @@ describe("loop.OTSdkDriver", function ()
           session.trigger("streamCreated", { stream: fakeStream });
 
           sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
             sinon.match.hasOwn("name", "mediaConnected"));
         });
 
       it("should not dispatch a ReceivingScreenShare action for camera streams",
         function() {
-          session.trigger("streamCreated", {stream: fakeStream});
+          session.trigger("streamCreated", { stream: fakeStream });
 
           sinon.assert.neverCalledWithMatch(dispatcher.dispatch,
-            new sharedActions.ReceivingScreenShare({receiving: true}));
+            new sharedActions.ReceivingScreenShare({ receiving: true }));
         });
 
       it("should dispatch a ReceivingScreenShare action for screen" +
         " sharing streams", function() {
           fakeStream.videoType = "screen";
 
           session.trigger("streamCreated", { stream: fakeStream });
 
@@ -1210,17 +1210,17 @@ describe("loop.OTSdkDriver", function ()
           }));
       });
 
       it("should dispatch a ConnectionStatus action", function() {
         driver._metrics.connections = 2;
         driver._metrics.sendStreams = 1;
         driver._metrics.recvStreams = 1;
 
-        session.trigger("streamDestroyed", {stream: stream});
+        session.trigger("streamDestroyed", { stream: stream });
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.ConnectionStatus({
             event: "Session.streamDestroyed",
             state: "sending",
             connections: 2,
             recvStreams: 0,
@@ -1443,34 +1443,34 @@ describe("loop.OTSdkDriver", function ()
         sinon.assert.calledOnce(fakeEvent.preventDefault);
       });
     });
 
     describe("videoEnabled", function() {
       it("should dispatch a RemoteVideoStatus action", function() {
         session.subscribe.yieldsOn(driver, null, fakeSubscriberObject,
           videoElement).returns(this.fakeSubscriberObject);
-        session.trigger("streamCreated", {stream: fakeSubscriberObject.stream});
+        session.trigger("streamCreated", { stream: fakeSubscriberObject.stream });
         driver._mockSubscribeEl.appendChild(videoElement);
 
         fakeSubscriberObject.trigger("videoEnabled");
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWith(dispatcher.dispatch,
           new sharedActions.RemoteVideoStatus({
             videoEnabled: true
           }));
       });
     });
 
     describe("videoDisabled", function() {
       it("should dispatch a RemoteVideoStatus action", function() {
         session.subscribe.yieldsOn(driver, null, fakeSubscriberObject,
           videoElement).returns(this.fakeSubscriberObject);
-        session.trigger("streamCreated", {stream: fakeSubscriberObject.stream});
+        session.trigger("streamCreated", { stream: fakeSubscriberObject.stream });
 
 
         fakeSubscriberObject.trigger("videoDisabled");
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.RemoteVideoStatus({
             videoEnabled: false
--- a/browser/components/loop/test/shared/sdk_mock.js
+++ b/browser/components/loop/test/shared/sdk_mock.js
@@ -2,17 +2,17 @@
  * 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/. */
 
 /**
  * This file mocks the functions from the OT sdk that we use. This is to provide
  * an interface that tests can mock out, without needing to maintain a copy of
  * the sdk or load one from the network.
  */
-(function (window) {
+(function(window) {
   "use strict";
 
   if (!window.OT) {
     window.OT = {};
   }
 
   window.OT.checkSystemRequirements = function() {
     return true;
--- a/browser/components/loop/test/shared/store_test.js
+++ b/browser/components/loop/test/shared/store_test.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/. */
 
-describe("loop.store", function () {
+describe("loop.store", function() {
   "use strict";
 
   var expect = chai.expect;
   var dispatcher;
   var sandbox;
   var sharedActions = loop.shared.actions;
   var TestUtils = React.addons.TestUtils;
 
@@ -35,18 +35,18 @@ describe("loop.store", function () {
           var TestStore = loop.store.createStore({});
           expect(function() {
             new TestStore();
           }).to.Throw(/required dispatcher/);
         });
 
         it("should call initialize() when constructed, if defined", function() {
           var initialize = sandbox.spy();
-          var TestStore = loop.store.createStore({initialize: initialize});
-          var options = {fake: true};
+          var TestStore = loop.store.createStore({ initialize: initialize });
+          var options = { fake: true };
 
           new TestStore(dispatcher, options);
 
           sinon.assert.calledOnce(initialize);
           sinon.assert.calledWithExactly(initialize, options);
         });
 
         it("should register actions", function() {
@@ -74,23 +74,23 @@ describe("loop.store", function () {
           }).to.Throw(/should implement an action handler for b/);
         });
       });
 
       describe("#getInitialStoreState", function() {
         it("should set initial store state if provided", function() {
           var TestStore = loop.store.createStore({
             getInitialStoreState: function() {
-              return {foo: "bar"};
+              return { foo: "bar" };
             }
           });
 
           var store = new TestStore(dispatcher);
 
-          expect(store.getStoreState()).eql({foo: "bar"});
+          expect(store.getStoreState()).eql({ foo: "bar" });
         });
       });
 
       describe("#dispatchAction", function() {
         it("should dispatch an action", function() {
           sandbox.stub(dispatcher, "dispatch");
           var TestStore = loop.store.createStore({});
           var TestAction = sharedActions.Action.define("TestAction", {});
@@ -105,101 +105,101 @@ describe("loop.store", function () {
       });
 
       describe("#getStoreState", function() {
         var TestStore = loop.store.createStore({});
         var store;
 
         beforeEach(function() {
           store = new TestStore(dispatcher);
-          store.setStoreState({foo: "bar", bar: "baz"});
+          store.setStoreState({ foo: "bar", bar: "baz" });
         });
 
         it("should retrieve the whole state by default", function() {
-          expect(store.getStoreState()).eql({foo: "bar", bar: "baz"});
+          expect(store.getStoreState()).eql({ foo: "bar", bar: "baz" });
         });
 
         it("should retrieve a given property state", function() {
           expect(store.getStoreState("bar")).eql("baz");
         });
       });
 
       describe("#setStoreState", function() {
         var TestStore = loop.store.createStore({});
         var store;
 
         beforeEach(function() {
           store = new TestStore(dispatcher);
-          store.setStoreState({foo: "bar"});
+          store.setStoreState({ foo: "bar" });
         });
 
         it("should update store state data", function() {
-          store.setStoreState({foo: "baz"});
+          store.setStoreState({ foo: "baz" });
 
           expect(store.getStoreState("foo")).eql("baz");
         });
 
         it("should trigger a `change` event", function(done) {
           store.once("change", function() {
             done();
           });
 
-          store.setStoreState({foo: "baz"});
+          store.setStoreState({ foo: "baz" });
         });
 
         it("should trigger a `change:<prop>` event", function(done) {
           store.once("change:foo", function() {
             done();
           });
 
-          store.setStoreState({foo: "baz"});
+          store.setStoreState({ foo: "baz" });
         });
       });
     });
   });
 
   describe("loop.store.StoreMixin", function() {
     var view1, view2, store, storeClass, testComp;
 
     beforeEach(function() {
       storeClass = loop.store.createStore({});
 
       store = new storeClass(dispatcher);
 
-      loop.store.StoreMixin.register({store: store});
+      loop.store.StoreMixin.register({ store: store });
 
       testComp = React.createClass({
         mixins: [loop.store.StoreMixin("store")],
         render: function() {
           return React.DOM.div();
         }
       });
 
       view1 = TestUtils.renderIntoDocument(React.createElement(testComp));
     });
 
     it("should update the state when the store changes", function() {
-      store.setStoreState({test: true});
+      store.setStoreState({ test: true });
 
-      expect(view1.state).eql({test: true});
+      expect(view1.state).eql({ test: true });
     });
 
     it("should stop listening to state changes", function() {
       // There's no easy way in TestUtils to unmount, so simulate it.
       view1.componentWillUnmount();
 
-      store.setStoreState({test2: true});
+      store.setStoreState({ test2: true });
 
       expect(view1.state).eql(null);
     });
 
     it("should not stop listening to state changes on other components", function() {
       view2 = TestUtils.renderIntoDocument(React.createElement(testComp));
 
       // There's no easy way in TestUtils to unmount, so simulate it.
       view1.componentWillUnmount();
 
-      store.setStoreState({test3: true});
+      store.setStoreState({ test3: true });
 
-      expect(view2.state).eql({test3: true});
+      expect(view2.state).eql({ test3: true });
     });
   });
 });
--- a/browser/components/loop/test/shared/textChatStore_test.js
+++ b/browser/components/loop/test/shared/textChatStore_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.store.TextChatStore", function () {
+describe("loop.store.TextChatStore", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
   var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
 
   var dispatcher, fakeSdkDriver, sandbox, store;
--- a/browser/components/loop/test/shared/textChatView_test.js
+++ b/browser/components/loop/test/shared/textChatView_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.shared.views.TextChatView", function () {
+describe("loop.shared.views.TextChatView", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sharedViews = loop.shared.views;
   var TestUtils = React.addons.TestUtils;
   var CHAT_MESSAGE_TYPES = loop.store.CHAT_MESSAGE_TYPES;
   var CHAT_CONTENT_TYPES = loop.shared.utils.CHAT_CONTENT_TYPES;
@@ -371,17 +371,17 @@ describe("loop.shared.views.TextChatView
       });
       var node = view.getDOMNode();
 
       expect(node.querySelector(".text-chat-entry-timestamp")).to.not.eql(null);
     });
 
     // note that this is really an integration test to be sure that we don't
     // inadvertently regress using LinkifiedTextView.
-    it("should linkify a URL starting with http", function (){
+    it("should linkify a URL starting with http", function() {
       view = mountTestComponent({
         showTimestamp: true,
         timestamp: "2015-06-23T22:48:39.738Z",
         type: CHAT_MESSAGE_TYPES.RECEIVED,
         contentType: CHAT_CONTENT_TYPES.TEXT,
         message: "Check out http://example.com and see what you think..."
       });
       var node = view.getDOMNode();
--- a/browser/components/loop/test/shared/utils_test.js
+++ b/browser/components/loop/test/shared/utils_test.js
@@ -60,17 +60,17 @@ describe("loop.shared.utils", function()
     });
 
     it("should call toLocaleDateString with arguments", function() {
       sharedUtils.formatDate(1000);
 
       sinon.assert.calledOnce(Date.prototype.toLocaleDateString);
       sinon.assert.calledWithExactly(Date.prototype.toLocaleDateString,
         navigator.language,
-        {year: "numeric", month: "long", day: "numeric"}
+        { year: "numeric", month: "long", day: "numeric" }
       );
     });
 
     it("should return the formatted string", function() {
       expect(sharedUtils.formatDate(1000)).eql("fake result");
     });
   });
 
@@ -316,17 +316,17 @@ describe("loop.shared.utils", function()
   });
 
   describe("#composeCallUrlEmail", function() {
     var composeEmail, telemetryAddValue;
 
     beforeEach(function() {
       // fake mozL10n
       sandbox.stub(navigator.mozL10n, "get", function(id) {
-        switch(id) {
+        switch (id) {
           case "share_email_subject6":
             return "subject";
           case "share_email_body6":
             return "body";
           case "share_email_body_context2":
             return "body_context";
           case "share_email_footer":
             return "footer";
@@ -361,17 +361,17 @@ describe("loop.shared.utils", function()
       sinon.assert.calledOnce(composeEmail);
       sinon.assert.calledWith(composeEmail, "subject", "body_context" + "footer");
     });
 
     it("should record a telemetry event when an email is composed", function() {
       sharedUtils.composeCallUrlEmail("http://invalid", null,
         "Hello, is me you're looking for?", "callfailed");
 
-      sinon.assert.calledOnce(telemetryAddValue, "LOOP_SHARING_ROOM_URL",  2);
+      sinon.assert.calledOnce(telemetryAddValue, "LOOP_SHARING_ROOM_URL", 2);
     });
 
     it("should log an error for invalid URLs", function() {
       sharedUtils.composeCallUrlEmail("http://invalid", "fake@invalid.tld");
 
       sinon.assert.calledOnce(console.error);
     });
   });
--- a/browser/components/loop/test/shared/validate_test.js
+++ b/browser/components/loop/test/shared/validate_test.js
@@ -8,80 +8,80 @@ describe("Validator", function() {
 
   // test helpers
   function create(dependencies, values) {
     var validator = new loop.validate.Validator(dependencies);
     return validator.validate.bind(validator, values);
   }
 
   // test types
-  function X(){}
-  function Y(){}
+  function X() {}
+  function Y() {}
 
   describe("#validate", function() {
     function RTCSessionDescription() {}
     var rtcsd;
 
     beforeEach(function() {
       rtcsd = new RTCSessionDescription();
     });
 
     it("should check for a single required dependency when no option passed",
       function() {
-        expect(create({x: Number}, {}))
+        expect(create({ x: Number }, {}))
           .to.Throw(TypeError, /missing required x$/);
       });
 
     it("should check for a missing required dependency, undefined passed",
       function() {
-        expect(create({x: Number}, {x: undefined}))
+        expect(create({ x: Number }, { x: undefined }))
           .to.Throw(TypeError, /missing required x$/);
       });
 
     it("should check for multiple missing required dependencies", function() {
-      expect(create({x: Number, y: String}, {}))
+      expect(create({ x: Number, y: String }, {}))
         .to.Throw(TypeError, /missing required x, y$/);
     });
 
     it("should check for required dependency types", function() {
-      expect(create({x: Number}, {x: "woops"})).to.Throw(
+      expect(create({ x: Number }, { x: "woops" })).to.Throw(
         TypeError, /invalid dependency: x; expected Number, got String$/);
     });
 
     it("should check for a dependency to match at least one of passed types",
       function() {
-        expect(create({x: [X, Y]}, {x: 42})).to.Throw(
+        expect(create({ x: [X, Y] }, { x: 42 })).to.Throw(
           TypeError, /invalid dependency: x; expected X, Y, got Number$/);
-        expect(create({x: [X, Y]}, {x: new Y()})).to.not.Throw();
+        expect(create({ x: [X, Y] }, { x: new Y() })).to.not.Throw();
       });
 
     it("should skip type check if required dependency type is undefined",
       function() {
-        expect(create({x: undefined}, {x: /whatever/})).not.to.Throw();
+        expect(create({ x: undefined }, { x: /whatever/ })).not.to.Throw();
       });
 
     it("should check for a String dependency", function() {
-      expect(create({foo: String}, {foo: 42})).to.Throw(
+      expect(create({ foo: String }, { foo: 42 })).to.Throw(
         TypeError, /invalid dependency: foo/);
     });
 
     it("should check for a Number dependency", function() {
-      expect(create({foo: Number}, {foo: "x"})).to.Throw(
+      expect(create({ foo: Number }, { foo: "x" })).to.Throw(
         TypeError, /invalid dependency: foo/);
     });
 
     it("should check for a custom constructor dependency", function() {
-      expect(create({foo: X}, {foo: null})).to.Throw(
+      expect(create({ foo: X }, { foo: null })).to.Throw(
         TypeError, /invalid dependency: foo; expected X, got null$/);
     });
 
     it("should check for a native constructor dependency", function() {
-      expect(create({foo: rtcsd}, {foo: "x"}))
+      expect(create({ foo: rtcsd }, { foo: "x" }))
         .to.Throw(TypeError,
                   /invalid dependency: foo; expected RTCSessionDescription/);
     });
 
     it("should check for a null dependency", function() {
-      expect(create({foo: null}, {foo: "x"})).to.Throw(
+      expect(create({ foo: null }, { foo: "x" })).to.Throw(
         TypeError, /invalid dependency: foo; expected null, got String$/);
     });
   });
 });
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/components/loop/test/shared/views_test.js
@@ -55,53 +55,53 @@ describe("loop.shared.views", function()
   });
 
   describe("MediaControlButton", function() {
     it("should render an enabled local audio button", function() {
       var comp = TestUtils.renderIntoDocument(
         React.createElement(sharedViews.MediaControlButton, {
           scope: "local",
           type: "audio",
-          action: function(){},
+          action: function() {},
           enabled: true
         }));
 
       expect(comp.getDOMNode().classList.contains("muted")).eql(false);
     });
 
     it("should render a muted local audio button", function() {
       var comp = TestUtils.renderIntoDocument(
           React.createElement(sharedViews.MediaControlButton, {
           scope: "local",
           type: "audio",
-          action: function(){},
+          action: function() {},
           enabled: false
         }));
 
       expect(comp.getDOMNode().classList.contains("muted")).eql(true);
     });
 
     it("should render an enabled local video button", function() {
       var comp = TestUtils.renderIntoDocument(
           React.createElement(sharedViews.MediaControlButton, {
           scope: "local",
           type: "video",
-          action: function(){},
+          action: function() {},
           enabled: true
         }));
 
       expect(comp.getDOMNode().classList.contains("muted")).eql(false);
     });
 
     it("should render a muted local video button", function() {
       var comp = TestUtils.renderIntoDocument(
         React.createElement(sharedViews.MediaControlButton, {
           scope: "local",
           type: "video",
-          action: function(){},
+          action: function() {},
           enabled: false
         }));
 
       expect(comp.getDOMNode().classList.contains("muted")).eql(true);
     });
   });
 
   describe("ScreenShareControlButton", function() {
@@ -287,17 +287,17 @@ describe("loop.shared.views", function()
   describe("SettingsControlButton", function() {
     var fakeMozLoop;
     var support_url = "https://support.com";
 
     beforeEach(function() {
       fakeMozLoop = {
         openURL: sandbox.stub(),
         setLoopPref: sandbox.stub(),
-        getLoopPref: function (prefName) {
+        getLoopPref: function(prefName) {
           switch (prefName) {
             case "support_url":
               return support_url;
             default:
               return prefName;
           }
         }
       };
@@ -309,49 +309,49 @@ describe("loop.shared.views", function()
       }, props);
 
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.SettingsControlButton, props));
     }
 
     it("should render a visible button", function() {
       var settingsMenuItems = [{ id: "help" }];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       var node = comp.getDOMNode().querySelector(".btn-settings");
       expect(node.classList.contains("hide")).eql(false);
     });
 
     it("should not render anything", function() {
       var comp = mountTestComponent();
       expect(comp.getDOMNode()).to.eql(null);
     });
 
     it("should not show an indefined menu option", function() {
       var settingsMenuItems = [
         { id: "not Defined" },
         { id: "help" }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
       var menuItems = comp.getDOMNode().querySelectorAll(".settings-menu > li");
       expect(menuItems).to.have.length.of(1);
     });
 
     it("should not render anythin if not exists any valid item to show", function() {
       var settingsMenuItems = [
         { id: "not Defined" },
         { id: "another wrong menu item" }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
       expect(comp.getDOMNode()).to.eql(null);
     });
 
     it("should show the settings dropdown on click", function() {
       var settingsMenuItems = [{ id: "help" }];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       expect(comp.state.showMenu).eql(false);
       TestUtils.Simulate.click(comp.getDOMNode().querySelector(".btn-settings"));
 
       expect(comp.state.showMenu).eql(true);
     });
 
     it("should have a `menu-below` class on the dropdown when the prop is set.", function() {
@@ -383,58 +383,58 @@ describe("loop.shared.views", function()
       var settingsMenuItems = [
         {
           id: "edit",
           enabled: true,
           visible: true,
           onClick: function() {}
         }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       var node = comp.getDOMNode().querySelector(".settings-menu > li.entry-settings-edit");
       expect(node.classList.contains("hide")).eql(false);
     });
 
     it("should hide edit Context on menu when the option is not visible", function() {
       var settingsMenuItems = [
         {
           id: "edit",
           enabled: false,
           visible: false,
           onClick: function() {}
         }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       var node = comp.getDOMNode().querySelector(".settings-menu > li.entry-settings-edit");
       expect(node.classList.contains("hide")).eql(true);
     });
 
     it("should call onClick method when the edit context menu item is clicked", function() {
       var onClickCalled = false;
       var settingsMenuItems = [
         {
           id: "edit",
           enabled: true,
           visible: true,
           onClick: sandbox.stub()
         }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       TestUtils.Simulate.click(comp.getDOMNode().querySelector(".settings-menu > li.entry-settings-edit"));
       sinon.assert.calledOnce(settingsMenuItems[0].onClick);
     });
 
     it("should open a tab to the support url when the support menu item is clicked", function() {
       var settingsMenuItems = [
         { id: "help" }
       ];
-      var comp = mountTestComponent({ menuItems: settingsMenuItems} );
+      var comp = mountTestComponent({ menuItems: settingsMenuItems });
 
       TestUtils.Simulate.click(comp.getDOMNode().querySelector(".settings-menu > li:last-child"));
 
       sinon.assert.calledOnce(fakeMozLoop.openURL);
       sinon.assert.calledWithExactly(fakeMozLoop.openURL, support_url);
     });
   });
 
@@ -516,73 +516,73 @@ describe("loop.shared.views", function()
 
       expect(comp.getDOMNode().querySelector(".btn-hangup-entry")).to.eql(null);
     });
 
     it("should hangup when hangup button is clicked", function() {
       var comp = mountTestComponent({
         hangup: hangup,
         publishStream: publishStream,
-        audio: {enabled: true}
+        audio: { enabled: true }
       });
 
       TestUtils.Simulate.click(
         comp.getDOMNode().querySelector(".btn-hangup"));
 
       sinon.assert.calledOnce(hangup);
       sinon.assert.calledWithExactly(hangup);
     });
 
     it("should unpublish audio when audio mute btn is clicked", function() {
       var comp = mountTestComponent({
         hangup: hangup,
         publishStream: publishStream,
-        audio: {enabled: true}
+        audio: { enabled: true }
       });
 
       TestUtils.Simulate.click(
         comp.getDOMNode().querySelector(".btn-mute-audio"));
 
       sinon.assert.calledOnce(publishStream);
       sinon.assert.calledWithExactly(publishStream, "audio", false);
     });
 
     it("should publish audio when audio mute btn is clicked", function() {
       var comp = mountTestComponent({
         hangup: hangup,
         publishStream: publishStream,
-        audio: {enabled: false}
+        audio: { enabled: false }
       });
 
       TestUtils.Simulate.click(
         comp.getDOMNode().querySelector(".btn-mute-audio"));
 
       sinon.assert.calledOnce(publishStream);
       sinon.assert.calledWithExactly(publishStream, "audio", true);
     });
 
     it("should unpublish video when video mute btn is clicked", function() {
       var comp = mountTestComponent({
         hangup: hangup,
         publishStream: publishStream,
-        video: {enabled: true}
+        video: { enabled: true }
       });
 
       TestUtils.Simulate.click(
         comp.getDOMNode().querySelector(".btn-mute-video"));
 
       sinon.assert.calledOnce(publishStream);
       sinon.assert.calledWithExactly(publishStream, "video", false);
     });
 
     it("should publish video when video mute btn is clicked", function() {
       var comp = mountTestComponent({
         hangup: hangup,
         publishStream: publishStream,
-        video: {enabled: false}
+        video: { enabled: false }
       });
 
       TestUtils.Simulate.click(
         comp.getDOMNode().querySelector(".btn-mute-video"));
 
       sinon.assert.calledOnce(publishStream);
       sinon.assert.calledWithExactly(publishStream, "video", true);
     });
@@ -596,18 +596,18 @@ describe("loop.shared.views", function()
         key: 0
       }, props || {});
       return TestUtils.renderIntoDocument(
         React.createElement(sharedViews.NotificationListView, props));
     }
 
     beforeEach(function() {
       coll = new sharedModels.NotificationCollection();
-      view = mountTestComponent({notifications: coll});
-      testNotif = {level: "warning", message: "foo"};
+      view = mountTestComponent({ notifications: coll });
+      testNotif = { level: "warning", message: "foo" };
       sinon.spy(view, "render");
     });
 
     afterEach(function() {
       view.render.restore();
     });
 
     describe("Collection events", function() {
@@ -685,17 +685,17 @@ describe("loop.shared.views", function()
         expect(checkbox.classList.contains("checked")).eql(true);
       });
 
       it("should alter the render state when the props are changed", function() {
         view = mountTestComponent({
           checked: true
         });
 
-        view.setProps({checked: false});
+        view.setProps({ checked: false });
 
         var checkbox = view.getDOMNode().querySelector(".checkbox");
         expect(checkbox.classList.contains("checked")).eql(false);
       });
 
       it("should add an ellipsis class when the prop is set", function() {
         view = mountTestComponent({
           label: "Some label",
@@ -974,44 +974,44 @@ describe("loop.shared.views", function()
       });
 
       it("should attach a video object according to the standard", function() {
         fakeViewElement.srcObject = null;
 
         sinon.stub(view, "getDOMNode").returns(fakeViewElement);
 
         view.attachVideo({
-          srcObject: {fake: 1}
+          srcObject: { fake: 1 }
         });
 
-        expect(fakeViewElement.srcObject).eql({fake: 1});
+        expect(fakeViewElement.srcObject).eql({ fake: 1 });
       });
 
       it("should attach a video object for Firefox", function() {
         fakeViewElement.mozSrcObject = null;
 
         sinon.stub(view, "getDOMNode").returns(fakeViewElement);
 
         view.attachVideo({
-          mozSrcObject: {fake: 2}
+          mozSrcObject: { fake: 2 }
         });
 
-        expect(fakeViewElement.mozSrcObject).eql({fake: 2});
+        expect(fakeViewElement.mozSrcObject).eql({ fake: 2 });
       });
 
       it("should attach a video object for Chrome", function() {
         fakeViewElement.src = null;
 
         sinon.stub(view, "getDOMNode").returns(fakeViewElement);
 
         view.attachVideo({
-          src: {fake: 2}
+          src: { fake: 2 }
         });
 
-        expect(fakeViewElement.src).eql({fake: 2});
+        expect(fakeViewElement.src).eql({ fake: 2 });
       });
     });
   });
 
   describe("MediaLayoutView", function() {
     var textChatStore, view;
 
     function mountTestComponent(extraProps) {
@@ -1033,17 +1033,17 @@ describe("loop.shared.views", function()
           _.extend(defaultProps, extraProps)));
     }
 
     beforeEach(function() {
       textChatStore = new loop.store.TextChatStore(dispatcher, {
         sdkDriver: {}
       });
 
-      loop.store.StoreMixin.register({textChatStore: textChatStore});
+      loop.store.StoreMixin.register({ textChatStore: textChatStore });
     });
 
     it("should mark the remote stream as the focus stream when not displaying screen share", function() {
       view = mountTestComponent({
         displayScreenShare: false
       });
 
       var node = view.getDOMNode();
--- a/browser/components/loop/test/standalone/standaloneAppStore_test.js
+++ b/browser/components/loop/test/standalone/standaloneAppStore_test.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-describe("loop.store.StandaloneAppStore", function () {
+describe("loop.store.StandaloneAppStore", function() {
   "use strict";
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sandbox, dispatcher;
 
   beforeEach(function() {
--- a/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
+++ b/browser/components/loop/test/standalone/standaloneMetricsStore_test.js
@@ -188,17 +188,17 @@ describe("loop.store.StandaloneMetricsSt
           "send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.button,
           "Joined in Firefox");
       });
     });
   });
 
   describe("Store Change Handlers", function() {
     it("should log an event on room full", function() {
-      fakeActiveRoomStore.setStoreState({roomState: ROOM_STATES.FULL});
+      fakeActiveRoomStore.setStoreState({ roomState: ROOM_STATES.FULL });
 
       sinon.assert.calledOnce(window.ga);
       sinon.assert.calledWithExactly(window.ga,
         "send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.pageLoad,
         "Room full");
     });
 
     it("should log an event when the room is expired or invalid", function() {
--- a/browser/components/loop/test/standalone/standaloneMozLoop_test.js
+++ b/browser/components/loop/test/standalone/standaloneMozLoop_test.js
@@ -9,17 +9,17 @@ describe("loop.StandaloneMozLoop", funct
   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
-    fakeXHR.xhr.onCreate = function (xhr) {
+    fakeXHR.xhr.onCreate = function(xhr) {
       requests.push(xhr);
     };
     fakeBaseServerUrl = "http://fake.api";
     fakeServerErrorDescription = {
       code: 401,
       errno: 101,
       error: "error",
       message: "invalid token",
@@ -81,27 +81,27 @@ describe("loop.StandaloneMozLoop", funct
     it("should call the callback with success parameters", function() {
       mozLoop.rooms.get("fakeToken", callback);
 
       var roomDetails = {
         roomName: "fakeName",
         roomUrl: "http://invalid"
       };
 
-      requests[0].respond(200, {"Content-Type": "application/json"},
+      requests[0].respond(200, { "Content-Type": "application/json" },
         JSON.stringify(roomDetails));
 
       sinon.assert.calledOnce(callback);
       sinon.assert.calledWithExactly(callback, null, roomDetails);
     });
 
     it("should call the callback with failure parameters", function() {
       mozLoop.rooms.get("fakeToken", callback);
 
-      requests[0].respond(401, {"Content-Type": "application/json"},
+      requests[0].respond(401, { "Content-Type": "application/json" },
                           JSON.stringify(fakeServerErrorDescription));
       sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
         return /HTTP 401 Unauthorized/.test(err.message);
       }));
     });
   });
 
   describe("#rooms.join", function() {
@@ -121,27 +121,27 @@ describe("loop.StandaloneMozLoop", funct
 
       var sessionData = {
         apiKey: "12345",
         sessionId: "54321",
         sessionToken: "another token",
         expires: 20
       };
 
-      requests[0].respond(200, {"Content-Type": "application/json"},
+      requests[0].respond(200, { "Content-Type": "application/json" },
         JSON.stringify(sessionData));
 
       sinon.assert.calledOnce(callback);
       sinon.assert.calledWithExactly(callback, null, sessionData);
     });
 
     it("should call the callback with failure parameters", function() {
       mozLoop.rooms.join("fakeToken", callback);
 
-      requests[0].respond(401, {"Content-Type": "application/json"},
+      requests[0].respond(401, { "Content-Type": "application/json" },
                           JSON.stringify(fakeServerErrorDescription));
       sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
         return /HTTP 401 Unauthorized/.test(err.message);
       }));
     });
   });
 
   describe("#rooms.refreshMembership", function() {
@@ -179,28 +179,28 @@ describe("loop.StandaloneMozLoop", funct
     it("should call the callback with success parameters", function() {
       standaloneMozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken",
                                                 callback);
 
       var responseData = {
         expires: 20
       };
 
-      requests[0].respond(200, {"Content-Type": "application/json"},
+      requests[0].respond(200, { "Content-Type": "application/json" },
         JSON.stringify(responseData));
 
       sinon.assert.calledOnce(callback);
       sinon.assert.calledWithExactly(callback, null, responseData);
     });
 
     it("should call the callback with failure parameters", function() {
       standaloneMozLoop.rooms.refreshMembership("fakeToken", "fakeSessionToken",
                                                 callback);
 
-      requests[0].respond(401, {"Content-Type": "application/json"},
+      requests[0].respond(401, { "Content-Type": "application/json" },
                           JSON.stringify(fakeServerErrDescription));
       sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
         return /HTTP 401 Unauthorized/.test(err.message);
       }));
     });
   });
 
   describe("#rooms.leave", function() {
@@ -225,16 +225,16 @@ describe("loop.StandaloneMozLoop", funct
 
       sinon.assert.calledOnce(callback);
       sinon.assert.calledWithExactly(callback, null, {});
     });
 
     it("should call the callback with failure parameters", function() {
       mozLoop.rooms.leave("fakeToken", "fakeSessionToken", callback);
 
-      requests[0].respond(401, {"Content-Type": "application/json"},
+      requests[0].respond(401, { "Content-Type": "application/json" },
                           JSON.stringify(fakeServerErrorDescription));
       sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
         return /HTTP 401 Unauthorized/.test(err.message);
       }));
     });
   });
 });
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -34,25 +34,25 @@ describe("loop.standaloneRoomViews", fun
       activeRoomStore: activeRoomStore,
       textChatStore: textChatStore
     });
 
     clock = sandbox.useFakeTimers();
     fakeWindow = {
       close: sandbox.stub(),
       addEventListener: function() {},
-      document: { addEventListener: function(){} },
+      document: { addEventListener: function() {} },
       removeEventListener: function() {},
       setTimeout: function(callback) { callback(); }
     };
     loop.shared.mixins.setRootObject(fakeWindow);
 
 
     sandbox.stub(navigator.mozL10n, "get", function(key, args) {
-      switch(key) {
+      switch (key) {
         case "standalone_title_with_room_name":
           return args.roomName + " — " + args.clientShortname;
         case "legal_text_and_links":
           return args.terms_of_use_url + " " + args.privacy_notice_url;
         default:
           return key;
       }
     });
@@ -361,57 +361,57 @@ describe("loop.standaloneRoomViews", fun
     function expectActionDispatched() {
       sinon.assert.calledOnce(dispatch);
       sinon.assert.calledWithExactly(dispatch,
         sinon.match.instanceOf(sharedActions.SetupStreamElements));
     }
 
     describe("#componentWillUpdate", function() {
       it("should set document.title to roomName and brand name when the READY state is dispatched", function() {
-        activeRoomStore.setStoreState({roomName: "fakeName", roomState: ROOM_STATES.INIT});
+        activeRoomStore.setStoreState({ roomName: "fakeName", roomState: ROOM_STATES.INIT });
         view = mountTestComponent();
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
         expect(fakeWindow.document.title).to.equal("fakeName — clientShortname2");
       });
 
       it("should set document.title brand name when there is no context available", function() {
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.INIT});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.INIT });
         view = mountTestComponent();
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
         expect(fakeWindow.document.title).to.equal("clientShortname2");
       });
 
       it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " +
         "is entered", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
           view = mountTestComponent();
 
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
           expectActionDispatched(view);
         });
 
       it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is " +
         "re-entered", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.ENDED });
           view = mountTestComponent();
 
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
           expectActionDispatched(view);
         });
     });
 
     describe("#componentDidUpdate", function() {
       beforeEach(function() {
         view = mountTestComponent();
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINING });
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
       });
 
       it("should not dispatch a `TileShown` action immediately in the JOINED state",
         function() {
           sinon.assert.notCalled(dispatch);
         });
 
       it("should dispatch a `TileShown` action after a wait when in the JOINED state",
@@ -419,79 +419,79 @@ describe("loop.standaloneRoomViews", fun
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
         });
 
       it("should dispatch a single `TileShown` action after a wait when going through multiple waiting states",
         function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
         });
 
       it("should not dispatch a `TileShown` action after a wait when in the HAS_PARTICIPANTS state",
         function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           sinon.assert.notCalled(dispatch);
         });
 
       it("should dispatch a `TileShown` action after a wait when a participant leaves",
         function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
           activeRoomStore.remotePeerDisconnected();
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
         });
     });
 
     describe("#componentWillReceiveProps", function() {
       beforeEach(function() {
         view = mountTestComponent();
 
         // Pretend the user waited a little bit
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINING });
         clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY - 1);
       });
 
       describe("Support multiple joins", function() {
         it("should send the first `TileShown` after waiting in JOINING state",
           function() {
             clock.tick(1);
 
             sinon.assert.calledOnce(dispatch);
             sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
           });
 
         it("should send the second `TileShown` after ending and rejoining",
           function() {
             // Trigger the first message then rejoin and wait
             clock.tick(1);
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.ENDED });
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINING });
             clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
             sinon.assert.calledTwice(dispatch);
             sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
           });
       });
 
       describe("Handle leaving quickly", function() {
         beforeEach(function() {
           // The user left and rejoined
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.ENDED });
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINING });
         });
 
         it("should not dispatch an old `TileShown` action after leaving and rejoining",
           function() {
             clock.tick(1);
 
             sinon.assert.notCalled(dispatch);
           });
@@ -536,115 +536,115 @@ describe("loop.standaloneRoomViews", fun
           enabled: true
         }));
       });
     });
 
     describe("#render", function() {
       beforeEach(function() {
         view = mountTestComponent();
-        activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINING });
       });
 
       describe("Empty room message", function() {
         it("should not display an message immediately in the JOINED state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .eql(null);
           });
 
         it("should display an empty room message after a wait when in the JOINED state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
             clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .not.eql(null);
           });
 
         it("should not display an message immediately in the SESSION_CONNECTED state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .eql(null);
           });
 
         it("should display an empty room message after a wait when in the SESSION_CONNECTED state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
             clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .not.eql(null);
           });
 
         it("should not display an message immediately in the HAS_PARTICIPANTS state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .eql(null);
           });
 
         it("should not display an empty room message even after a wait when in the HAS_PARTICIPANTS state",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
             clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
             expect(view.getDOMNode().querySelector(".empty-room-message"))
               .eql(null);
           });
       });
 
       describe("Empty room tile offer", function() {
         it("should display a waiting room message and tile iframe on JOINED", function() {
           var DUMMY_TILE_URL = "http://tile/";
           loop.config.tilesIframeUrl = DUMMY_TILE_URL;
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           expect(view.getDOMNode().querySelector(".room-waiting-area")).not.eql(null);
 
           var tile = view.getDOMNode().querySelector(".room-waiting-tile");
           expect(tile).not.eql(null);
           expect(tile.src).eql(DUMMY_TILE_URL);
         });
 
         it("should dispatch a RecordClick action when the tile support link is clicked", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
           clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
 
           TestUtils.Simulate.click(view.getDOMNode().querySelector(".room-waiting-area a"));
 
           sinon.assert.calledTwice(dispatcher.dispatch);
           sinon.assert.calledWithExactly(dispatcher.dispatch,
             new sharedActions.RecordClick({
               linkInfo: "Tiles support link click"
             }));
         });
 
       });
 
       describe("Prompt media message", function() {
         it("should display a prompt for user media on MEDIA_WAIT",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
             expect(view.getDOMNode().querySelector(".prompt-media-message"))
               .not.eql(null);
           });
       });
 
       describe("Full room message", function() {
         it("should display a full room message on FULL",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.FULL});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.FULL });
 
             expect(view.getDOMNode().querySelector(".full-room-message"))
               .not.eql(null);
           });
       });
 
       describe("Failed room message", function() {
         it("should display the StandaloneRoomFailureView", function() {
@@ -667,30 +667,30 @@ describe("loop.standaloneRoomViews", fun
       });
 
       describe("Join button", function() {
         function getJoinButton(elem) {
           return elem.getDOMNode().querySelector(".btn-join");
         }
 
         it("should render the Join button when room isn't active", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
           expect(getJoinButton(view)).not.eql(null);
         });
 
         it("should not render the Join button when room is active",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
 
             expect(getJoinButton(view)).eql(null);
           });
 
         it("should join the room when clicking the Join button", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
           TestUtils.Simulate.click(getJoinButton(view));
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.JoinRoom());
         });
       });
 
@@ -884,58 +884,58 @@ describe("loop.standaloneRoomViews", fun
 
       describe("Leave button", function() {
         function getLeaveButton(elem) {
           return elem.getDOMNode().querySelector(".btn-hangup");
         }
 
         it("should remove the Leave button when the room state is READY",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
 
             expect(getLeaveButton(view)).eql(null);
           });
 
         it("should remove the Leave button when the room state is FAILED",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.FAILED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.FAILED });
 
             expect(getLeaveButton(view)).eql(null);
           });
 
         it("should remove the Leave button when the room state is FULL",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.FULL});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.FULL });
 
             expect(getLeaveButton(view)).eql(null);
           });
 
         it("should display the Leave button when the room state is SESSION_CONNECTED",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
 
             expect(getLeaveButton(view)).not.eql(null);
           });
 
         it("should display the Leave button when the room state is JOINED",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.JOINED });
 
             expect(getLeaveButton(view)).not.eql(null);
           });
 
         it("should display the Leave button when the room state is HAS_PARTICIPANTS",
           function() {
-            activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+            activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
 
             expect(getLeaveButton(view)).not.eql(null);
           });
 
         it("should leave the room when clicking the Leave button", function() {
-          activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+          activeRoomStore.setStoreState({ roomState: ROOM_STATES.HAS_PARTICIPANTS });
 
           TestUtils.Simulate.click(getLeaveButton(view));
 
           sinon.assert.calledOnce(dispatch);
           sinon.assert.calledWithExactly(dispatch, new sharedActions.LeaveRoom());
         });
       });
 
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -97,45 +97,45 @@ describe("loop.webapp", function() {
       standaloneAppStore = new loop.store.StandaloneAppStore({
         dispatcher: dispatcher,
         sdk: sdk
       });
     });
 
     it("should display the UnsupportedDeviceView for `unsupportedDevice` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "unsupportedDevice", unsupportedPlatform: "ios"});
+        standaloneAppStore.setStoreState({ windowType: "unsupportedDevice", unsupportedPlatform: "ios" });
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.UnsupportedDeviceView);
       });
 
     it("should display the UnsupportedBrowserView for `unsupportedBrowser` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "unsupportedBrowser", isFirefox: false});
+        standaloneAppStore.setStoreState({ windowType: "unsupportedBrowser", isFirefox: false });
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.UnsupportedBrowserView);
       });
 
     it("should display the StandaloneRoomControllerView for `room` window type",
       function() {
-        standaloneAppStore.setStoreState({windowType: "room", isFirefox: true});
+        standaloneAppStore.setStoreState({ windowType: "room", isFirefox: true });
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.standaloneRoomViews.StandaloneRoomControllerView);
       });
 
     it("should display the HomeView for `home` window type", function() {
-        standaloneAppStore.setStoreState({windowType: "home", isFirefox: true});
+        standaloneAppStore.setStoreState({ windowType: "home", isFirefox: true });
 
         var webappRootView = mountTestComponent();
 
         TestUtils.findRenderedComponentWithType(webappRootView,
           loop.webapp.HomeView);
     });
   });
 
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/components/loop/test/xpcshell/head.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 // Initialize this before the imports, as some of them need it.
 do_get_profile();
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource://testing-common/httpd.js");
@@ -164,34 +164,34 @@ MockWebSocketChannel.prototype = {
     this.context = aContext;
 
     this.listener.onStart(this.context);
   },
 
   sendMsg: function(aMsg) {
     var message = JSON.parse(aMsg);
 
-    switch(message.messageType) {
+    switch (message.messageType) {
       case "hello":
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "hello",
-                          uaid: kUAID}));
+          JSON.stringify({ messageType: "hello",
+                          uaid: kUAID }));
         break;
       case "register":
         this.channelID = message.channelID;
         let statusCode = 200;
         if (this.initRegStatus) {
           statusCode = this.initRegStatus;
           this.initRegStatus = 0;
         }
         this.listener.onMessageAvailable(this.context,
-          JSON.stringify({messageType: "register",
+          JSON.stringify({ messageType: "register",
                           status: statusCode,
                           channelID: this.channelID,
-                          pushEndpoint: kEndPointUrl}));
+                          pushEndpoint: kEndPointUrl }));
         break;
       default:
         this.defaultMsgHandler && this.defaultMsgHandler(message);
     }
   },
 
   close: function(aCode, aReason) {
     this.stop(aCode);
@@ -202,21 +202,21 @@ MockWebSocketChannel.prototype = {
       JSON.stringify({
         messageType: "notification", updates: [{
           channelID: this.channelID,
           version: version
         }]
     }));
   },
 
-  stop: function (err) {
+  stop: function(err) {
     this.listener.onStop(this.context, err || -1);
   },
 
-  serverClose: function (err) {
+  serverClose: function(err) {
     this.listener.onServerClose(this.context, err || -1);
   }
 };
 
 const extend = function(target, source) {
   for (let key of Object.getOwnPropertyNames(source)) {
     target[key] = source[key];
   }
--- a/browser/components/loop/test/xpcshell/test_looppush_initialize.js
+++ b/browser/components/loop/test/xpcshell/test_looppush_initialize.js
@@ -18,17 +18,17 @@ add_test(function test_initalize_missing
 });
 
 add_test(function test_initalize_missing_notifycallback() {
   Assert.throws(() => MozLoopPushHandler.register("chan-1", dummyCallback, null));
   run_next_test();
 });
 
 add_test(function test_initalize_websocket() {
-  MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+  MozLoopPushHandler.initialize({ mockWebSocket: mockWebSocket });
   MozLoopPushHandler.register(
     "chan-1",
     function(err, url, id) {
       Assert.equal(err, null, "err should be null to indicate success");
       Assert.equal(url, kEndPointUrl, "Should return push server application URL");
       Assert.equal(id, "chan-1", "Should have channel id = chan-1");
       Assert.equal(mockWebSocket.uri.prePath, kServerPushUrl,
                    "Should have the url from preferences");
@@ -98,17 +98,17 @@ add_test(function test_retry_registratio
   MozLoopPushHandler.uaID = undefined;
   mockWebSocket.initRegStatus = 500;
   mockWebSocket.stop();
 });
 
 add_test(function test_reconnect_no_registration() {
   let regCnt = 0;
   MozLoopPushHandler.shutdown();
-  MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+  MozLoopPushHandler.initialize({ mockWebSocket: mockWebSocket });
   MozLoopPushHandler.register(
     "test-chan",
     function(err, url, id) {
       Assert.equal(++regCnt, 1, "onRegistered should only be called once");
       Assert.equal(err, null, "err should be null to indicate success");
       Assert.equal(url, kEndPointUrl, "Should return push server application URL");
       Assert.equal(id, "test-chan", "Should have channel id = test-chan");
       mockWebSocket.stop();
@@ -125,17 +125,17 @@ add_test(function test_ping_websocket() 
     pingReceived = true;
     // Do not send a ping response.
   };
   mockWebSocket.close = () => {
     socketClosed = true;
   };
 
   MozLoopPushHandler.shutdown();
-  MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+  MozLoopPushHandler.initialize({ mockWebSocket: mockWebSocket });
   MozLoopPushHandler.register(
     "test-chan",
     function(err, url) {
       Assert.equal(err, null, "err should be null to indicate success");
       waitForCondition(() => pingReceived).then(() => {
         waitForCondition(() => socketClosed).then(() => {
           run_next_test();
         }, () => {
@@ -159,46 +159,46 @@ add_test(function test_retry_pushurl() {
       // Non-200 response
       response.setStatusLine(null, 500, "Retry");
       response.processAsync();
       response.finish();
       break;
     case 2:
       // missing parameter
       response.setStatusLine(null, 200, "OK");
-      response.write(JSON.stringify({pushServerURI: null}));
+      response.write(JSON.stringify({ pushServerURI: null }));
       response.processAsync();
       response.finish();
       break;
     case 3:
       // json parse error
       response.setStatusLine(null, 200, "OK");
       response.processAsync();
       response.finish();
       break;
     case 4:
       response.setStatusLine(null, 200, "OK");
-      response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
+      response.write(JSON.stringify({ pushServerURI: kServerPushUrl }));
       response.processAsync();
       response.finish();
 
       run_next_test();
       break;
     }
   });
 
-  MozLoopPushHandler.initialize({mockWebSocket: mockWebSocket});
+  MozLoopPushHandler.initialize({ mockWebSocket: mockWebSocket });
 });
 
 function run_test() {
   setupFakeLoopServer();
 
   loopServer.registerPathHandler("/push-server-config", (request, response) => {
     response.setStatusLine(null, 200, "OK");
-    response.write(JSON.stringify({pushServerURI: kServerPushUrl}));
+    response.write(JSON.stringify({ pushServerURI: kServerPushUrl }));
     response.processAsync();
     response.finish();
   });
 
   Services.prefs.setCharPref("services.push.serverURL", kServerPushUrl);
   Services.prefs.setIntPref("loop.retry_delay.start", 10); // 10 ms
   Services.prefs.setIntPref("loop.retry_delay.limit", 20); // 20 ms
   Services.prefs.setIntPref("loop.ping.interval", 50); // 50 ms
--- a/browser/components/loop/test/xpcshell/test_looprooms.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms.js
@@ -146,17 +146,17 @@ const kRoomUpdates = {
   "4": {
     participants: [{
       displayName: "Adam",
       roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
     }, {
       displayName: "Alexis",
       account: "alexis@example.com",
       roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
-    },  {
+    }, {
       displayName: "Ruharb",
       roomConnectionId: "5de6281c-6568-455f-af08-c0b0a973100e"
     }]
   },
   "5": {
     deleted: true
   }
 };
@@ -647,17 +647,17 @@ function run_test() {
 
   LoopRooms.on("add", onRoomAdded);
   LoopRooms.on("update", onRoomUpdated);
   LoopRooms.on("delete", onRoomDeleted);
   LoopRooms.on("joined", onRoomJoined);
   LoopRooms.on("left", onRoomLeft);
   LoopRooms.on("refresh", onRefresh);
 
-  do_register_cleanup(function () {
+  do_register_cleanup(function() {
     // Revert original Chat.open implementation
     Chat.open = openChatOrig;
     Services.prefs.clearUserPref("loop.key");
     Services.prefs.clearUserPref("loop.key.fxa");
 
     MozLoopServiceInternal.fxAOAuthTokenData = null;
     MozLoopServiceInternal.fxAOAuthProfile = null;
 
--- a/browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
@@ -196,17 +196,17 @@ add_task(function* test_get_rooms_saves_
 
   yield clearRoomsCache();
 });
 
 // Test that when we get a room it updates the saved key if it is different.
 add_task(function* test_get_rooms_saves_different_keys() {
   let roomsCache = {};
   roomsCache[LOOP_SESSION_TYPE.FXA] = {
-    QzBbvGmIZWU: {key: "fakeKey"}
+    QzBbvGmIZWU: { key: "fakeKey" }
   };
   yield saveRoomsCache(roomsCache);
 
   const kRoomToken = "QzBbvGmIZWU";
 
   let room = yield LoopRooms.promise("get", kRoomToken);
 
   // Check that we've saved the encryption keys correctly.
@@ -220,17 +220,17 @@ add_task(function* test_get_rooms_saves_
 
 // Test that if roomKey decryption fails, the saved key is used for decryption.
 add_task(function* test_get_rooms_uses_saved_key() {
   const kRoomToken = "_nxD4V4FflQ";
   const kExpected = kExpectedRooms.get(kRoomToken);
 
   let roomsCache = {};
   roomsCache[LOOP_SESSION_TYPE.FXA] = {
-    "_nxD4V4FflQ": {key: kExpected.roomKey}
+    "_nxD4V4FflQ": { key: kExpected.roomKey }
   };
   yield saveRoomsCache(roomsCache);
 
   // Change the encryption key for FxA, so that decoding the room key will break.
   Services.prefs.setCharPref("loop.key.fxa", "invalidKey");
 
   let room = yield LoopRooms.promise("get", kRoomToken);
 
@@ -256,17 +256,17 @@ function run_test() {
 
   Services.prefs.setCharPref("loop.key.fxa", kFxAKey);
   Services.prefs.setBoolPref(kContextEnabledPref, true);
 
   // Pretend we're signed into FxA.
   MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" };
   MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
 
-  do_register_cleanup(function () {
+  do_register_cleanup(function() {
     Services.prefs.clearUserPref(kContextEnabledPref);
     Services.prefs.clearUserPref("loop.key.fxa");
 
     MozLoopServiceInternal.fxAOAuthTokenData = null;
     MozLoopServiceInternal.fxAOAuthProfile = null;
   });
 
   run_next_test();
--- a/browser/components/loop/test/xpcshell/test_looprooms_first_notification.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms_first_notification.js
@@ -103,17 +103,17 @@ add_task(function* test_notification_fir
 
 function run_test() {
   setupFakeLoopServer();
 
   Services.prefs.setCharPref("loop.key", kGuestKey);
   LoopRooms.on("add", onRoomAdded);
   LoopRooms.on("update", onRoomUpdated);
 
-  do_register_cleanup(function () {
+  do_register_cleanup(function() {
     Services.prefs.clearUserPref("loop.key");
 
     LoopRooms.off("add", onRoomAdded);
     LoopRooms.off("update", onRoomUpdated);
   });
 
   run_next_test();
 }
--- a/browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
+++ b/browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
@@ -7,17 +7,17 @@
 Cu.import("resource://services-common/utils.js");
 
 const loopCrypto = Cu.import("resource:///modules/loop/crypto.js", {}).LoopCrypto;
 const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("resource:///modules/loop/LoopRoomsCache.jsm", {});
 
 var gTimerArgs = [];
 
 timerHandlers.startTimer = function(callback, delay) {
-  gTimerArgs.push({callback, delay});
+  gTimerArgs.push({ callback, delay });
   return gTimerArgs.length;
 };
 
 var gRoomPatches = [];
 
 const kContextEnabledPref = "loop.contextInConverations.enabled";
 
 const kFxAKey = "uGIs-kGbYt1hBBwjyW7MLQ";
@@ -124,17 +124,17 @@ function run_test() {
 
   Services.prefs.setCharPref("loop.key.fxa", kFxAKey);
   Services.prefs.setBoolPref(kContextEnabledPref, true);
 
   // Pretend we're signed into FxA.
   MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" };
   MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
 
-  do_register_cleanup(function () {
+  do_register_cleanup(function() {
     Services.prefs.clearUserPref(kContextEnabledPref);
     Services.prefs.clearUserPref("loop.key.fxa");
 
     MozLoopServiceInternal.fxAOAuthTokenData = null;
     MozLoopServiceInternal.fxAOAuthProfile = null;
   });
 
   run_next_test();
--- a/browser/components/loop/test/xpcshell/test_loopservice_dnd.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_dnd.js
@@ -37,17 +37,17 @@ function run_test() {
 
   loopServer.registerPathHandler("/registration", (request, response) => {
     response.setStatusLine(null, 200, "OK");
     response.processAsync();
     response.finish();
   });
   loopServer.registerPathHandler("/calls", (request, response) => {
     response.setStatusLine(null, 200, "OK");
-    response.write(JSON.stringify({calls: [{callId: 4444333221, websocketToken: "0deadbeef0"}]}));
+    response.write(JSON.stringify({ calls: [{ callId: 4444333221, websocketToken: "0deadbeef0" }] }));
     response.processAsync();
     response.finish();
   });
 
   do_register_cleanup(function() {
     // Revert original Chat.open implementation
     Chat.open = openChatOrig;
 
--- a/browser/components/loop/test/xpcshell/test_loopservice_hawk_request.js
+++ b/browser/components/loop/test/xpcshell/test_loopservice_hawk_request.js
@@ -17,17 +17,17 @@ add_task(function* request_with_unicode(
     let jsonBody = JSON.parse(body);
     Assert.equal(jsonBody.name, CommonUtils.encodeUTF8(unicodeName));
 
     response.setStatusLine(null, 200, "OK");
     response.processAsync();
     response.finish();
   });
 
-  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/fake", "POST", {name: unicodeName}).then(
+  yield MozLoopServiceInternal.hawkRequestInternal(LOOP_SESSION_TYPE.GUEST, "/fake", "POST", { name: unicodeName }).then(
     () => Assert.ok(true, "Should have accepted"),
     () => Assert.ok(false, "Should have accepted"));
 });
 
 function run_test() {
   setupFakeLoopServer();
 
   do_register_cleanup(() => {
--- a/browser/components/loop/ui/fake-l10n.js
+++ b/browser/components/loop/ui/fake-l10n.js
@@ -7,21 +7,21 @@
 
   /**
    * /!\ FIXME: THIS IS A HORRID HACK which fakes both the mozL10n and webL10n
    * objects and makes them returning the string id and serialized vars if any,
    * for any requested string id.
    * @type {Object}
    */
   navigator.mozL10n = document.mozL10n = {
-    initialize: function(){},
+    initialize: function() {},
 
-    getLanguage: function(){},
+    getLanguage: function() {},
 
-    getDirection: function(){
+    getDirection: function() {
       if (document.location.search === "?rtl=1") {
         return "rtl";
       }
 
       return "ltr";
     },
 
     get: function(stringId, vars) {
--- a/browser/components/loop/ui/fake-mozLoop.js
+++ b/browser/components/loop/ui/fake-mozLoop.js
@@ -107,27 +107,27 @@ var fakeRooms = [
   "use strict";
 
   /**
    * Faking the mozLoop object which doesn't exist in regular web pages.
    * @type {Object}
    */
   navigator.mozLoop = {
     ensureRegistered: function() {},
-    getAudioBlob: function(){},
+    getAudioBlob: function() {},
     getLoopPref: function(pref) {
-      switch(pref) {
+      switch (pref) {
         // Ensure we skip FTE completely.
         case "gettingStarted.seen":
           return true;
       }
       return null;
     },
     hasEncryptionKey: true,
-    setLoopPref: function(){},
+    setLoopPref: function() {},
     releaseCallData: function() {},
     copyString: function() {},
     getUserAvatar: function(emailAddress) {
       var avatarUrl = "http://www.gravatar.com/avatar/0a996f0fe2727ef1668bdb11897e4459.jpg?default=blank&s=40";
       return Math.ceil(Math.random() * 3) === 2 ? avatarUrl : null;
     },
     getSelectedTabMetadata: function(callback) {
       callback({
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -18,19 +18,19 @@
   var PanelView = loop.panel.PanelView;
   var SignInRequestView = loop.panel.SignInRequestView;
   // 1.2. Conversation Window
   var DesktopRoomEditContextView = loop.roomViews.DesktopRoomEditContextView;
   var RoomFailureView = loop.roomViews.RoomFailureView;
   var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
 
   // 2. Standalone webapp
-  var UnsupportedBrowserView  = loop.webapp.UnsupportedBrowserView;
-  var UnsupportedDeviceView   = loop.webapp.UnsupportedDeviceView;
-  var StandaloneRoomView      = loop.standaloneRoomViews.StandaloneRoomView;
+  var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
+  var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
+  var StandaloneRoomView = loop.standaloneRoomViews.StandaloneRoomView;
   var StandaloneHandleUserAgentView = loop.standaloneRoomViews.StandaloneHandleUserAgentView;
 
   // 3. Shared components
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
@@ -44,17 +44,17 @@
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
   }
 
-  function noop(){}
+  function noop() {}
 
   // We save the visibility change listeners so that we can fake an event
   // to the panel once we've loaded all the views.
   var visibilityListeners = [];
   var rootObject = window;
 
   rootObject.document.addEventListener = function(eventName, func) {
     if (eventName === "visibilitychange") {
@@ -136,22 +136,22 @@
         // the dimensions here are taken from the poster images that we're
         // using, since they give the <video> elements their initial intrinsic
         // size.  This ensures that the right aspect ratios are calculated.
         // These are forced to 640x480, because it makes it visually easy to
         // validate that the showcase looks like the real app on a chine
         // (eg MacBook Pro) where that is the default camera resolution.
         var newStoreState = {
           localVideoDimensions: {
-            camera: {height: 480, orientation: 0, width: 640}
+            camera: { height: 480, orientation: 0, width: 640 }
           },
           mediaConnected: options.mediaConnected,
           receivingScreenShare: !!options.receivingScreenShare,
           remoteVideoDimensions: {
-            camera: {height: 480, orientation: 0, width: 640}
+            camera: { height: 480, orientation: 0, width: 640 }
           },
           remoteVideoEnabled: options.remoteVideoEnabled,
           // Override the matchMedia, this is so that the correct version is
           // used for the frame.
           //
           // Currently, we use an icky hack, and the showcase conspires with
           // react-frame-component to set iframe.contentWindow.matchMedia onto
           // the store. Once React context matures a bit (somewhere between
@@ -167,17 +167,17 @@
 
         if (options.receivingScreenShare) {
           // Note that the image we're using had to be scaled a bit, and
           // it still ended up a bit narrower than the live thing that
           // WebRTC sends; presumably a different scaling algorithm.
           // For showcase purposes, this shouldn't matter much, as the sizes
           // of things being shared will be fairly arbitrary.
           newStoreState.remoteVideoDimensions.screen =
-          {height: 456, orientation: 0, width: 641};
+          { height: 456, orientation: 0, width: 641 };
         }
 
         store.setStoreState(newStoreState);
       } catch (ex) {
         console.error("exception in forcedUpdate:", ex);
       }
     };
 
@@ -334,17 +334,17 @@
     roomUrl: "http://showcase",
     urls: [{
       description: "A wonderful page!",
       location: "http://wonderful.invalid"
       // use the fallback thumbnail
     }]
   }));
 
-  textChatStore.setStoreState({textChatEnabled: true});
+  textChatStore.setStoreState({ textChatEnabled: true });
 
   dispatcher.dispatch(new sharedActions.SendTextChatMessage({
     contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
     message: "Rheet!",
     sentTimestamp: "2015-06-23T22:21:45.590Z"
   }));
   dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
     contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
@@ -387,17 +387,17 @@
 
   // Local mocks
   var mockMozLoopNoRooms = _.cloneDeep(navigator.mozLoop);
   mockMozLoopNoRooms.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
   var mockMozLoopNoRoomsNoContext = _.cloneDeep(navigator.mozLoop);
-  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function() {};
   mockMozLoopNoRoomsNoContext.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
   var roomStoreNoRooms = new loop.store.RoomStore(new loop.Dispatcher(), {
     mozLoop: mockMozLoopNoRooms,
     activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
       mozLoop: mockMozLoopNoRooms,
@@ -420,17 +420,17 @@
 
   var mockMozLoopLoggedIn = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedIn.userProfile = {
     email: "text@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
   var mockMozLoopLoggedInNoContext = _.cloneDeep(navigator.mozLoop);
-  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function() {};
   mockMozLoopLoggedInNoContext.userProfile = _.cloneDeep(mockMozLoopLoggedIn.userProfile);
 
   var mockMozLoopLoggedInLongEmail = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedInLongEmail.userProfile = {
     email: "reallyreallylongtext@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
@@ -548,17 +548,17 @@
       var cx = React.addons.classSet;
       return (
         React.createElement("div", {className: "example"}, 
           React.createElement("h3", {id: this.makeId()}, 
             this.props.summary, 
             React.createElement("a", {href: this.makeId("#")}, " ¶")
           ), 
           React.createElement("div", {className: "comp"}, 
-            React.createElement(Frame, {className: cx({dashed: this.props.dashed}), 
+            React.createElement(Frame, {className: cx({ dashed: this.props.dashed }), 
                    cssClass: this.props.cssClass, 
                    height: height, 
                    onContentsRendered: this.props.onContentsRendered, 
                    width: width}, 
               this.props.children
             )
           )
         )
@@ -878,32 +878,32 @@
                            summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)", 
                            width: 348}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   roomState: ROOM_STATES.INIT, 
                   roomStore: invitationRoomStore})
               )
             ), 
 
             React.createElement(FramedExample, {height: 288, 
                            onContentsRendered: invitationRoomStore.activeRoomStore.forcedUpdate, 
                            summary: "Desktop room Edit Context w/Error", 
                            width: 348}, 
               React.createElement("div", {className: "fx-embedded room-invitation-overlay"}, 
                 React.createElement(DesktopRoomEditContextView, {
                   dispatcher: dispatcher, 
                   error: {}, 
                   mozLoop: navigator.mozLoop, 
-                  onClose: function(){}, 
+                  onClose: function() {}, 
                   roomData: {}, 
                   savingContext: false, 
                   show: true}
                   )
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
@@ -914,17 +914,17 @@
               /* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */
               React.createElement("div", {className: "fx-embedded overflow-hidden"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                   roomStore: desktopRoomStoreLoading})
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
                            height: 398, 
@@ -932,17 +932,17 @@
                            summary: "Desktop room conversation", 
                            width: 348}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                   roomStore: roomStore})
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
                            height: 482, 
@@ -950,17 +950,17 @@
                            summary: "Desktop room conversation (medium)", 
                            width: 602}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                   roomStore: desktopRoomStoreMedium})
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
                            height: 485, 
@@ -968,51 +968,51 @@
                            summary: "Desktop room conversation (large)", 
                            width: 646}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                   roomStore: desktopRoomStoreLarge})
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
                            height: 398, 
                            onContentsRendered: desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate, 
                            summary: "Desktop room conversation local face-mute", 
                            width: 348}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomStore: desktopLocalFaceMuteRoomStore})
               )
             ), 
 
             React.createElement(FramedExample, {dashed: true, 
                            height: 398, 
                            onContentsRendered: desktopRemoteFaceMuteRoomStore.activeRoomStore.forcedUpdate, 
                            summary: "Desktop room conversation remote face-mute", 
                            width: 348}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
                   chatWindowDetached: false, 
                   dispatcher: dispatcher, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  onCallTerminated: function(){}, 
+                  onCallTerminated: function() {}, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   roomStore: desktopRemoteFaceMuteRoomStore})
               )
             )
           ), 
 
           React.createElement(Section, {name: "StandaloneHandleUserAgentView"}, 
             React.createElement(FramedExample, {
@@ -1316,19 +1316,19 @@
       caughtWarnings.push(args);
       consoleWarn.apply(console, args);
     };
 
     try {
       React.render(React.createElement(App, null), document.getElementById("main"));
 
       for (var listener of visibilityListeners) {
-        listener({target: {hidden: false}});
+        listener({ target: { hidden: false } });
       }
-    } catch(err) {
+    } catch (err) {
       console.error(err);
       uncaughtError = err;
     }
 
     // Wait until all the FramedExamples have been fully loaded.
     setTimeout(function waitForQueuedFrames() {
       if (window.queuedFrames.length !== 0) {
         setTimeout(waitForQueuedFrames, 500);
@@ -1353,27 +1353,27 @@
 
         divFailuresNode.className = "failures";
         emNode.innerHTML = ((uncaughtError && warningsMismatch) ? 2 : 1).toString();
         divFailuresNode.appendChild(emNode);
         resultsElement.appendChild(divFailuresNode);
 
         if (warningsMismatch) {
           liTestFail.className = "test";
-          liTestFail.className = liTestFail.className + " fail";
+          liTestFail.className += " fail";
           h2Node.innerHTML = "Unexpected number of warnings detected in UI-Showcase";
           preErrorNode.className = "error";
           preErrorNode.innerHTML = "Got: " + caughtWarnings.length + "\n" + "Expected: " + expectedWarningsCount;
           liTestFail.appendChild(h2Node);
           liTestFail.appendChild(preErrorNode);
           resultsElement.appendChild(liTestFail);
         }
         if (uncaughtError) {
           liTestFail.className = "test";
-          liTestFail.className = liTestFail.className + " fail";
+          liTestFail.className += " fail";
           h2Node.innerHTML = "Errors rendering UI-Showcase";
           preErrorNode.className = "error";
           preErrorNode.innerHTML = uncaughtError + "\n" + uncaughtError.stack;
           liTestFail.appendChild(h2Node);
           liTestFail.appendChild(preErrorNode);
           resultsElement.appendChild(liTestFail);
         }
       } else {
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -18,19 +18,19 @@
   var PanelView = loop.panel.PanelView;
   var SignInRequestView = loop.panel.SignInRequestView;
   // 1.2. Conversation Window
   var DesktopRoomEditContextView = loop.roomViews.DesktopRoomEditContextView;
   var RoomFailureView = loop.roomViews.RoomFailureView;
   var DesktopRoomConversationView = loop.roomViews.DesktopRoomConversationView;
 
   // 2. Standalone webapp
-  var UnsupportedBrowserView  = loop.webapp.UnsupportedBrowserView;
-  var UnsupportedDeviceView   = loop.webapp.UnsupportedDeviceView;
-  var StandaloneRoomView      = loop.standaloneRoomViews.StandaloneRoomView;
+  var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
+  var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
+  var StandaloneRoomView = loop.standaloneRoomViews.StandaloneRoomView;
   var StandaloneHandleUserAgentView = loop.standaloneRoomViews.StandaloneHandleUserAgentView;
 
   // 3. Shared components
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
@@ -44,17 +44,17 @@
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
   }
 
-  function noop(){}
+  function noop() {}
 
   // We save the visibility change listeners so that we can fake an event
   // to the panel once we've loaded all the views.
   var visibilityListeners = [];
   var rootObject = window;
 
   rootObject.document.addEventListener = function(eventName, func) {
     if (eventName === "visibilitychange") {
@@ -136,22 +136,22 @@
         // the dimensions here are taken from the poster images that we're
         // using, since they give the <video> elements their initial intrinsic
         // size.  This ensures that the right aspect ratios are calculated.
         // These are forced to 640x480, because it makes it visually easy to
         // validate that the showcase looks like the real app on a chine
         // (eg MacBook Pro) where that is the default camera resolution.
         var newStoreState = {
           localVideoDimensions: {
-            camera: {height: 480, orientation: 0, width: 640}
+            camera: { height: 480, orientation: 0, width: 640 }
           },
           mediaConnected: options.mediaConnected,
           receivingScreenShare: !!options.receivingScreenShare,
           remoteVideoDimensions: {
-            camera: {height: 480, orientation: 0, width: 640}
+            camera: { height: 480, orientation: 0, width: 640 }
           },
           remoteVideoEnabled: options.remoteVideoEnabled,
           // Override the matchMedia, this is so that the correct version is
           // used for the frame.
           //
           // Currently, we use an icky hack, and the showcase conspires with
           // react-frame-component to set iframe.contentWindow.matchMedia onto
           // the store. Once React context matures a bit (somewhere between
@@ -167,17 +167,17 @@
 
         if (options.receivingScreenShare) {
           // Note that the image we're using had to be scaled a bit, and
           // it still ended up a bit narrower than the live thing that
           // WebRTC sends; presumably a different scaling algorithm.
           // For showcase purposes, this shouldn't matter much, as the sizes
           // of things being shared will be fairly arbitrary.
           newStoreState.remoteVideoDimensions.screen =
-          {height: 456, orientation: 0, width: 641};
+          { height: 456, orientation: 0, width: 641 };
         }
 
         store.setStoreState(newStoreState);
       } catch (ex) {
         console.error("exception in forcedUpdate:", ex);
       }
     };
 
@@ -334,17 +334,17 @@
     roomUrl: "http://showcase",
     urls: [{
       description: "A wonderful page!",
       location: "http://wonderful.invalid"
       // use the fallback thumbnail
     }]
   }));
 
-  textChatStore.setStoreState({textChatEnabled: true});
+  textChatStore.setStoreState({ textChatEnabled: true });
 
   dispatcher.dispatch(new sharedActions.SendTextChatMessage({
     contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
     message: "Rheet!",
     sentTimestamp: "2015-06-23T22:21:45.590Z"
   }));
   dispatcher.dispatch(new sharedActions.ReceivedTextChatMessage({
     contentType: loop.shared.utils.CHAT_CONTENT_TYPES.TEXT,
@@ -387,17 +387,17 @@
 
   // Local mocks
   var mockMozLoopNoRooms = _.cloneDeep(navigator.mozLoop);
   mockMozLoopNoRooms.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
   var mockMozLoopNoRoomsNoContext = _.cloneDeep(navigator.mozLoop);
-  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function() {};
   mockMozLoopNoRoomsNoContext.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
   var roomStoreNoRooms = new loop.store.RoomStore(new loop.Dispatcher(), {
     mozLoop: mockMozLoopNoRooms,
     activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
       mozLoop: mockMozLoopNoRooms,
@@ -420,17 +420,17 @@
 
   var mockMozLoopLoggedIn = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedIn.userProfile = {
     email: "text@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
   var mockMozLoopLoggedInNoContext = _.cloneDeep(navigator.mozLoop);
-  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function() {};
   mockMozLoopLoggedInNoContext.userProfile = _.cloneDeep(mockMozLoopLoggedIn.userProfile);
 
   var mockMozLoopLoggedInLongEmail = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedInLongEmail.userProfile = {
     email: "reallyreallylongtext@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
@@ -548,17 +548,17 @@
       var cx = React.addons.classSet;
       return (
         <div className="example">
           <h3 id={this.makeId()}>
             {this.props.summary}
             <a href={this.makeId("#")}>&nbsp;¶</a>
           </h3>
           <div className="comp">
-            <Frame className={cx({dashed: this.props.dashed})}
+            <Frame className={cx({ dashed: this.props.dashed })}
                    cssClass={this.props.cssClass}
                    height={height}
                    onContentsRendered={this.props.onContentsRendered}
                    width={width}>
               {this.props.children}
             </Frame>
           </div>
         </div>
@@ -878,32 +878,32 @@
                            summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"
                            width={348}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   roomState={ROOM_STATES.INIT}
                   roomStore={invitationRoomStore} />
               </div>
             </FramedExample>
 
             <FramedExample height={288}
                            onContentsRendered={invitationRoomStore.activeRoomStore.forcedUpdate}
                            summary="Desktop room Edit Context w/Error"
                            width={348}>
               <div className="fx-embedded room-invitation-overlay">
                 <DesktopRoomEditContextView
                   dispatcher={dispatcher}
                   error={{}}
                   mozLoop={navigator.mozLoop}
-                  onClose={function(){}}
+                  onClose={function() {}}
                   roomData={{}}
                   savingContext={false}
                   show={true}
                   />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
@@ -914,17 +914,17 @@
               {/* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */}
               <div className="fx-embedded overflow-hidden">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomState={ROOM_STATES.HAS_PARTICIPANTS}
                   roomStore={desktopRoomStoreLoading} />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
                            height={398}
@@ -932,17 +932,17 @@
                            summary="Desktop room conversation"
                            width={348}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomState={ROOM_STATES.HAS_PARTICIPANTS}
                   roomStore={roomStore} />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
                            height={482}
@@ -950,17 +950,17 @@
                            summary="Desktop room conversation (medium)"
                            width={602}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomState={ROOM_STATES.HAS_PARTICIPANTS}
                   roomStore={desktopRoomStoreMedium} />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
                            height={485}
@@ -968,51 +968,51 @@
                            summary="Desktop room conversation (large)"
                            width={646}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomState={ROOM_STATES.HAS_PARTICIPANTS}
                   roomStore={desktopRoomStoreLarge} />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
                            height={398}
                            onContentsRendered={desktopLocalFaceMuteRoomStore.activeRoomStore.forcedUpdate}
                            summary="Desktop room conversation local face-mute"
                            width={348}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomStore={desktopLocalFaceMuteRoomStore} />
               </div>
             </FramedExample>
 
             <FramedExample dashed={true}
                            height={398}
                            onContentsRendered={desktopRemoteFaceMuteRoomStore.activeRoomStore.forcedUpdate}
                            summary="Desktop room conversation remote face-mute"
                            width={348} >
               <div className="fx-embedded">
                 <DesktopRoomConversationView
                   chatWindowDetached={false}
                   dispatcher={dispatcher}
                   localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  onCallTerminated={function(){}}
+                  onCallTerminated={function() {}}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   roomStore={desktopRemoteFaceMuteRoomStore} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="StandaloneHandleUserAgentView">
             <FramedExample
@@ -1285,22 +1285,22 @@
           </Section>
 
           <Section className="svg-icons" name="SVG icons preview">
             <FramedExample height={240}
                            summary="10x10"
                            width={800}>
               <SVGIcons size="10x10"/>
             </FramedExample>
-            <FramedExample  height={350}
+            <FramedExample height={350}
                             summary="14x14"
                             width={800}>
               <SVGIcons size="14x14" />
             </FramedExample>
-            <FramedExample  height={480}
+            <FramedExample height={480}
                             summary="16x16"
                             width={800}>
               <SVGIcons size="16x16"/>
             </FramedExample>
           </Section>
 
         </ShowCase>
       );
@@ -1316,19 +1316,19 @@
       caughtWarnings.push(args);
       consoleWarn.apply(console, args);
     };
 
     try {
       React.render(<App />, document.getElementById("main"));
 
       for (var listener of visibilityListeners) {
-        listener({target: {hidden: false}});
+        listener({ target: { hidden: false } });
       }
-    } catch(err) {
+    } catch (err) {
       console.error(err);
       uncaughtError = err;
     }
 
     // Wait until all the FramedExamples have been fully loaded.
     setTimeout(function waitForQueuedFrames() {
       if (window.queuedFrames.length !== 0) {
         setTimeout(waitForQueuedFrames, 500);
@@ -1353,27 +1353,27 @@
 
         divFailuresNode.className = "failures";
         emNode.innerHTML = ((uncaughtError && warningsMismatch) ? 2 : 1).toString();
         divFailuresNode.appendChild(emNode);
         resultsElement.appendChild(divFailuresNode);
 
         if (warningsMismatch) {
           liTestFail.className = "test";
-          liTestFail.className = liTestFail.className + " fail";
+          liTestFail.className += " fail";
           h2Node.innerHTML = "Unexpected number of warnings detected in UI-Showcase";
           preErrorNode.className = "error";
           preErrorNode.innerHTML = "Got: " + caughtWarnings.length + "\n" + "Expected: " + expectedWarningsCount;
           liTestFail.appendChild(h2Node);
           liTestFail.appendChild(preErrorNode);
           resultsElement.appendChild(liTestFail);
         }
         if (uncaughtError) {
           liTestFail.className = "test";
-          liTestFail.className = liTestFail.className + " fail";
+          liTestFail.className += " fail";
           h2Node.innerHTML = "Errors rendering UI-Showcase";
           preErrorNode.className = "error";
           preErrorNode.innerHTML = uncaughtError + "\n" + uncaughtError.stack;
           liTestFail.appendChild(h2Node);
           liTestFail.appendChild(preErrorNode);
           resultsElement.appendChild(liTestFail);
         }
       } else {
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -100,16 +100,22 @@ const MAX_VISIBLE_HISTORY_TILES = 15;
 const PING_SCORE_DIVISOR = 10000;
 
 // Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_]
 const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"];
 
 // Location of inadjacent sites json
 const INADJACENCY_SOURCE = "chrome://browser/content/newtab/newTab.inadjacent.json";
 
+// Fake URL to keep track of last block of a suggested tile in the frequency cap object
+const FAKE_SUGGESTED_BLOCK_URL = "ignore://suggested_block";
+
+// Time before suggested tile is allowed to play again after block - default to 1 day
+const AFTER_SUGGESTED_BLOCK_DECAY_TIME = 24*60*60*1000;
+
 /**
  * Singleton that serves as the provider of directory links.
  * Directory links are a hard-coded set of links shown if a user's link
  * inventory is empty.
  */
 var DirectoryLinksProvider = {
 
   __linksURL: null,
@@ -488,16 +494,38 @@ var DirectoryLinksProvider = {
       // otherwise link is still ok, but we need to set timeoutDate
       return {use: true, timeoutDate: link.endTime};
     }
     // if we are here, the link is ok and no timeoutDate needed
     return {use: true};
   },
 
   /**
+   * Handles block on suggested tile: updates fake block url with current timestamp
+   */
+  handleSuggestedTileBlock: function DirectoryLinksProvider_handleSuggestedTileBlock() {
+    this._updateFrequencyCapSettings({url: FAKE_SUGGESTED_BLOCK_URL});
+    this._writeFrequencyCapFile();
+  },
+
+  /**
+   * Checks if suggested tile is being blocked for the rest of "decay time"
+   * @return True if blocked, false otherwise
+   */
+  _isSuggestedTileBlocked: function DirectoryLinksProvider__isSuggestedTileBlocked() {
+    let capObject = this._frequencyCaps[FAKE_SUGGESTED_BLOCK_URL];
+    if (!capObject || !capObject.lastUpdated) {
+      // user never blocked suggested tile or lastUpdated is missing
+      return false;
+    }
+    // otherwise, make sure that enough time passed after suggested tile was blocked
+    return (capObject.lastUpdated + AFTER_SUGGESTED_BLOCK_DECAY_TIME) > Date.now();
+  },
+
+  /**
    * Report some action on a newtab page (view, click)
    * @param sites Array of sites shown on newtab page
    * @param action String of the behavior to report
    * @param triggeringSiteIndex optional Int index of the site triggering action
    * @return download promise
    */
   reportSitesAction: function DirectoryLinksProvider_reportSitesAction(sites, action, triggeringSiteIndex) {
     let pastImpressions;
@@ -885,19 +913,22 @@ var DirectoryLinksProvider = {
           url: mostFrecentLink.url,
           frecency: SUGGESTED_FRECENCY,
           lastVisitDate: mostFrecentLink.lastVisitDate,
           type: mostFrecentLink.type,
         }, 0, true);
       }
     }
 
-    if (this._topSitesWithSuggestedLinks.size == 0 || !this._shouldUpdateSuggestedTile()) {
+    if (this._topSitesWithSuggestedLinks.size == 0 ||
+        !this._shouldUpdateSuggestedTile() ||
+        this._isSuggestedTileBlocked()) {
       // There are no potential suggested links we can show or not
-      // enough history for a suggested tile.
+      // enough history for a suggested tile, or suggested tile was
+      // recently blocked and wait time interval has not decayed yet
       return;
     }
 
     // Create a flat list of all possible links we can show as suggested.
     // Note that many top sites may map to the same suggested links, but we only
     // want to count each suggested link once (based on url), thus possibleLinks is a map
     // from url to suggestedLink. Thus, each link has an equal chance of being chosen at
     // random from flattenedLinks if it appears only once.
@@ -1108,17 +1139,18 @@ var DirectoryLinksProvider = {
    * @param timeDetla milliseconds
    *        all cap objects with lastUpdated less than (now() - timeDelta)
    *        will be removed. This is done to remove frequency cap objects
    *        for unused tile urls
    */
   _pruneFrequencyCapUrls: function DirectoryLinksProvider_pruneFrequencyCapUrls(timeDelta = DEFAULT_PRUNE_TIME_DELTA) {
     let timeThreshold = Date.now() - timeDelta;
     Object.keys(this._frequencyCaps).forEach(url => {
-      if (this._frequencyCaps[url].lastUpdated <= timeThreshold) {
+      // remove url if it is not ignorable and wasn't updated for a while
+      if (!url.startsWith("ignore") && this._frequencyCaps[url].lastUpdated <= timeThreshold) {
         delete this._frequencyCaps[url];
       }
     });
   },
 
   /**
    * Checks if supplied timestamp happened today
    * @param timestamp in milliseconds
--- a/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
+++ b/browser/modules/test/xpcshell/test_DirectoryLinksProvider.js
@@ -1998,8 +1998,68 @@ add_task(function test_reportPastImpress
   sites.isPinned = _ => false;
   yield sendPingAndTest("click", "block", 2);
 
   // Cleanup.
   done = true;
   NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
   DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
 });
+
+add_task(function test_blockSuggestedTiles() {
+  // Initial setup
+  let suggestedTile = suggestedTile1;
+  let topSites = ["site0.com", "1040.com", "site2.com", "hrblock.com", "site4.com", "freetaxusa.com", "site6.com"];
+  let data = {"suggested": [suggestedTile1, suggestedTile2, suggestedTile3], "directory": [someOtherSite]};
+  let dataURI = 'data:application/json,' + JSON.stringify(data);
+
+  yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
+  let links = yield fetchData();
+
+  let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
+  NewTabUtils.isTopPlacesSite = function(site) {
+    return topSites.indexOf(site) >= 0;
+  }
+
+  let origGetProviderLinks = NewTabUtils.getProviderLinks;
+  NewTabUtils.getProviderLinks = function(provider) {
+    return links;
+  }
+
+  let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
+  DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
+
+  // load the links
+  yield new Promise(resolve => {
+    DirectoryLinksProvider.getLinks(resolve);
+  });
+
+  // ensure that tile is suggested
+  let suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+  do_check_true(suggestedLink.frecent_sites);
+
+  // block suggested tile in a regular way
+  DirectoryLinksProvider.reportSitesAction([{
+      isPinned: function() {return false;},
+      link: Object.assign({frecency: 1000},suggestedLink)
+  }], "block", 0);
+
+  // suggested tile still must be recommended
+  suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+  do_check_true(suggestedLink.frecent_sites);
+
+  // timestamp suggested_block in the frequency cap object
+  DirectoryLinksProvider.handleSuggestedTileBlock();
+  // no more recommendations should be seen
+  do_check_eq(DirectoryLinksProvider._updateSuggestedTile(), undefined);
+
+  // move lastUpdated for suggested tile into the past
+  DirectoryLinksProvider._frequencyCaps["ignore://suggested_block"].lastUpdated = Date.now() - 25*60*60*1000;
+  // ensure that suggested tile updates again
+  suggestedLink = DirectoryLinksProvider._updateSuggestedTile();
+  do_check_true(suggestedLink.frecent_sites);
+
+  // Cleanup
+  yield promiseCleanDirectoryLinksProvider();
+  NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
+  NewTabUtils.getProviderLinks = origGetProviderLinks;
+  DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
+});
--- a/devtools/client/jsonview/css/search-box.css
+++ b/devtools/client/jsonview/css/search-box.css
@@ -21,26 +21,26 @@
 }
 
 /******************************************************************************/
 /* Light Theme & Dark Theme*/
 
 .theme-dark .searchBox,
 .theme-light .searchBox {
   border: 1px solid rgb(170, 170, 170);
-  background-image: url("chrome://browser/skin/devtools/magnifying-glass-light.png");
+  background-image: url("chrome://devtools/skin/themes/images/magnifying-glass-light.png");
   background-position: 8px center;
   border-radius: 2px;
   padding-left: 25px;
   margin-top: 1px;
   height: 16px;
   font-style: italic;
 }
 
 /******************************************************************************/
 /* Dark Theme */
 
 .theme-dark .searchBox {
   background-color: rgba(24, 29, 32, 1);
   color: rgba(184, 200, 217, 1);
   border-color: var(--theme-splitter-color);
-  background-image: url("chrome://browser/skin/devtools/magnifying-glass.png");
+  background-image: url("chrome://devtools/skin/themes/images/magnifying-glass.png");
 }
--- a/devtools/client/memory/test/mochitest/test_census-view-01.html
+++ b/devtools/client/memory/test/mochitest/test_census-view-01.html
@@ -2,62 +2,65 @@
 <html>
 <!--
 Bug 1067491 - Test taking a census over the RDP.
 -->
 <head>
   <meta charset="utf-8">
   <title>Census Tree 01</title>
   <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link href="chrome://browser/content/devtools/widgets.css" type="text/css" />
+  <link href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css" />
   <link href="chrome://devtools/skin/themes/light-theme.css" type="text/css" />
   <link href="chrome://devtools/skin/themes/common.css" type="text/css" />
   <link href="chrome://devtools/skin/themes/widgets.css" type="text/css" />
   <link href="chrome://devtools/skin/themes/memory.css" type="text/css" />
 </head>
 <body>
 <ul id="container" style="width:100%;height:300px;"></ul>
 <pre id="test">
 <script src="head.js" type="application/javascript;version=1.8"></script>
 <script>
 window.onload = function() {
-  var { CensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
+  var { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
   var { INDENTATION, CensusView } = require("devtools/client/memory/modules/census-view");
   SimpleTest.waitForExplicitFinish();
   const countBreakdown = { by: "count", count: true, bytes: true };
 
   const BREAKDOWN = {
     by: "coarseType",
     objects: { by: "objectClass", then: countBreakdown },
     strings: countBreakdown,
+    scripts: countBreakdown,
     other: { by: "internalType", then: countBreakdown },
   };
 
   const REPORT = {
     "objects": {
       "Function": { bytes: 10, count: 1 },
       "Array": { bytes: 20, count: 2 },
     },
     "strings": { bytes: 10, count: 1 },
+    "scripts": { bytes: 20, count: 2 },
     "other": {
       "js::Shape": { bytes: 30, count: 3 },
       "js::Shape2": { bytes: 40, count: 4 }
     },
   };
 
   const EXPECTED_ROWS = [
-    { level: 0, name: "strings", bytes: 10, count: 1, },
-    { level: 0, name: "objects" },
+    { level: 0, name: "other", bytes: 0, count: 0 },
+        { level: 1, name: "js::Shape2", bytes: 40, count: 4, },
+        { level: 1, name: "js::Shape", bytes: 30, count: 3, },
+    { level: 0, name: "objects", bytes: 0, count: 0 },
         { level: 1, name: "Array", bytes: 20, count: 2, },
         { level: 1, name: "Function", bytes: 10, count: 1, },
-    { level: 0, name: "other" },
-        { level: 1, name: "js::Shape2", bytes: 40, count: 4, },
-        { level: 1, name: "js::Shape", bytes: 30, count: 3, },
+    { level: 0, name: "scripts", bytes: 20, count: 2, },
+    { level: 0, name: "strings", bytes: 10, count: 1, },
   ];
-  var censusTreeNode = new CensusTreeNode(BREAKDOWN, REPORT);
+  var censusTreeNode = censusReportToCensusTreeNode(BREAKDOWN, REPORT);
 
   var view = new CensusView({
     censusTreeNode: censusTreeNode,
     hidden: true
   });
 
   view.attachTo(document.querySelector("#container"));
 
@@ -76,24 +79,24 @@ window.onload = function() {
 
     is(el.style.MozMarginStart, (INDENTATION * expected.level) + "px",
       `correct indentation for ${expected.name}`);
 
     if ("bytes" in expected) {
       is(bytesEl.innerHTML, String(expected.bytes),
         `correct bytes "${expected.bytes}" in heap tree`);
     } else {
-      ok(!bytesEl, "no bytes correctly displayed for ${expected.name}");
+      ok(!bytesEl, `no bytes correctly displayed for ${expected.name}`);
     }
 
     if ("count" in expected) {
       is(countEl.innerHTML, String(expected.count),
         `correct count "${expected.count}" in heap tree`);
     } else {
-      ok(!countEl, "no count correctly displayed for ${expected.name}");
+      ok(!countEl, `no count correctly displayed for ${expected.name}`);
     }
   }
 
   SimpleTest.finish();
 };
 </script>
 </pre>
 </body>
--- a/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js
+++ b/devtools/client/memory/test/unit/test_action-set-breakdown-and-refresh-02.js
@@ -5,17 +5,17 @@
  * Tests the task creator `setBreakdownAndRefreshAndRefresh()` for custom
  * breakdowns.
  */
 
 let { snapshotState: states } = require("devtools/client/memory/constants");
 let { breakdownEquals } = require("devtools/client/memory/utils");
 let { setBreakdownAndRefresh } = require("devtools/client/memory/actions/breakdown");
 let { takeSnapshotAndCensus } = require("devtools/client/memory/actions/snapshot");
-let custom = { by: "internalType", then: { by: "count", bytes: true }};
+let custom = { by: "internalType", then: { by: "count", bytes: true, count: false }};
 
 function run_test() {
   run_next_test();
 }
 
 add_task(function *() {
   let front = new StubbedMemoryFront();
   let heapWorker = new HeapAnalysesClient();
@@ -29,10 +29,14 @@ add_task(function *() {
 
   dispatch(takeSnapshotAndCensus(front, heapWorker));
   yield waitUntilSnapshotState(store, [states.SAVED_CENSUS]);
 
   ok(breakdownEquals(getState().snapshots[0].breakdown, custom),
     "New snapshot stored custom breakdown when done taking census");
   ok(getState().snapshots[0].census.children.length, "Census has some children");
   // Ensure we don't have `count` in any results
-  ok(getState().snapshots[0].census.children.every(c => !c.count), "Census used custom breakdown");
+  ok(getState().snapshots[0].census.children.every(c => !c.count),
+     "Census used custom breakdown without counts");
+  // Ensure we do have `bytes` in the results
+  ok(getState().snapshots[0].census.children.every(c => typeof c.bytes === "number"),
+     "Census used custom breakdown with bytes");
 });
--- a/devtools/client/performance/legacy/actors.js
+++ b/devtools/client/performance/legacy/actors.js
@@ -67,16 +67,17 @@ LegacyProfilerFront.prototype = {
    * Unregisters events for the underlying profiler actor.
    */
   destroy: Task.async(function *() {
     if (this._poller) {
       yield this._poller.destroy();
     }
     yield this.unregisterEventNotifications({ events: this.EVENTS });
     this._target.client.removeListener("eventNotification", this._onProfilerEvent);
+    yield this._front.destroy();
   }),
 
   /**
    * Starts the profiler actor, if necessary.
    *
    * @option {number?} bufferSize
    * @option {number?} sampleFrequency
    */
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -648,16 +648,18 @@ box.requests-menu-status[code^="5"] {
   padding-top: 3px;
   font-weight: 600;
 }
 
 #requests-menu-filter-freetext-text {
   transition-property: max-width, -moz-padding-end, -moz-padding-start;
   transition-duration: 250ms;
   transition-timing-function: ease;
+  margin-left: 3px;
+  margin-right: 3px;
 }
 
 #requests-menu-filter-freetext-text:not([focused]):not([filled]) > .textbox-input-box {
   overflow: hidden;
 }
 
 #requests-menu-filter-freetext-text:not([focused]):not([filled]) {
   max-width: 20px !important;
--- a/devtools/client/themes/toolbars.inc.css
+++ b/devtools/client/themes/toolbars.inc.css
@@ -323,30 +323,28 @@
   }
 }
 
 /* Text input */
 
 .devtools-textinput,
 .devtools-searchinput {
   -moz-appearance: none;
-  margin: 0 3px;
+  margin: 1px 0;
   border: 1px solid;
 %ifdef XP_MACOSX
   border-radius: 20px;
 %else
   border-radius: 2px;
 %endif
   padding: 4px 6px;
   border-color: var(--theme-splitter-color);
 }
 
 .devtools-searchinput {
-  margin-top: 1px;
-  margin-bottom: 1px;
   padding: 0;
   -moz-padding-start: 22px;
   -moz-padding-end: 4px;
   background-position: 8px center;
   background-size: 11px 11px;
   background-repeat: no-repeat;
   font-size: inherit;
 }
--- a/devtools/client/webide/content/webide.js
+++ b/devtools/client/webide/content/webide.js
@@ -68,19 +68,16 @@ window.addEventListener("unload", functi
 
 var UI = {
   init: function() {
     this._telemetry = new Telemetry();
     this._telemetry.toolOpened("webide");
 
     AppManager.init();
 
-    this.onMessage = this.onMessage.bind(this);
-    window.addEventListener("message", this.onMessage);
-
     this.appManagerUpdate = this.appManagerUpdate.bind(this);
     AppManager.on("app-manager-update", this.appManagerUpdate);
 
     Cmds.showProjectPanel();
     Cmds.showRuntimePanel();
 
     this.updateCommands();
 
@@ -131,17 +128,16 @@ var UI = {
     Simulators.on("configure", this.configureSimulator);
   },
 
   destroy: function() {
     window.removeEventListener("focus", this.onfocus, true);
     AppManager.off("app-manager-update", this.appManagerUpdate);
     AppManager.destroy();
     Simulators.off("configure", this.configureSimulator);
-    window.removeEventListener("message", this.onMessage);
     this.updateConnectionTelemetry();
     this._telemetry.toolClosed("webide");
     this._telemetry.toolClosed("webideProjectEditor");
     this._telemetry.destroy();
   },
 
   canCloseProject: function() {
     if (this.projecteditor) {
@@ -920,107 +916,99 @@ var UI = {
       if (deviceDate - localDate > 7 * MS_PER_DAY) {
         this.reportError("error_runtimeVersionTooRecent", deviceID, localID);
       }
     }
   }),
 
   /********** TOOLBOX **********/
 
-  onMessage: function(event) {
-    // The custom toolbox sends a message to its parent
-    // window.
-    try {
-      let json = JSON.parse(event.data);
-      switch (json.name) {
-        case "toolbox-close":
-          // There are many ways to close a toolbox:
-          // * Close button inside the toolbox
-          // * Toggle toolbox wrench in WebIDE
-          // * Disconnect the current runtime gracefully
-          // * Yank cord out of device
-          // We can't know for sure which one was used here, so reset the
-          // |toolboxPromise| since someone must be destroying it to reach here,
-          // and call our own close method.
-          if (this.toolboxIframe && this.toolboxIframe.uid == json.uid) {
-            this.toolboxPromise = null;
-            this._closeToolboxUI();
-          }
-          break;
-      }
-    } catch(e) { console.error(e); }
+  /**
+   * There are many ways to close a toolbox:
+   *   * Close button inside the toolbox
+   *   * Toggle toolbox wrench in WebIDE
+   *   * Disconnect the current runtime gracefully
+   *   * Yank cord out of device
+   *   * Close or crash the app/tab
+   * We can't know for sure which one was used here, so reset the
+   * |toolboxPromise| since someone must be destroying it to reach here,
+   * and call our own close method.
+   */
+  _onToolboxClosed: function(promise, iframe) {
+    // Only save toolbox size, disable wrench button, workaround focus issue...
+    // if we are closing the last toolbox:
+    //  - toolboxPromise is nullified by destroyToolbox and is still null here
+    //    if no other toolbox has been opened in between,
+    //  - having two distinct promise means we are receiving closed event
+    //    for a previous, non-current, toolbox.
+    if (!this.toolboxPromise || this.toolboxPromise === promise) {
+      this.toolboxPromise = null;
+      this.resetFocus();
+      Services.prefs.setIntPref("devtools.toolbox.footer.height", iframe.height);
+
+      let splitter = document.querySelector(".devtools-horizontal-splitter");
+      splitter.setAttribute("hidden", "true");
+      document.querySelector("#action-button-debug").removeAttribute("active");
+    }
+    // We have to destroy the iframe, otherwise, the keybindings of webide don't work
+    // properly anymore.
+    iframe.remove();
   },
 
   destroyToolbox: function() {
     // Only have a live toolbox if |this.toolboxPromise| exists
     if (this.toolboxPromise) {
       let toolboxPromise = this.toolboxPromise;
       this.toolboxPromise = null;
-      return toolboxPromise.then(toolbox => {
-        return toolbox.destroy();
-      }).then(null, console.error)
-        .then(() => this._closeToolboxUI())
-        .then(null, console.error);
+      return toolboxPromise.then(toolbox => toolbox.destroy());
     }
     return promise.resolve();
   },
 
   createToolbox: function() {
     // If |this.toolboxPromise| exists, there is already a live toolbox
     if (this.toolboxPromise) {
       return this.toolboxPromise;
     }
-    this.toolboxPromise = AppManager.getTarget().then((target) => {
-      return this._showToolbox(target);
-    }, console.error);
-    return this.busyUntil(this.toolboxPromise, "opening toolbox");
-  },
-
-  _showToolbox: function(target) {
-    let splitter = document.querySelector(".devtools-horizontal-splitter");
-    splitter.removeAttribute("hidden");
 
     let iframe = document.createElement("iframe");
     iframe.id = "toolbox";
 
     // Compute a uid on the iframe in order to identify toolbox iframe
     // when receiving toolbox-close event
     iframe.uid = new Date().getTime();
 
+    let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");
+    iframe.height = height;
+
+    let promise = this.toolboxPromise = AppManager.getTarget().then(target => {
+      return this._showToolbox(target, iframe);
+    }).then(toolbox => {
+      // Destroy the toolbox on WebIDE side before
+      // toolbox.destroy's promise resolves.
+      toolbox.once("destroyed", this._onToolboxClosed.bind(this, promise, iframe));
+      return toolbox;
+    }, console.error);
+
+    return this.busyUntil(this.toolboxPromise, "opening toolbox");
+  },
+
+  _showToolbox: function(target, iframe) {
+    let splitter = document.querySelector(".devtools-horizontal-splitter");
+    splitter.removeAttribute("hidden");
+
     document.querySelector("notificationbox").insertBefore(iframe, splitter.nextSibling);
     let host = Toolbox.HostType.CUSTOM;
     let options = { customIframe: iframe, zoom: false, uid: iframe.uid };
-    this.toolboxIframe = iframe;
-
-    let height = Services.prefs.getIntPref("devtools.toolbox.footer.height");
-    iframe.height = height;
 
     document.querySelector("#action-button-debug").setAttribute("active", "true");
 
     return gDevTools.showToolbox(target, null, host, options);
   },
 
-  _closeToolboxUI: function() {
-    if (!this.toolboxIframe) {
-      return;
-    }
-
-    this.resetFocus();
-    Services.prefs.setIntPref("devtools.toolbox.footer.height", this.toolboxIframe.height);
-
-    // We have to destroy the iframe, otherwise, the keybindings of webide don't work
-    // properly anymore.
-    this.toolboxIframe.remove();
-    this.toolboxIframe = null;
-
-    let splitter = document.querySelector(".devtools-horizontal-splitter");
-    splitter.setAttribute("hidden", "true");
-    document.querySelector("#action-button-debug").removeAttribute("active");
-  },
-
   prePackageLog: function (msg) {
     if (msg == "start") {
       UI.selectDeckPanel("logs");
     }
   }
 };
 
 EventEmitter.decorate(UI);
@@ -1101,17 +1089,17 @@ var Cmds = {
   }),
 
   stop: function() {
     return UI.busyUntil(AppManager.stopRunningApp(), "stopping app");
   },
 
   toggleToolbox: function() {
     UI.onAction("debug");
-    if (UI.toolboxIframe) {
+    if (UI.toolboxPromise) {
       UI.destroyToolbox();
       return promise.resolve();
     } else {
       return UI.createToolbox();
     }
   },
 
   removeProject: function() {
--- a/devtools/client/webide/modules/app-manager.js
+++ b/devtools/client/webide/modules/app-manager.js
@@ -360,20 +360,39 @@ var AppManager = exports.AppManager = {
     if (manifest && this._appsFront) {
       return this._appsFront.apps.get(manifest);
     }
     return null;
   },
 
   _selectedProject: null,
   set selectedProject(project) {
-    // A regular comparison still sees a difference when equal in some cases
-    if (JSON.stringify(this._selectedProject) ===
-        JSON.stringify(project)) {
+    // A regular comparison doesn't work as we recreate a new object every time
+    let prev = this._selectedProject;
+    if (!prev && !project) {
       return;
+    } else if (prev && project && prev.type === project.type) {
+      let type = project.type;
+      if (type === "runtimeApp") {
+        if (prev.app.manifestURL === project.app.manifestURL) {
+          return;
+        }
+      } else if (type === "tab") {
+        if (prev.app.actor === project.app.actor) {
+          return;
+        }
+      } else if (type === "packaged" || type === "hosted") {
+        if (prev.location === project.location) {
+          return;
+        }
+      } else if (type === "mainProcess") {
+        return;
+      } else {
+        throw new Error("Unsupported project type: " + type);
+      }
     }
 
     let cancelled = false;
     this.update("before-project", { cancel: () => { cancelled = true; } });
     if (cancelled)  {
       return;
     }
 
--- a/devtools/client/webide/test/chrome.ini
+++ b/devtools/client/webide/test/chrome.ini
@@ -56,8 +56,9 @@ skip-if = true # Bug 1201392 - Update ad
 [test_telemetry.html]
 skip-if = true # Bug 1201392 - Update add-ons after migration
 [test_device_preferences.html]
 [test_device_settings.html]
 [test_fullscreenToolbox.html]
 [test_zoom.html]
 [test_build.html]
 [test_simulators.html]
+[test_toolbox.html]
--- a/devtools/client/webide/test/test_autoselect_project.html
+++ b/devtools/client/webide/test/test_autoselect_project.html
@@ -73,16 +73,28 @@
           items[1].click();
 
           yield waitForUpdate(win, "runtime-targets");
 
           is(Object.keys(DebuggerServer._connections).length, 1, "Locally connected");
           ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
           is(win.AppManager.selectedProject.type, "mainProcess", "Main process reselected");
 
+          // Wait for the toolbox to be fully loaded
+          yield win.UI.toolboxPromise;
+
+          // If we happen to pass a project object targeting the same context,
+          // here, the main process, the `selectedProject` attribute shouldn't be updated
+          // so that no `project` event would fire.
+          let oldProject = win.AppManager.selectedProject;
+          win.AppManager.selectedProject = {
+            type: "mainProcess"
+          };
+          is(win.AppManager.selectedProject, oldProject, "AppManager.selectedProject shouldn't be updated if we selected the same project");
+
           yield win.Cmds.disconnectRuntime();
 
           yield closeWebIDE(win);
 
           DebuggerServer.destroy();
 
           SimpleTest.finish();
         });
--- a/devtools/client/webide/test/test_fullscreenToolbox.html
+++ b/devtools/client/webide/test/test_fullscreenToolbox.html
@@ -42,18 +42,16 @@
             docProject.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
           });
 
           yield waitForUpdate(win, "project");
 
           ok(win.UI.toolboxPromise, "Toolbox promise exists");
           yield win.UI.toolboxPromise;
 
-          ok(win.UI.toolboxIframe, "Toolbox iframe exists");
-
           let nbox = win.document.querySelector("#notificationbox");
           ok(!nbox.hasAttribute("toolboxfullscreen"), "Toolbox is not fullscreen");
 
           win.Cmds.showRuntimeDetails();
 
           ok(!nbox.hasAttribute("toolboxfullscreen"), "Toolbox is not fullscreen");
 
           yield win.Cmds.disconnectRuntime();
--- a/devtools/client/webide/test/test_runtime.html
+++ b/devtools/client/webide/test/test_runtime.html
@@ -152,18 +152,16 @@
           });
 
           yield waitForUpdate(win, "project");
 
           // Toolbox opens automatically for main process / runtime apps
           ok(win.UI.toolboxPromise, "Toolbox promise exists");
           yield win.UI.toolboxPromise;
 
-          ok(win.UI.toolboxIframe, "Toolbox iframe exists");
-
           yield win.Cmds.disconnectRuntime();
 
           Services.prefs.setIntPref("devtools.webide.busyTimeout", 100);
 
           // Wait for error message since connection never completes
           let errorDeferred = promise.defer();
           win.UI.reportError = errorName => {
             if (errorName === "error_operationTimeout") {
new file mode 100644
--- /dev/null
+++ b/devtools/client/webide/test/test_toolbox.html
@@ -0,0 +1,95 @@
+<!DOCTYPE html>
+
+<html>
+
+  <head>
+    <meta charset="utf8">
+    <title></title>
+
+    <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="application/javascript" src="chrome://mochikit/content/chrome-harness.js"></script>
+    <script type="application/javascript;version=1.8" src="head.js"></script>
+    <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  </head>
+
+  <body>
+
+    <script type="application/javascript;version=1.8">
+      const { DebuggerServer } = require("devtools/server/main");
+
+      window.onload = function() {
+        SimpleTest.waitForExplicitFinish();
+
+        let win;
+
+        SimpleTest.registerCleanupFunction(() => {
+          Task.spawn(function*() {
+            if (win) {
+              yield closeWebIDE(win);
+            }
+            DebuggerServer.destroy();
+            yield removeAllProjects();
+          });
+        });
+
+        Task.spawn(function*() {
+          if (!DebuggerServer.initialized) {
+            DebuggerServer.init();
+            DebuggerServer.addBrowserActors();
+          }
+
+          win = yield openWebIDE();
+          let docRuntime = getRuntimeDocument(win);
+          let docProject = getProjectDocument(win);
+
+          win.AppManager.update("runtime-list");
+
+          let deferred = promise.defer();
+          win.AppManager.connection.once(
+              win.Connection.Events.CONNECTED,
+              () => deferred.resolve());
+
+          docRuntime.querySelectorAll(".runtime-panel-item-other")[1].click();
+
+          ok(win.document.querySelector("window").className, "busy", "UI is busy");
+          yield win.UI._busyPromise;
+
+          is(Object.keys(DebuggerServer._connections).length, 1, "Connected");
+
+          yield waitForUpdate(win, "runtime-global-actors");
+
+          ok(win.AppManager.isMainProcessDebuggable(), "Main process available");
+
+          // Select main process
+          SimpleTest.executeSoon(() => {
+            docProject.querySelectorAll("#project-panel-runtimeapps .panel-item")[0].click();
+          });
+
+          yield waitForUpdate(win, "project");
+
+          // Toolbox opens automatically for main process / runtime apps
+          ok(win.UI.toolboxPromise, "Toolbox promise exists");
+          let toolbox = yield win.UI.toolboxPromise;
+
+          yield toolbox.destroy();
+
+          ok(!win.UI.toolboxPromise, "Toolbox promise should be unset once toolbox.destroy()'s promise resolves");
+
+          // Reopen the toolbox right after to check races and also
+          // opening a toolbox more than just once against the same target
+          yield win.Cmds.toggleToolbox();
+
+          ok(win.UI.toolboxPromise, "Toolbox promise exists");
+
+          yield win.UI.destroyToolbox();
+
+          ok(!win.UI.toolboxPromise, "Toolbox promise is also nullified the second times");
+
+          yield win.Cmds.disconnectRuntime();
+
+          SimpleTest.finish();
+        });
+      }
+    </script>
+  </body>
+</html>
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -1025,17 +1025,21 @@ var DebuggerServer = {
       if (childTransport) {
         // If we have a child transport, the actor has already
         // been created. We need to stop using this message manager.
         childTransport.close();
         childTransport = null;
         aConnection.cancelForwarding(prefix);
 
         // ... and notify the child process to clean the tab actors.
-        mm.sendAsyncMessage("debug:disconnect", { prefix: prefix });
+        try {
+          // Bug 1169643: Ignore any exception as the child process
+          // may already be destroyed by now.
+          mm.sendAsyncMessage("debug:disconnect", { prefix: prefix });
+        } catch(e) {}
       } else {
         // Otherwise, the app has been closed before the actor
         // had a chance to be created, so we are not able to create
         // the actor.
         deferred.resolve(null);
       }
       if (actor) {
         // The ContentActor within the child process doesn't necessary
new file mode 100644
--- /dev/null
+++ b/devtools/server/tests/unit/test_client_close.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var gClient;
+var gDebuggee;
+
+function run_test()
+{
+  initTestDebuggerServer();
+  gDebuggee = testGlobal("test-1");
+  DebuggerServer.addTestGlobal(gDebuggee);
+
+  let transport = DebuggerServer.connectPipe();
+  gClient = new DebuggerClient(transport);
+  gClient.connect(function(aType, aTraits) {
+    attachTestTab(gClient, "test-1", function(aReply, aTabClient) {
+      test_close(transport);
+    });
+  });
+  do_test_pending();
+}
+
+function test_close(aTransport)
+{
+  // Check that, if we fake a transport shutdown
+  // (like if a device is unplugged)
+  // the client is automatically closed,
+  // and we can still call client.close.
+  let onClosed = function() {
+    gClient.removeListener("closed", onClosed);
+    ok(true, "Client emitted 'closed' event");
+    gClient.close(function() {
+      ok(true, "client.close() successfully called its callback");
+      do_test_finished();
+    });
+  }
+  gClient.addListener("closed", onClosed);
+  aTransport.close();
+}
--- a/devtools/server/tests/unit/test_client_request.js
+++ b/devtools/server/tests/unit/test_client_request.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test the DebuggerClient.request API.
 
-var gClient;
+var gClient, gActorId;
 
 function TestActor(conn) {
   this.conn = conn;
 }
 TestActor.prototype = {
   actorPrefix: "test",
 
   hello: function () {
@@ -31,17 +31,19 @@ function run_test()
   DebuggerServer.init();
   DebuggerServer.addBrowserActors();
 
   add_test(init);
   add_test(test_client_request_callback);
   add_test(test_client_request_promise);
   add_test(test_client_request_promise_error);
   add_test(test_client_request_event_emitter);
-  add_test(close_client);
+  add_test(test_close_client_while_sending_requests);
+  add_test(test_client_request_after_close);
+  add_test(test_client_request_after_close_callback);
   run_next_test();
 }
 
 function init()
 {
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect(function onConnect() {
     gClient.listTabs(function onListTabs(aResponse) {
@@ -130,13 +132,83 @@ function test_client_request_event_emitt
   request.on("json-reply", reply => {
     do_check_eq(reply.from, gActorId);
     do_check_eq(reply.hello, "world");
     checkStack("test_client_request_event_emitter");
     run_next_test();
   });
 }
 
-function close_client() {
+function test_close_client_while_sending_requests() {
+  // First send a first request that will be "active"
+  // while the connection is closed.
+  // i.e. will be sent but no response received yet.
+  let activeRequest = gClient.request({
+    to: gActorId,
+    type: "hello"
+  });
+
+  // Pile up a second one that will be "pending".
+  // i.e. won't event be sent.
+  let pendingRequest = gClient.request({
+    to: gActorId,
+    type: "hello"
+  });
+
+  let expectReply = promise.defer()
+  gClient.expectReply("root", function(response) {
+    do_check_eq(response.error, "connectionClosed");
+    do_check_eq(response.message, "server side packet from 'root' can't be received as the connection just closed.");
+    expectReply.resolve();
+  });
+
   gClient.close(() => {
-    run_next_test()
+    activeRequest.then(() => {
+      ok(false, "First request unexpectedly succeed while closing the connection");
+    }, response => {
+      do_check_eq(response.error, "connectionClosed");
+      do_check_eq(response.message, "'hello' active request packet to '" + gActorId + "' can't be sent as the connection just closed.");
+    })
+    .then(() => pendingRequest)
+    .then(() => {
+      ok(false, "Second request unexpectedly succeed while closing the connection");
+    }, response => {
+      do_check_eq(response.error, "connectionClosed");
+      do_check_eq(response.message, "'hello' pending request packet to '" + gActorId + "' can't be sent as the connection just closed.");
+    })
+    .then(() => expectReply.promise)
+    .then(run_next_test)
   });
 }
+
+function test_client_request_after_close()
+{
+  // Test that DebuggerClient.request fails after we called client.close()
+  // (with promise API)
+  let request = gClient.request({
+    to: gActorId,
+    type: "hello"
+  });
+
+  request.then(response => {
+    ok(false, "Request succeed even after client.close");
+  }, response => {
+    ok(true, "Request failed after client.close");
+    do_check_eq(response.error, "connectionClosed");
+    ok(response.message.match(/'hello' request packet to '.*' can't be sent as the connection is closed./));
+    run_next_test();
+  });
+}
+
+function test_client_request_after_close_callback()
+{
+  // Test that DebuggerClient.request fails after we called client.close()
+  // (with callback API)
+  let request = gClient.request({
+    to: gActorId,
+    type: "hello"
+  }, response => {
+    ok(true, "Request failed after client.close");
+    do_check_eq(response.error, "connectionClosed");
+    ok(response.message.match(/'hello' request packet to '.*' can't be sent as the connection is closed./));
+    run_next_test();
+  });
+}
--- a/devtools/server/tests/unit/xpcshell.ini
+++ b/devtools/server/tests/unit/xpcshell.ini
@@ -262,8 +262,9 @@ reason = bug 1014071
 [test_setBreakpoint-on-line.js]
 [test_setBreakpoint-on-line-in-gcd-script.js]
 [test_setBreakpoint-on-line-with-multiple-offsets.js]
 [test_setBreakpoint-on-line-with-multiple-statements.js]
 [test_setBreakpoint-on-line-with-no-offsets.js]
 [test_setBreakpoint-on-line-with-no-offsets-at-end-of-script.js]
 [test_setBreakpoint-on-line-with-no-offsets-in-gcd-script.js]
 [test_safe-getter.js]
+[test_client_close.js]
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -351,37 +351,48 @@ DebuggerClient.prototype = {
    *        If specified, will be called when the debugging connection
    *        has been closed.
    */
   close: function (aOnClosed) {
     // Disable detach event notifications, because event handlers will be in a
     // cleared scope by the time they run.
     this._eventsEnabled = false;
 
+    let cleanup = () => {
+      this._transport.close();
+      this._transport = null;
+    };
+
+    // If the connection is already closed,
+    // there is no need to detach client
+    // as we won't be able to send any message.
+    if (this._closed) {
+      cleanup();
+      if (aOnClosed) {
+        aOnClosed();
+      }
+      return;
+    }
+
     if (aOnClosed) {
       this.addOneTimeListener('closed', function (aEvent) {
         aOnClosed();
       });
     }
 
     // Call each client's `detach` method by calling
     // lastly registered ones first to give a chance
     // to detach child clients first.
     let clients = [...this._clients.values()];
     this._clients.clear();
     const detachClients = () => {
       let client = clients.pop();
       if (!client) {
         // All clients detached.
-        this._transport.close();
-        this._transport = null;
-        this._activeRequests.clear();
-        this._activeRequests = null;
-        this._pendingRequests.clear();
-        this._pendingRequests = null;
+        cleanup();
         return;
       }
       if (client.detach) {
         client.detach(detachClients);
         return;
       }
       detachClients();
     };
@@ -659,20 +670,30 @@ DebuggerClient.prototype = {
    *                     rejected if any (unexpected) errors occur.
    *                     This object also emits "progress" events for each chunk
    *                     that is copied.  See stream-utils.js.
    */
   request: function (aRequest, aOnResponse) {
     if (!this.mainRoot) {
       throw Error("Have not yet received a hello packet from the server.");
     }
+    let type = aRequest.type || "";
     if (!aRequest.to) {
-      let type = aRequest.type || "";
       throw Error("'" + type + "' request packet has no destination.");
     }
+    if (this._closed) {
+      let msg = "'" + type + "' request packet to " +
+                "'" + aRequest.to + "' " +
+               "can't be sent as the connection is closed.";
+      let resp = { error: "connectionClosed", message: msg };
+      if (aOnResponse) {
+        aOnResponse(resp);
+      }
+      return promise.reject(resp);
+    }
 
     let request = new Request(aRequest);
     request.format = "json";
     request.stack = components.stack;
     if (aOnResponse) {
       request.on("json-reply", aOnResponse);
     }
 
@@ -1036,18 +1057,43 @@ DebuggerClient.prototype = {
   /**
    * Called by DebuggerTransport when the underlying stream is closed.
    *
    * @param aStatus nsresult
    *        The status code that corresponds to the reason for closing
    *        the stream.
    */
   onClosed: function (aStatus) {
+    this._closed = true;
     this.emit("closed");
 
+    // Reject all pending and active requests
+    let reject = function(type, request, actor) {
+      // Server can send packets on its own and client only pass a callback
+      // to expectReply, so that there is no request object.
+      let msg;
+      if (request.request) {
+        msg = "'" + request.request.type + "' " + type + " request packet" +
+              " to '" + actor + "' " +
+              "can't be sent as the connection just closed.";
+      } else {
+        msg = "server side packet from '" + actor + "' can't be received " +
+              "as the connection just closed.";
+      }
+      let packet = { error: "connectionClosed", message: msg };
+      request.emit("json-reply", packet);
+    };
+
+    this._pendingRequests.forEach((list, actor) => {
+      list.forEach(request => reject("pending", request, actor));
+    });
+    this._pendingRequests.clear();
+    this._activeRequests.forEach(reject.bind(null, "active"));
+    this._activeRequests.clear();
+
     // The |_pools| array on the client-side currently is used only by
     // protocol.js to store active fronts, mirroring the actor pools found in
     // the server.  So, read all usages of "pool" as "protocol.js front".
     //
     // In the normal case where we shutdown cleanly, the toolbox tells each tool
     // to close, and they each call |destroy| on any fronts they were using.
     // When |destroy| or |cleanup| is called on a protocol.js front, it also
     // removes itself from the |_pools| array.  Once the toolbox has shutdown,
--- a/devtools/shared/heapsnapshot/CensusUtils.js
+++ b/devtools/shared/heapsnapshot/CensusUtils.js
@@ -12,29 +12,39 @@ function Visitor() { };
 exports.Visitor = Visitor;
 
 /**
  * The `enter` method is called when a new sub-report is entered in traversal.
  *
  * @param {Object} breakdown
  *        The breakdown for the sub-report that is being entered by traversal.
  *
+ * @param {Object} report
+ *        The report generated by the given breakdown.
+ *
  * @param {any} edge
  *        The edge leading to this sub-report. The edge is null if (but not iff!
  *        eg, null allocation stack edges) we are entering the root report.
  */
-Visitor.prototype.enter = function (breakdown, edge) { };
+Visitor.prototype.enter = function (breakdown, report, edge) { };
 
 /**
  * The `exit` method is called when traversal of a sub-report has finished.
  *
  * @param {Object} breakdown
  *        The breakdown for the sub-report whose traversal has finished.
+ *
+ * @param {Object} report
+ *        The report generated by the given breakdown.
+ *
+ * @param {any} edge
+ *        The edge leading to this sub-report. The edge is null if (but not iff!
+ *        eg, null allocation stack edges) we are entering the root report.
  */
-Visitor.prototype.exit = function (breakdown) { };
+Visitor.prototype.exit = function (breakdown, report, edge) { };
 
 /**
  * The `count` method is called when leaf nodes (reports whose breakdown is
  * by: "count") in the report tree are encountered.
  *
  * @param {Object} breakdown
  *        The count breakdown for this report.
  *
@@ -106,25 +116,25 @@ function getReportEdges(breakdown, repor
   return EDGES[breakdown.by](breakdown, report);
 }
 exports.getReportEdges = getReportEdges;
 
 /*** walk *******************************************************************/
 
 function recursiveWalk(breakdown, edge, report, visitor) {
   if (breakdown.by === "count") {
-    visitor.enter(breakdown, edge);
+    visitor.enter(breakdown, report, edge);
     visitor.count(breakdown, report, edge);
-    visitor.exit(breakdown, edge);
+    visitor.exit(breakdown, report, edge);
   } else {
-    visitor.enter(breakdown, edge);
+    visitor.enter(breakdown, report, edge);
     for (let { edge, referent, breakdown } of getReportEdges(breakdown, report)) {
       recursiveWalk(breakdown, edge, referent, visitor);
     }
-    visitor.exit(breakdown, edge);
+    visitor.exit(breakdown, report, edge);
   }
 };
 
 /**
  * Walk the given `report` that was generated by taking a census with the
  * specified `breakdown`.
  *
  * @param {Object} breakdown
@@ -201,17 +211,17 @@ DiffVisitor.prototype._set = function (r
   } else {
     report[edge] = val;
   }
 };
 
 /**
  * @overrides Visitor.prototype.enter
  */
-DiffVisitor.prototype.enter = function (breakdown, edge) {
+DiffVisitor.prototype.enter = function (breakdown, report, edge) {
   const isFirstTimeEntering = this._results === null;
 
   const newResults = breakdown.by === "allocationStack" ? new Map() : {};
   let newOther;
 
   if (!this._results) {
     // This is the first time we have entered a sub-report.
     this._results = newResults;
@@ -230,17 +240,17 @@ DiffVisitor.prototype.enter = function (
   const visited = this._edgesVisited[this._edgesVisited.length - 1];
   visited.add(edge);
   this._edgesVisited.push(new Set());
 };
 
 /**
  * @overrides Visitor.prototype.exit
  */
-DiffVisitor.prototype.exit = function (breakdown) {
+DiffVisitor.prototype.exit = function (breakdown, report, edge) {
   // Find all the edges in the other census report that were not traversed and
   // add them to the results directly.
   const other = this._otherCensusStack[this._otherCensusStack.length - 1];
   if (other) {
     const visited = this._edgesVisited[this._edgesVisited.length - 1];
     const unvisited = getReportEdges(breakdown, other)
       .map(e => e.edge)
       .filter(e => !visited.has(e));
--- a/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
+++ b/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
@@ -7,17 +7,17 @@
 // heavyweight analyses on them without blocking the main thread. A
 // HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
 // instance. See HeapAnalysesClient.js.
 
 "use strict";
 
 importScripts("resource://gre/modules/workers/require.js");
 importScripts("resource://devtools/shared/worker/helper.js");
-const { CensusTreeNode } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
+const { censusReportToCensusTreeNode } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
 const CensusUtils = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
 
 // The set of HeapSnapshot instances this worker has read into memory. Keyed by
 // snapshot file path.
 const snapshots = Object.create(null);
 
 /**
  * @see HeapAnalysesClient.prototype.readHeapSnapshot
@@ -33,17 +33,17 @@ workerHelper.createTask(self, "readHeapS
  */
 workerHelper.createTask(self, "takeCensus", ({ snapshotFilePath, censusOptions, requestOptions }) => {
   if (!snapshots[snapshotFilePath]) {
     throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
   }
 
   let report = snapshots[snapshotFilePath].takeCensus(censusOptions);
   return requestOptions.asTreeNode
-    ? new CensusTreeNode(censusOptions.breakdown, report)
+    ? censusReportToCensusTreeNode(censusOptions.breakdown, report)
     : report;
 });
 
 /**
  * @see HeapAnalysesClient.prototype.takeCensusDiff
  */
 workerHelper.createTask(self, "takeCensusDiff", request => {
   const {
@@ -61,11 +61,11 @@ workerHelper.createTask(self, "takeCensu
     throw new Error(`No known heap snapshot for '${secondSnapshotFilePath}'`);
   }
 
   const first = snapshots[firstSnapshotFilePath].takeCensus(censusOptions);
   const second = snapshots[secondSnapshotFilePath].takeCensus(censusOptions);
   const delta = CensusUtils.diff(censusOptions.breakdown, first, second);
 
   return requestOptions.asTreeNode
-    ? new CensusTreeNode(censusOptions.breakdown, delta)
+    ? censusReportToCensusTreeNode(censusOptions.breakdown, delta)
     : delta;
 });
--- a/devtools/shared/heapsnapshot/census-tree-node.js
+++ b/devtools/shared/heapsnapshot/census-tree-node.js
@@ -1,18 +1,519 @@
 /* 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";
 
+// CensusTreeNode is an intermediate representation of a census report that
+// exists between after a report is generated by taking a census and before the
+// report is rendered in the DOM. It must be dead simple to render, with no
+// further data processing or massaging needed before rendering DOM nodes. Our
+// goal is to do the census report to CensusTreeNode transformation in the
+// HeapAnalysesWorker, and ensure that the **only** work that the main thread
+// has to do is strictly DOM rendering work.
+
+const { Visitor, walk } = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
+
 /**
- * Utilities for interfacing with census reports from dbg.memory.takeCensus().
+ * Return true if the given object is a SavedFrame stack object, false otherwise.
+ *
+ * @param {any} obj
+ * @returns {Boolean}
+ */
+function isSavedFrame(obj) {
+  return Object.prototype.toString.call(obj) === "[object SavedFrame]";
+}
+
+/**
+ * A CensusTreeNodeCache maps from SavedFrames to CensusTreeNodes. It is used when
+ * aggregating multiple SavedFrame allocation stack keys into a tree of many
+ * CensusTreeNodes. Each stack may share older frames, and we want to preserve
+ * this sharing when converting to CensusTreeNode, so before creating a new
+ * CensusTreeNode, we look for an existing one in one of our CensusTreeNodeCaches.
+ */
+function CensusTreeNodeCache() {}
+CensusTreeNodeCache.prototype = null;
+
+/**
+ * The value of a single entry stored in a CensusTreeNodeCache. It is a pair of
+ * the CensusTreeNode for this cache value, and the subsequent
+ * CensusTreeNodeCache for this node's children.
+ *
+ * @param {SavedFrame} frame
+ *        The frame being cached.
+ */
+function CensusTreeNodeCacheValue() {
+  // The CensusTreeNode for this cache value.
+  this.node = undefined;
+  // The CensusTreeNodeCache for this frame's children.
+  this.children = undefined;
+}
+
+CensusTreeNodeCacheValue.prototype = null;
+
+/**
+ * Create a unique string for the given SavedFrame (ignoring the frame's parent
+ * chain) that can be used as a hash to key this frame within a CensusTreeNodeCache.
+ *
+ * NB: We manually hash rather than using an ES6 Map because we are purposely
+ * ignoring the parent chain and wish to consider frames with everything the
+ * same except their parents as the same.
+ *
+ * @param {SavedFrame} frame
+ *        The SavedFrame object we would like to lookup in or insert into a
+ *        CensusTreeNodeCache.
+ *
+ * @returns {String}
+ *          The unique string that can be used as a key in a CensusTreeNodeCache.
+ */
+CensusTreeNodeCache.hashFrame = function (frame) {
+  return `FRAME,${frame.functionDisplayName},${frame.source},${frame.line},${frame.column},${frame.asyncCause}`;
+};
+
+/**
+ * Create a unique string for the given CensusTreeNode **with regards to
+ * siblings at the current depth of the tree, not within the whole tree.** It
+ * can be used as a hash to key this node within a CensusTreeNodeCache.
+ *
+ * @param {CensusTreeNode} node
+ *        The node we would like to lookup in or insert into a cache.
+ *
+ * @returns {String}
+ *          The unique string that can be used as a key in a CensusTreeNodeCache.
+ */
+CensusTreeNodeCache.hashNode = function (node) {
+  return isSavedFrame(node.name)
+    ? CensusTreeNodeCache.hashFrame(node.name)
+    : `NODE,${node.name}`;
+};
+
+/**
+ * Insert the given CensusTreeNodeCacheValue whose node.name is a SavedFrame
+ * object in the given cache.
+ *
+ * @param {CensusTreeNodeCache} cache
+ * @param {CensusTreeNodeCacheValue} value
+ */
+CensusTreeNodeCache.insertFrame = function (cache, value) {
+  cache[CensusTreeNodeCache.hashFrame(value.node.name)] = value;
+};
+
+/**
+ * Insert the given value in the cache.
+ *
+ * @param {CensusTreeNodeCache} cache
+ * @param {CensusTreeNodeCacheValue} value
+ */
+CensusTreeNodeCache.insertNode = function (cache, value) {
+  if (isSavedFrame(value.node.name)) {
+    CensusTreeNodeCache.insertFrame(cache, value);
+  } else {
+    cache[CensusTreeNodeCache.hashNode(value.node)] = value;
+  }
+};
+
+/**
+ * Lookup `frame` in `cache` and return its value if it exists.
+ *
+ * @param {CensusTreeNodeCache} cache
+ * @param {SavedFrame} frame
+ *
+ * @returns {undefined|CensusTreeNodeCacheValue}
+ */
+CensusTreeNodeCache.lookupFrame = function (cache, frame) {
+  return cache[CensusTreeNodeCache.hashFrame(frame)];
+};
+
+/**
+ * Lookup `node` in `cache` and return its value if it exists.
+ *
+ * @param {CensusTreeNodeCache} cache
+ * @param {CensusTreeNode} node
+ *
+ * @returns {undefined|CensusTreeNodeCacheValue}
+ */
+CensusTreeNodeCache.lookupNode = function (cache, node) {
+  return isSavedFrame(node.name)
+    ? CensusTreeNodeCache.lookupFrame(cache, node.name)
+    : cache[CensusTreeNodeCache.hashNode(node)];
+};
+
+/**
+ * Add `child` to `parent`'s set of children.
+ *
+ * @param {CensusTreeNode} parent
+ * @param {CensusTreeNode} child
+ */
+function addChild(parent, child) {
+  if (!parent.children) {
+    parent.children = [];
+  }
+  parent.children.push(child);
+}
+
+/**
+ * Get an array of each frame in the provided stack.
+ *
+ * @param {SavedFrame} stack
+ * @returns {Array<SavedFrame>}
+ */
+function getArrayOfFrames(stack) {
+  const frames = [];
+  let frame = stack;
+  while (frame) {
+    frames.push(frame);
+    frame = frame.parent;
+  }
+  frames.reverse();
+  return frames;
+}
+
+/**
+ * Given an `edge` to a sub-`report` whose structure is described by
+ * `breakdown`, create a CensusTreeNode tree.
+ *
+ * @param {Object} breakdown
+ *        The breakdown specifying the structure of the given report.
+ *
+ * @param {Object} report
+ *        The census report.
+ *
+ * @param {null|String|SavedFrame} edge
+ *        The edge leading to this report from the parent report.
+ *
+ * @param {CensusTreeNodeCache} cache
+ *        The cache of CensusTreeNodes we have already made for the siblings of
+ *        the node being created. The existing nodes are reused when possible.
+ *
+ * @param {Object} outParams
+ *        The return values are attached to this object after this function
+ *        returns. Because we create a CensusTreeNode for each frame in a
+ *        SavedFrame stack edge, there may multiple nodes per sub-report.
+ *
+ *          - top: The deepest node in the CensusTreeNode subtree created.
+ *
+ *          - bottom: The shallowest node in the CensusTreeNode subtree created.
+ *                    This is null if the shallowest node in the subtree was
+ *                    found in the `cache` and reused.
+ *
+ *        Note that top and bottom are not necessarily different. In the case
+ *        where there is a 1:1 correspondence between an edge in the report and
+ *        a CensusTreeNode, top and bottom refer to the same node.
  */
+function makeCensusTreeNodeSubTree(breakdown, report, edge, cache, outParams) {
+  if (!isSavedFrame(edge)) {
+    const node = new CensusTreeNode(edge);
+    outParams.top = outParams.bottom = node;
+    return;
+  }
 
-const COARSE_TYPES = new Set(["objects", "scripts", "strings", "other"]);
+  // Loop through each frame in the stack and get or create a CensusTreeNode for
+  // the frame.
+
+  const frames = getArrayOfFrames(edge);
+  let currentCache = cache;
+  let prevNode;
+  for (let i = 0, length = frames.length; i < length; i++) {
+    const frame = frames[i];
+
+    // Get or create the CensusTreeNodeCacheValue for this frame. If we already
+    // have a CensusTreeNodeCacheValue (and hence a CensusTreeNode) for this
+    // frame, we don't need to add the node to the previous node's children as
+    // we have already done that. If we don't have a CensusTreeNodeCacheValue
+    // and CensusTreeNode for this frame, then create one and make sure to hook
+    // it up as a child of the previous node.
+    let isNewNode = false;
+    let val = CensusTreeNodeCache.lookupFrame(currentCache, frame);
+    if (!val) {
+      isNewNode = true;
+      val = new CensusTreeNodeCacheValue();
+      val.node = new CensusTreeNode(frame);
+
+      CensusTreeNodeCache.insertFrame(currentCache, val);
+      if (prevNode) {
+        addChild(prevNode, val.node);
+      }
+    }
+
+    if (i === 0) {
+      outParams.bottom = isNewNode ? val.node : null;
+    }
+    if (i === length - 1) {
+      outParams.top = val.node;
+    }
+
+    prevNode = val.node;
+
+    if (i !== length - 1 && !val.children) {
+      // This is not the last frame and therefore this node will have
+      // children, which we must cache.
+      val.children = new CensusTreeNodeCache();
+    }
+
+    currentCache = val.children;
+  }
+}
+
+/**
+ * A Visitor that walks a census report and creates the corresponding
+ * CensusTreeNode tree.
+ */
+function CensusTreeNodeVisitor() {
+  // The root of the resulting CensusTreeNode tree.
+  this._root = null;
+
+  // The stack of CensusTreeNodes that we are in the process of building while
+  // walking the census report.
+  this._nodeStack = [];
+
+  // To avoid unnecessary allocations, we reuse the same out parameter object
+  // passed to `makeCensusTreeNodeSubTree` every time we call it.
+  this._outParams = {
+    top: null,
+    bottom: null,
+  };
+
+  // The stack of `CensusTreeNodeCache`s that we use to aggregate many SavedFrame stacks
+  // into a single CensusTreeNode tree.
+  this._cacheStack = [new CensusTreeNodeCache()];
+}
+
+CensusTreeNodeVisitor.prototype = Object.create(Visitor);
+
+/**
+ * Create the CensusTreeNode subtree for this sub-report and link it to the
+ * parent CensusTreeNode.
+ *
+ * @overrides Visitor.prototype.enter
+ */
+CensusTreeNodeVisitor.prototype.enter = function (breakdown, report, edge) {
+  const cache = this._cacheStack[this._cacheStack.length - 1];
+  makeCensusTreeNodeSubTree(breakdown, report, edge, cache, this._outParams);
+  const { top, bottom } = this._outParams;
+
+  if (!this._root) {
+    this._root = bottom;
+  } else {
+    if (bottom) {
+      addChild(this._nodeStack[this._nodeStack.length - 1], bottom);
+    }
+  }
+
+  this._cacheStack.push(new CensusTreeNodeCache);
+  this._nodeStack.push(top);
+};
+
+function values(cache) {
+  return Object.keys(cache).map(k => cache[k]);
+}
+
+/**
+ * We have finished adding children to the CensusTreeNode subtree for the
+ * current sub-report. Make sure that the children are sorted for every node in
+ * the subtree.
+ *
+ * @overrides Visitor.prototype.exit
+ */
+CensusTreeNodeVisitor.prototype.exit = function (breakdown, report, edge) {
+  // Ensure all children are sorted and have their counts/bytes aggregated. We
+  // only need to consider cache children here, because other children
+  // correspond to other sub-reports and we already fixed them up in an earlier
+  // invocation of `exit`.
+
+  function dfs(node, childrenCache) {
+    if (childrenCache) {
+      const childValues = values(childrenCache);
+      for (let i = 0, length = childValues.length; i < length; i++) {
+        dfs(childValues[i].node, childValues[i].children);
+      }
+    }
+
+    node.totalCount = node.count;
+    node.totalBytes = node.bytes;
+
+    if (node.children) {
+      node.children.sort(compareByTotal);
+
+      for (let i = 0, length = node.children.length; i < length; i++) {
+        node.totalCount += node.children[i].totalCount;
+        node.totalBytes += node.children[i].totalBytes;
+      }
+    }
+  }
+
+  const top = this._nodeStack.pop();
+  const cache = this._cacheStack.pop();
+  dfs(top, cache);
+};
+
+/**
+ * @overrides Visitor.prototype.count
+ */
+CensusTreeNodeVisitor.prototype.count = function (breakdown, report, edge) {
+  const node = this._nodeStack[this._nodeStack.length - 1];
+
+  if (breakdown.count) {
+    node.count = report.count;
+  }
+
+  if (breakdown.bytes) {
+    node.bytes = report.bytes;
+  }
+};
+
+/**
+ * Get the root of the resulting CensusTreeNode tree.
+ *
+ * @returns {CensusTreeNode}
+ */
+CensusTreeNodeVisitor.prototype.root = function () {
+  if (!this._root) {
+    throw new Error("Attempt to get the root before walking the census report!");
+  }
+
+  if (this._nodeStack.length) {
+    throw new Error("Attempt to get the root while walking the census report!");
+  }
+
+  return this._root;
+};
+
+/**
+ * Create a single, uninitialized CensusTreeNode.
+ *
+ * @param {null|String|SavedFrame} name
+ */
+function CensusTreeNode (name) {
+  this.name = name;
+  this.bytes = 0;
+  this.totalBytes = 0;
+  this.count = 0;
+  this.totalCount = 0;
+  this.children = undefined;
+}
+
+CensusTreeNode.prototype = null;
+
+/**
+ * Compare the given nodes by their `totalBytes` properties, and breaking ties
+ * with the `totalCount`, `bytes`, and `count` properties (in that order).
+ *
+ * @param {CensusTreeNode} node1
+ * @param {CensusTreeNode} node2
+ *
+ * @returns {Number}
+ *          A number suitable for using with Array.prototype.sort.
+ */
+function compareByTotal(node1, node2) {
+  return node2.totalBytes - node1.totalBytes
+      || node2.totalCount - node1.totalCount
+      || node2.bytes      - node1.bytes
+      || node2.count      - node1.count;
+}
+
+/**
+ * Compare the given nodes by their `bytes` properties, and breaking ties with
+ * the `count`, `totalBytes`, and `totalCount` properties (in that order).
+ *
+ * @param {CensusTreeNode} node1
+ * @param {CensusTreeNode} node2
+ *
+ * @returns {Number}
+ *          A number suitable for using with Array.prototype.sort.
+ */
+function compareBySelf(node1, node2) {
+  return node2.bytes      - node1.bytes
+      || node2.count      - node1.count
+      || node2.totalBytes - node1.totalBytes
+      || node2.totalCount - node1.totalCount;
+}
+
+/**
+ * Given an un-inverted CensusTreeNode tree, return the corresponding inverted
+ * CensusTreeNode tree. The input tree is not modified. The resulting inverted
+ * tree is sorted by self bytes rather than by total bytes.
+ *
+ * @param {CensusTreeNode} tree
+ *        The un-inverted tree.
+ *
+ * @returns {CensusTreeNode}
+ *          The corresponding inverted tree.
+ */
+function invert(tree) {
+  const inverted = new CensusTreeNodeCacheValue();
+  inverted.node = new CensusTreeNode(null);
+
+  // Do a depth-first search of the un-inverted tree. As we reach each leaf,
+  // take the path from the old root to the leaf, reverse that path, and add it
+  // to the new, inverted tree's root.
+
+  const path = [];
+  (function addInvertedPaths(node) {
+    path.push(node);
+
+    if (node.children) {
+      for (let i = 0, length = node.children.length; i < length; i++) {
+        addInvertedPaths(node.children[i]);
+      }
+    } else {
+      // We found a leaf node, add the reverse path to the inverted tree.
+
+      let current = inverted;
+      for (let i = path.length - 1; i >= 0; i--) {
+        const node = path[i];
+
+        if (!current.children) {
+          current.children = new CensusTreeNodeCache();
+        }
+
+        // If we already have a corresponding node in the inverted tree, merge
+        // this node's counts with it. Otherwise, create the corresponding node
+        // in the inverted tree, add it to the parent's children cache, and
+        // create the parent->child edge.
+        let val = CensusTreeNodeCache.lookupNode(current.children, node);
+        if (val) {
+          val.node.count += node.count;
+          val.node.totalCount += node.totalCount;
+          val.node.bytes += node.bytes;
+          val.node.totalBytes += node.totalBytes;
+        } else {
+          val = new CensusTreeNodeCacheValue();
+
+          val.node = new CensusTreeNode(node.name);
+          val.node.count = node.count;
+          val.node.totalCount = node.totalCount;
+          val.node.bytes = node.bytes;
+          val.node.totalBytes = node.totalBytes;
+
+          addChild(current.node, val.node);
+          CensusTreeNodeCache.insertNode(current.children, val);
+        }
+
+        current = val;
+      }
+    }
+
+    path.pop();
+  }(tree));
+
+  // Next, do a depth-first search of the inverted tree and ensure that siblings
+  // are sorted by their self bytes/count.
+
+  (function ensureSorted(node) {
+    if (node.children) {
+      node.children.sort(compareBySelf);
+      for (let i = 0, length = node.children.length; i < length; i++) {
+        ensureSorted(node.children[i]);
+      }
+    }
+  }(inverted.node));
+
+  return inverted.node;
+}
 
 /**
  * Takes a report from a census (`dbg.memory.takeCensus()`) and the breakdown
  * used to generate the census and returns a structure used to render
  * a tree to display the data.
  *
  * Returns a recursive "CensusTreeNode" object, looking like:
  *
@@ -20,72 +521,27 @@ const COARSE_TYPES = new Set(["objects",
  *   // `children` if it exists, is sorted by `bytes`, if they are leaf nodes.
  *   children: ?[<CensusTreeNode...>],
  *   name: <?String>
  *   count: <?Number>
  *   bytes: <?Number>
  * }
  *
  * @param {Object} breakdown
+ *        The breakdown used to generate the census report.
+ *
  * @param {Object} report
- * @param {?String} name
- * @return {Object}
+ *        The census report generated with the specified breakdown.
+ *
+ * @param {Object} options
+ *        Configuration options.
+ *          - invert: Whether to invert the resulting tree or not. Defaults to
+ *                    false, ie uninverted.
+ *
+ * @returns {CensusTreeNode}
  */
-function CensusTreeNode (breakdown, report, name) {
-  this.name = name;
-  this.bytes = void 0;
-  this.count = void 0;
-  this.children = void 0;
-
-  CensusTreeNodeBreakdowns[breakdown.by](this, breakdown, report);
-
-  if (this.children) {
-    this.children.sort(sortByBytes);
-  }
-}
-
-CensusTreeNode.prototype = null;
-
-/**
- * A series of functions to handle different breakdowns used by CensusTreeNode
- */
-const CensusTreeNodeBreakdowns = Object.create(null);
-
-CensusTreeNodeBreakdowns.count = function (node, breakdown, report) {
-  if (breakdown.bytes === true) {
-    node.bytes = report.bytes;
-  }
-  if (breakdown.count === true) {
-    node.count = report.count;
-  }
+exports.censusReportToCensusTreeNode = function (breakdown, report,
+                                                 options = { invert: false }) {
+  const visitor = new CensusTreeNodeVisitor();
+  walk(breakdown, report, visitor);
+  const root = visitor.root();
+  return options.invert ? invert(root) : root;
 };
-
-CensusTreeNodeBreakdowns.internalType = function (node, breakdown, report) {
-  node.children = [];
-  for (let key of Object.keys(report)) {
-    node.children.push(new CensusTreeNode(breakdown.then, report[key], key));
-  }
-};
-
-CensusTreeNodeBreakdowns.objectClass = function (node, breakdown, report) {
-  node.children = [];
-  for (let key of Object.keys(report)) {
-    let bd = key === "other" ? breakdown.other : breakdown.then;
-    node.children.push(new CensusTreeNode(bd, report[key], key));
-  }
-};
-
-CensusTreeNodeBreakdowns.coarseType = function (node, breakdown, report) {
-  node.children = [];
-  for (let type of Object.keys(breakdown).filter(type => COARSE_TYPES.has(type))) {
-    node.children.push(new CensusTreeNode(breakdown[type], report[type], type));
-  }
-};
-
-CensusTreeNodeBreakdowns.allocationStack = function (node, breakdown, report) {
-  node.children = [];
-};
-
-function sortByBytes (a, b) {
-  return (b.bytes || 0) - (a.bytes || 0);
-}
-
-exports.CensusTreeNode = CensusTreeNode;
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
@@ -15,17 +15,17 @@ const { Census } = Cu.import("resource:/
 const { addDebuggerToGlobal } =
   Cu.import("resource://gre/modules/jsdebugger.jsm", {});
 const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const HeapAnalysesClient =
   require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 const Services = require("Services");
-const { CensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
+const { censusReportToCensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
 const CensusUtils = require("devtools/shared/heapsnapshot/CensusUtils");
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
 if (Services.appInfo &&
     Services.appInfo.processType == Services.appInfo.PROCESS_TYPE_DEFAULT) {
   Services.prefs.setBoolPref("devtools.debugger.log", true);
 }
@@ -145,40 +145,64 @@ function saveHeapSnapshotAndTakeCensus(d
   const snapshot = ChromeUtils.readHeapSnapshot(filePath);
   ok(snapshot, "Should have read a heap snapshot back from " + filePath);
   ok(snapshot instanceof HeapSnapshot, "snapshot should be an instance of HeapSnapshot");
 
   equal(typeof snapshot.takeCensus, "function", "snapshot should have a takeCensus method");
   return snapshot.takeCensus(censusOptions);
 }
 
+function isSavedFrame(obj) {
+  return Object.prototype.toString.call(obj) === "[object SavedFrame]";
+}
+
+function savedFrameReplacer(key, val) {
+  if (isSavedFrame(val)) {
+    return `<SavedFrame '${val.toString().split(/\n/g).shift()}'>`;
+  } else {
+    return val;
+  }
+}
+
 /**
  * Assert that creating a CensusTreeNode from the given `report` with the
  * specified `breakdown` creates the given `expected` CensusTreeNode.
  *
  * @param {Object} breakdown
  *        The census breakdown.
  *
  * @param {Object} report
  *        The census report.
  *
  * @param {Object} expected
  *        The expected CensusTreeNode result.
  *
- * @param {String} assertion
- *        The assertion message.
+ * @param {Object} options
+ *        The options to pass through to `censusReportToCensusTreeNode`.
  */
-function compareCensusViewData (breakdown, report, expected, assertion) {
-  let data = new CensusTreeNode(breakdown, report);
-  equal(JSON.stringify(data), JSON.stringify(expected), assertion);
+function compareCensusViewData (breakdown, report, expected, options) {
+  dumpn("Generating CensusTreeNode from report:");
+  dumpn("breakdown: " + JSON.stringify(breakdown, null, 4));
+  dumpn("report: " + JSON.stringify(report, null, 4));
+  dumpn("expected: " + JSON.stringify(expected, savedFrameReplacer, 4));
+
+  const actual = censusReportToCensusTreeNode(breakdown, report, options);
+  dumpn("actual: " + JSON.stringify(actual, savedFrameReplacer, 4));
+
+  assertStructurallyEquivalent(actual, expected);
 }
 
 // Deep structural equivalence that can handle Map objects in addition to plain
 // objects.
 function assertStructurallyEquivalent(actual, expected, path="root") {
+  if (actual === expected) {
+    equal(actual, expected, "actual and expected are the same");
+    return;
+  }
+
   equal(typeof actual, typeof expected, `${path}: typeof should be the same`);
 
   if (actual && typeof actual === "object") {
     const actualProtoString = Object.prototype.toString.call(actual);
     const expectedProtoString = Object.prototype.toString.call(expected);
     equal(actualProtoString, expectedProtoString,
           `${path}: Object.prototype.toString.call() should be the same`);
 
@@ -190,30 +214,30 @@ function assertStructurallyEquivalent(ac
            `${path}: every key in actual should exist in expected: ${String(key).slice(0, 10)}`);
         expectedKeys.delete(key);
 
         assertStructurallyEquivalent(actual.get(key), expected.get(key),
                                      path + ".get(" + String(key).slice(0, 20) + ")");
       }
 
       equal(expectedKeys.size, 0,
-            `${path}: every key in expected should also exist in actual`);
+            `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`);
     } else {
       const expectedKeys = new Set(Object.keys(expected));
 
       for (let key of Object.keys(actual)) {
         ok(expectedKeys.has(key),
            `${path}: every key in actual should exist in expected: ${key}`);
         expectedKeys.delete(key);
 
         assertStructurallyEquivalent(actual[key], expected[key], path + "." + key);
       }
 
       equal(expectedKeys.size, 0,
-            `${path}: every key in expected should also exist in actual`);
+            `${path}: every key in expected should also exist in actual, did not see ${[...expectedKeys]}`);
     }
   } else {
     equal(actual, expected, `${path}: primitives should be equal`);
   }
 }
 
 /**
  * Assert that creating a diff of the `first` and `second` census reports
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_takeCensus_06.js
@@ -0,0 +1,109 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the HeapAnalyses{Client,Worker} can take censuses by
+// "allocationStack" and return a CensusTreeNode.
+
+function run_test() {
+  run_next_test();
+}
+
+const BREAKDOWN = {
+  by: "objectClass",
+  then: {
+    by: "allocationStack",
+    then: { by: "count", count: true, bytes: true },
+    noStack: { by: "count", count: true, bytes: true }
+  },
+  other: { by: "count", count: true, bytes: true }
+};
+
+add_task(function* () {
+  const g = newGlobal();
+  const dbg = new Debugger(g);
+
+  // 5 allocation markers with no stack.
+  g.eval(`
+         this.markers = [];
+         for (var i = 0; i < 5; i++) {
+           markers.push(allocationMarker());
+         }
+         `);
+
+  dbg.memory.allocationSamplingProbability = 1;
+  dbg.memory.trackingAllocationSites = true;
+
+  // 5 allocation markers at 5 stacks.
+  g.eval(`
+         (function shouldHaveCountOfOne() {
+           markers.push(allocationMarker());
+           markers.push(allocationMarker());
+           markers.push(allocationMarker());
+           markers.push(allocationMarker());
+           markers.push(allocationMarker());
+         }());
+         `);
+
+  // 5 allocation markers at 1 stack.
+  g.eval(`
+         (function shouldHaveCountOfFive() {
+           for (var i = 0; i < 5; i++) {
+             markers.push(allocationMarker());
+           }
+         }());
+         `);
+
+  const snapshotFilePath = saveNewHeapSnapshot({ debugger: dbg });
+
+  const client = new HeapAnalysesClient();
+  yield client.readHeapSnapshot(snapshotFilePath);
+  ok(true, "Should have read the heap snapshot");
+
+  const report = yield client.takeCensus(snapshotFilePath, {
+    breakdown: BREAKDOWN
+  });
+
+  const treeNode = yield client.takeCensus(snapshotFilePath, {
+    breakdown: BREAKDOWN
+  }, {
+    asTreeNode: true
+  });
+
+  const markers = treeNode.children.find(c => c.name === "AllocationMarker");
+  ok(markers);
+
+  const noStack = markers.children.find(c => c.name === "noStack");
+  equal(noStack.count, 5);
+
+  let numShouldHaveFiveFound = 0;
+  let numShouldHaveOneFound = 0;
+
+  function walk(node) {
+    if (node.children) {
+      node.children.forEach(walk);
+    }
+
+    if (!isSavedFrame(node.name)) {
+      return;
+    }
+
+    if (node.name.functionDisplayName === "shouldHaveCountOfFive") {
+      equal(node.count, 5, "shouldHaveCountOfFive should have count of five");
+      numShouldHaveFiveFound++;
+    }
+
+    if (node.name.functionDisplayName === "shouldHaveCountOfOne") {
+      equal(node.count, 1, "shouldHaveCountOfOne should have count of one");
+      numShouldHaveOneFound++;
+    }
+  }
+  markers.children.forEach(walk);
+
+  equal(numShouldHaveFiveFound, 1);
+  equal(numShouldHaveOneFound, 5);
+
+  compareCensusViewData(BREAKDOWN, report, treeNode,
+    "Returning census as a tree node represents same data as the report");
+
+  client.destroy();
+});
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-01.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-01.js
@@ -1,17 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests CensusTreeNode with `internalType` breakdown.
  */
-function run_test() {
-  compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, `${JSON.stringify(BREAKDOWN)} has correct results.`);
-}
 
 const BREAKDOWN = {
   by: "internalType",
   then: { by: "count", count: true, bytes: true }
 };
 
 const REPORT = {
   "JSObject": {
@@ -24,14 +21,44 @@ const REPORT = {
   },
   "JSString": {
     "bytes": 0,
     "count": 0,
   },
 };
 
 const EXPECTED = {
+  name: null,
+  bytes: 0,
+  totalBytes: 600,
+  count: 0,
+  totalCount: 60,
   children: [
-    { name: "js::Shape", bytes: 500, count: 50, },
-    { name: "JSObject", bytes: 100, count: 10, },
-    { name: "JSString", bytes: 0, count: 0, },
+    {
+      name: "js::Shape",
+      bytes: 500,
+      totalBytes: 500,
+      count: 50,
+      totalCount: 50,
+      children: undefined
+    },
+    {
+      name: "JSObject",
+      bytes: 100,
+      totalBytes: 100,
+      count: 10,
+      totalCount: 10,
+      children: undefined
+    },
+    {
+      name: "JSString",
+      bytes: 0,
+      totalBytes: 0,
+      count: 0,
+      totalCount: 0,
+      children: undefined
+    },
   ],
 };
+
+function run_test() {
+  compareCensusViewData(BREAKDOWN, REPORT, EXPECTED);
+}
--- a/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js
+++ b/devtools/shared/heapsnapshot/tests/unit/test_census-tree-node-02.js
@@ -1,45 +1,109 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests CensusTreeNode with `coarseType` breakdown.
  */
 
-function run_test() {
-  compareCensusViewData(BREAKDOWN, REPORT, EXPECTED, `${JSON.stringify(BREAKDOWN)} has correct results.`);
-}
-
 const countBreakdown = { by: "count", count: true, bytes: true };
 
 const BREAKDOWN = {
   by: "coarseType",
   objects: { by: "objectClass", then: countBreakdown },
   strings: countBreakdown,
+  scripts: countBreakdown,
   other: { by: "internalType", then: countBreakdown },
 };
 
 const REPORT = {
   "objects": {
     "Function": { bytes: 10, count: 1 },
     "Array": { bytes: 20, count: 2 },
   },
   "strings": { bytes: 10, count: 1 },
+  "scripts": { bytes: 1, count: 1 },
   "other": {
     "js::Shape": { bytes: 30, count: 3 },
     "js::Shape2": { bytes: 40, count: 4 }
   },
 };
 
 const EXPECTED = {
+  name: null,
+  bytes: 0,
+  totalBytes: 111,
+  count: 0,
+  totalCount: 12,
   children: [
-    { name: "strings", bytes: 10, count: 1, },
-    { name: "objects", children: [
-      { name: "Array", bytes: 20, count: 2, },
-      { name: "Function", bytes: 10, count: 1, },
-    ]},
-    { name: "other", children: [
-      { name: "js::Shape2", bytes: 40, count: 4, },
-      { name: "js::Shape", bytes: 30, count: 3, },
-    ]},
+    {
+      name: "other",
+      count: 0,
+      totalCount: 7,
+      bytes: 0,
+      totalBytes: 70,
+      children: [
+        {
+          name: "js::Shape2",
+          bytes: 40,
+          totalBytes: 40,
+          count: 4,
+          totalCount: 4,
+          children: undefined
+        },
+        {
+          name: "js::Shape",
+          bytes: 30,
+          totalBytes: 30,
+          count: 3,
+          totalCount: 3,
+          children: undefined
+        },
+      ]
+    },
+    {
+      name: "objects",
+      count: 0,
+      totalCount: 3,
+      bytes: 0,
+      totalBytes: 30,
+      children: [
+        {
+          name: "Array",
+          bytes: 20,
+          totalBytes: 20,
+          count: 2,
+          totalCount: 2,
+          children: undefined
+        },
+        {
+          name: "Function",
+          bytes: 10,
+          totalBytes: 10,
+          count: 1,
+          totalCount: 1,
+          children: undefined
+        },
+      ]
+    },
+    {
+      name: "strings",
+