Merge mozilla-central to b2g-i
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 24 Jun 2015 16:08:47 +0200
changeset 280868 14f96e9b415aa5002bfb4fd41462038be2ecfbf2
parent 280867 0d0c0b80ca698306056f41c3bb06a408264463ee (current diff)
parent 280864 4cdc1a95a672aaae593448b89b1caee33a159ab7 (diff)
child 280869 d8e5b5fb69da7df8147b781a7cedea393926933d
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone41.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 mozilla-central to b2g-i
embedding/tests/winEmbed/SMALL.ICO
embedding/tests/winEmbed/WebBrowserChrome.cpp
embedding/tests/winEmbed/WebBrowserChrome.h
embedding/tests/winEmbed/WindowCreator.cpp
embedding/tests/winEmbed/WindowCreator.h
embedding/tests/winEmbed/moz.build
embedding/tests/winEmbed/resource.h
embedding/tests/winEmbed/winEmbed.ICO
embedding/tests/winEmbed/winEmbed.cpp
embedding/tests/winEmbed/winEmbed.h
embedding/tests/winEmbed/winEmbed.rc
embedding/tests/winEmbed/winembed.dsp
layout/base/nsIPercentHeightObserver.h
profile/dirserviceprovider/ProfileUnlockerWin.cpp
profile/dirserviceprovider/ProfileUnlockerWin.h
profile/dirserviceprovider/moz.build
profile/dirserviceprovider/nsProfileDirServiceProvider.cpp
profile/dirserviceprovider/nsProfileDirServiceProvider.h
profile/dirserviceprovider/nsProfileLock.cpp
profile/dirserviceprovider/nsProfileLock.h
profile/dirserviceprovider/nsProfileStringTypes.h
profile/moz.build
profile/notifications.txt
profile/nsIProfileUnlocker.idl
testing/web-platform/meta/IndexedDB/idbcursor_update_index5.htm.ini
testing/web-platform/meta/IndexedDB/idbcursor_update_objectstore6.htm.ini
testing/web-platform/meta/IndexedDB/idbfactory_open12.htm.ini
testing/web-platform/meta/IndexedDB/idbfactory_open2.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_get7.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_getKey7.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_openCursor2.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_openKeyCursor3.htm.ini
testing/web-platform/meta/IndexedDB/idbobjectstore_get.htm.ini
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -678,17 +678,17 @@ getRoleCB(AtkObject *aAtkObj)
 
 #undef ROLE
 
   if (aAtkObj->role == ATK_ROLE_LIST_BOX && !IsAtkVersionAtLeast(2, 1))
     aAtkObj->role = ATK_ROLE_LIST;
   else if (aAtkObj->role == ATK_ROLE_TABLE_ROW && !IsAtkVersionAtLeast(2, 1))
     aAtkObj->role = ATK_ROLE_LIST_ITEM;
   else if (aAtkObj->role == ATK_ROLE_MATH && !IsAtkVersionAtLeast(2, 12))
-    aAtkObj->role = ATK_ROLE_PANEL;
+    aAtkObj->role = ATK_ROLE_SECTION;
   else if (aAtkObj->role == ATK_ROLE_STATIC && !IsAtkVersionAtLeast(2, 16))
     aAtkObj->role = ATK_ROLE_TEXT;
   else if ((aAtkObj->role == ATK_ROLE_MATH_FRACTION ||
             aAtkObj->role == ATK_ROLE_MATH_ROOT) && !IsAtkVersionAtLeast(2, 16))
     aAtkObj->role = ATK_ROLE_SECTION;
 
   return aAtkObj->role;
 }
--- a/accessible/base/RoleMap.h
+++ b/accessible/base/RoleMap.h
@@ -1062,281 +1062,281 @@ ROLE(SWITCH,
      NSAccessibilityCheckBoxRole,
      ROLE_SYSTEM_CHECKBUTTON,
      IA2_ROLE_TOGGLE_BUTTON,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_MATH,
      "math",
      ATK_ROLE_MATH,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      ROLE_SYSTEM_EQUATION,
      ROLE_SYSTEM_EQUATION,
      eNoNameRule)
 
 ROLE(MATHML_IDENTIFIER,
      "mathml identifier",
      ATK_ROLE_STATIC,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_NUMBER,
      "mathml number",
      ATK_ROLE_STATIC,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_OPERATOR,
      "mathml operator",
      ATK_ROLE_STATIC,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_TEXT,
      "mathml text",
      ATK_ROLE_STATIC,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_STRING_LITERAL,
      "mathml string literal",
      ATK_ROLE_STATIC,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_GLYPH,
      "mathml glyph",
      ATK_ROLE_IMAGE,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNameFromSubtreeRule)
 
 ROLE(MATHML_ROW,
      "mathml row",
-     ATK_ROLE_PANEL,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_FRACTION,
      "mathml fraction",
      ATK_ROLE_MATH_FRACTION,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_SQUARE_ROOT,
      "mathml square root",
      ATK_ROLE_MATH_ROOT,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_ROOT,
      "mathml root",
      ATK_ROLE_MATH_ROOT,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_FENCED,
      "mathml fenced",
-     ATK_ROLE_PANEL,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_ENCLOSED,
      "mathml enclosed",
-     ATK_ROLE_PANEL,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STYLE,
      "mathml style",
-     ATK_ROLE_PANEL,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_SUB,
      "mathml sub",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_SUP,
      "mathml sup",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_SUB_SUP,
      "mathml sub sup",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_UNDER,
      "mathml under",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_OVER,
      "mathml over",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_UNDER_OVER,
      "mathml under over",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_MULTISCRIPTS,
      "mathml multiscripts",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_TABLE,
      "mathml table",
      ATK_ROLE_TABLE,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_LABELED_ROW,
      "mathml labeled row",
      ATK_ROLE_TABLE_ROW,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_TABLE_ROW,
      "mathml table row",
      ATK_ROLE_TABLE_ROW,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_CELL,
      "mathml cell",
      ATK_ROLE_TABLE_CELL,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_ACTION,
      "mathml action",
-     ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_ERROR,
      "mathml error",
-     ATK_ROLE_PANEL,
-     NSAccessibilityUnknownRole,
+     ATK_ROLE_SECTION,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK,
      "mathml stack",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_LONG_DIVISION,
      "mathml long division",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK_GROUP,
      "mathml stack group",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK_ROW,
      "mathml stack row",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK_CARRIES,
      "mathml stack carries",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK_CARRY,
      "mathml stack carry",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(MATHML_STACK_LINE,
      "mathml stack line",
      ATK_ROLE_UNKNOWN,
-     NSAccessibilityUnknownRole,
+     NSAccessibilityGroupRole,
      0,
      IA2_ROLE_UNKNOWN,
      eNoNameRule)
 
 ROLE(RADIO_GROUP,
      "grouping",
      ATK_ROLE_PANEL,
      NSAccessibilityRadioGroupRole,
--- a/accessible/mac/mozAccessible.mm
+++ b/accessible/mac/mozAccessible.mm
@@ -498,16 +498,76 @@ GetClosestInterestingAccessible(id anObj
       return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
 
     case roles::TERM:
       return @"AXTerm";
 
     case roles::DEFINITION:
       return @"AXDefinition";
 
+    case roles::MATHML_MATH:
+      return @"AXDocumentMath";
+
+    case roles::MATHML_FRACTION:
+      return @"AXMathFraction";
+
+    case roles::MATHML_FENCED:
+      // XXX This should be AXMathFence, but doing so without implementing the
+      // whole fence interface seems to make VoiceOver crash, so we present it
+      // as a row for now.
+      return @"AXMathRow";
+
+    case roles::MATHML_SUB:
+    case roles::MATHML_SUP:
+    case roles::MATHML_SUB_SUP:
+      return @"AXMathSubscriptSuperscript";
+
+    case roles::MATHML_ROW:
+      return @"AXMathRow";
+
+    case roles::MATHML_UNDER:
+    case roles::MATHML_OVER:
+    case roles::MATHML_UNDER_OVER:
+      return @"AXMathUnderOver";
+
+    case roles::MATHML_SQUARE_ROOT:
+      return @"AXMathSquareRoot";
+
+    case roles::MATHML_ROOT:
+      return @"AXMathRoot";
+
+    case roles::MATHML_TEXT:
+      return @"AXMathText";
+
+    case roles::MATHML_NUMBER:
+      return @"AXMathNumber";
+
+    case roles::MATHML_IDENTIFIER:
+      return @"AXMathIdentifier";
+
+    case roles::MATHML_TABLE:
+      return @"AXMathTable";
+
+    case roles::MATHML_TABLE_ROW:
+      return @"AXMathTableRow";
+
+    case roles::MATHML_CELL:
+      return @"AXMathTableCell";
+
+    // XXX: NSAccessibility also uses subroles AXMathSeparatorOperator and
+    // AXMathFenceOperator. We should use the NS_MATHML_OPERATOR_FENCE and
+    // NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they
+    // are only available from the MathML layout code. Hence we just fallback
+    // to subrole AXMathOperator for now.
+    case roles::MATHML_OPERATOR:
+      return @"AXMathOperator";
+
+    case roles::MATHML_MULTISCRIPTS:
+      return @"AXMathMultiscript";
+
     case roles::SWITCH:
       return @"AXSwitch";
 
     default:
       break;
   }
 
   return nil;
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -300,17 +300,16 @@
 @RESPATH@/components/parentalcontrols.xpt
 #ifdef MOZ_WEBRTC
 @RESPATH@/components/peerconnection.xpt
 #endif
 @RESPATH@/components/places.xpt
 @RESPATH@/components/plugin.xpt
 @RESPATH@/components/pref.xpt
 @RESPATH@/components/prefetch.xpt
-@RESPATH@/components/profile.xpt
 #ifdef MOZ_ENABLE_PROFILER_SPS
 @RESPATH@/components/profiler.xpt
 #endif
 @RESPATH@/components/proxyObject.xpt
 @RESPATH@/components/rdf.xpt
 @RESPATH@/components/satchel.xpt
 @RESPATH@/components/saxparser.xpt
 @RESPATH@/components/sessionstore.xpt
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -836,16 +836,20 @@ input[type=button] {
   padding: 0;
 }
 
 .newtab-intro-image-customize #newtab-customize-title {
   display: block;
   max-height: 40px;
 }
 
+.newtab-intro-image-customize #newtab-customize-panel-anchor {
+  display: none;
+}
+
 .newtab-intro-image-customize .newtab-customize-panel-item:not([selected]):hover {
   background-color: inherit;
   color: #7A7A7A;
   background: none;
 }
 
 #newtab-intro-text > p {
   margin: 0 0 1em 0;
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -470,16 +470,17 @@ skip-if = e10s # Bug 940206 - nsIWebCont
 [browser_addCertException.js]
 skip-if = e10s # Bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_bug1045809.js]
 [browser_e10s_switchbrowser.js]
 [browser_e10s_about_process.js]
 [browser_e10s_chrome_process.js]
 [browser_e10s_javascript.js]
 [browser_blockHPKP.js]
+tags = psm
 skip-if = e10s # bug 1100687 - test directly manipulates content (content.document.getElementById)
 [browser_mcb_redirect.js]
 [browser_windowactivation.js]
 [browser_contextmenu_childprocess.js]
 [browser_bug963945.js]
 [browser_readerMode.js]
 support-files =
   readerModeArticle.html
--- a/browser/components/loop/.eslintrc
+++ b/browser/components/loop/.eslintrc
@@ -58,18 +58,18 @@
     "no-use-before-define": 0,    // TODO: Remove (use default)
     "quotes": [2, "double", "avoid-escape"],
     "strict": 0,                  // [2, "function"],
     // eslint-plugin-react rules. These are documented at
     // <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
     "react/jsx-quotes": [2, "double", "avoid-escape"],
     "react/jsx-no-undef": 2,
     // Need to fix instances where this is failing.
-    "react/jsx-sort-props": 0,
-    "react/jsx-sort-prop-types": 0,
+    "react/jsx-sort-props": 2,
+    "react/jsx-sort-prop-types": 2,
     "react/jsx-uses-vars": 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-update-set-state": 2,
     "react/no-unknown-property": 2,
     // Need to fix instances where this is currently failing
     "react/prop-types": 0,
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -130,29 +130,29 @@ loop.contacts = (function(_, mozL10n) {
       return (
         React.createElement("div", {className: "contacts-gravatar-promo"}, 
           React.createElement(Button, {additionalClass: "button-close", onClick: this.handleCloseButtonClick}), 
           React.createElement("p", {dangerouslySetInnerHTML: {__html: message}, 
              onClick: this.handleLinkClick}), 
           React.createElement(ButtonGroup, null, 
             React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_nothanks"), 
                     onClick: this.handleCloseButtonClick}), 
-            React.createElement(Button, {caption: mozL10n.get("gravatars_promo_button_use"), 
-                    additionalClass: "button-accept", 
+            React.createElement(Button, {additionalClass: "button-accept", 
+                    caption: mozL10n.get("gravatars_promo_button_use"), 
                     onClick: this.handleUseButtonClick})
           )
         )
       );
     }
   });
 
   const ContactDropdown = React.createClass({displayName: "ContactDropdown",
     propTypes: {
-      handleAction: React.PropTypes.func.isRequired,
-      canEdit: React.PropTypes.bool
+      canEdit: React.PropTypes.bool,
+      handleAction: React.PropTypes.func.isRequired
     },
 
     getInitialState: function () {
       return {
         openDirUp: false
       };
     },
 
@@ -186,59 +186,63 @@ loop.contacts = (function(_, mozL10n) {
       let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
                                           : "block_contact_menu_button";
 
       return (
         React.createElement("ul", {className: cx({ "dropdown-menu": true,
                             "dropdown-menu-up": this.state.openDirUp })}, 
           React.createElement("li", {className: cx({ "dropdown-menu-item": true,
                               "disabled": this.props.blocked }), 
-              onClick: this.onItemClick, 
-              "data-action": "video-call"}, 
+              "data-action": "video-call", 
+              onClick: this.onItemClick}, 
             React.createElement("i", {className: "icon icon-video-call"}), 
             mozL10n.get("video_call_menu_button")
           ), 
           React.createElement("li", {className: cx({ "dropdown-menu-item": true,
                               "disabled": this.props.blocked }), 
-              onClick: this.onItemClick, "data-action": "audio-call"}, 
+              "data-action": "audio-call", 
+              onClick: this.onItemClick}, 
             React.createElement("i", {className: "icon icon-audio-call"}), 
             mozL10n.get("audio_call_menu_button")
           ), 
           React.createElement("li", {className: cx({ "dropdown-menu-item": true,
                               "disabled": !this.props.canEdit }), 
-              onClick: this.onItemClick, "data-action": "edit"}, 
+              "data-action": "edit", 
+              onClick: this.onItemClick}, 
             React.createElement("i", {className: "icon icon-edit"}), 
             mozL10n.get("edit_contact_menu_button")
           ), 
           React.createElement("li", {className: "dropdown-menu-item", 
-              onClick: this.onItemClick, "data-action": blockAction}, 
+              "data-action": blockAction, 
+              onClick: this.onItemClick}, 
             React.createElement("i", {className: "icon icon-" + blockAction}), 
             mozL10n.get(blockLabel)
           ), 
           React.createElement("li", {className: cx({ "dropdown-menu-item": true,
                               "disabled": !this.props.canEdit }), 
-              onClick: this.onItemClick, "data-action": "remove"}, 
+               "data-action": "remove", 
+               onClick: this.onItemClick}, 
             React.createElement("i", {className: "icon icon-remove"}), 
             mozL10n.get("remove_contact_menu_button2")
           )
         )
       );
     }
   });
 
   const ContactDetail = React.createClass({displayName: "ContactDetail",
     getInitialState: function() {
       return {
         showMenu: false
       };
     },
 
     propTypes: {
-      handleContactAction: React.PropTypes.func,
-      contact: React.PropTypes.object.isRequired
+      contact: React.PropTypes.object.isRequired,
+      handleContactAction: React.PropTypes.func
     },
 
     _onBodyClick: function() {
       // Hide the menu after other click handlers have been invoked.
       setTimeout(this.hideDropdownMenu, 10);
     },
 
     showDropdownMenu: function() {
@@ -307,19 +311,19 @@ loop.contacts = (function(_, mozL10n) {
           ), 
           React.createElement("div", {className: "icons"}, 
             React.createElement("i", {className: "icon icon-video", 
                onClick: this.handleAction.bind(null, "video-call")}), 
             React.createElement("i", {className: "icon icon-caret-down", 
                onClick: this.showDropdownMenu})
           ), 
           this.state.showMenu
-            ? React.createElement(ContactDropdown, {handleAction: this.handleAction, 
+            ? React.createElement(ContactDropdown, {blocked: this.props.contact.blocked, 
                                canEdit: this.canEdit(), 
-                               blocked: this.props.contact.blocked})
+                               handleAction: this.handleAction})
             : null
           
         )
       );
     }
   });
 
   const ContactsList = React.createClass({displayName: "ContactsList",
@@ -544,18 +548,19 @@ loop.contacts = (function(_, mozL10n) {
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let cx = React.addons.classSet;
 
       let viewForItem = item => {
         return (
-          React.createElement(ContactDetail, {key: item._guid, contact: item, 
-                         handleContactAction: this.handleContactAction})
+          React.createElement(ContactDetail, {contact: item, 
+                         handleContactAction: this.handleContactAction, 
+                         key: item._guid})
         );
       };
 
       let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
       let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
@@ -712,26 +717,33 @@ loop.contacts = (function(_, mozL10n) {
       let phoneOrEmailRequired = !this.state.email && !this.state.tel;
 
       return (
         React.createElement("div", {className: "content-area contact-form"}, 
           React.createElement("header", null, this.props.mode == "add"
                    ? mozL10n.get("add_contact_button")
                    : mozL10n.get("edit_contact_title")), 
           React.createElement("label", null, mozL10n.get("edit_contact_name_label")), 
-          React.createElement("input", {ref: "name", required: true, pattern: "\\s*\\S.*", type: "text", 
-                 className: cx({pristine: this.state.pristine}), 
+          React.createElement("input", {className: cx({pristine: this.state.pristine}), 
+                 pattern: "\\s*\\S.*", 
+                 ref: "name", 
+                 required: true, 
+                 type: "text", 
                  valueLink: this.linkState("name")}), 
           React.createElement("label", null, mozL10n.get("edit_contact_email_label")), 
-          React.createElement("input", {ref: "email", type: "email", required: phoneOrEmailRequired, 
-                 className: cx({pristine: this.state.pristine}), 
+          React.createElement("input", {className: cx({pristine: this.state.pristine}), 
+                 ref: "email", 
+                 required: phoneOrEmailRequired, 
+                 type: "email", 
                  valueLink: this.linkState("email")}), 
           React.createElement("label", null, mozL10n.get("new_contact_fxos_phone_placeholder")), 
-          React.createElement("input", {ref: "tel", type: "tel", required: phoneOrEmailRequired, 
-                 className: cx({pristine: this.state.pristine}), 
+          React.createElement("input", {className: cx({pristine: this.state.pristine}), 
+                 ref: "tel", 
+                 required: phoneOrEmailRequired, 
+                 type: "tel", 
                  valueLink: this.linkState("tel")}), 
           React.createElement(ButtonGroup, null, 
             React.createElement(Button, {additionalClass: "button-cancel", 
                     caption: mozL10n.get("cancel_button"), 
                     onClick: this.handleCancelButtonClick}), 
             React.createElement(Button, {additionalClass: "button-accept", 
                     caption: this.props.mode == "add"
                              ? mozL10n.get("add_contact_button")
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -130,29 +130,29 @@ loop.contacts = (function(_, mozL10n) {
       return (
         <div className="contacts-gravatar-promo">
           <Button additionalClass="button-close" onClick={this.handleCloseButtonClick}/>
           <p dangerouslySetInnerHTML={{__html: message}}
              onClick={this.handleLinkClick}></p>
           <ButtonGroup>
             <Button caption={mozL10n.get("gravatars_promo_button_nothanks")}
                     onClick={this.handleCloseButtonClick}/>
-            <Button caption={mozL10n.get("gravatars_promo_button_use")}
-                    additionalClass="button-accept"
+            <Button additionalClass="button-accept"
+                    caption={mozL10n.get("gravatars_promo_button_use")}
                     onClick={this.handleUseButtonClick}/>
           </ButtonGroup>
         </div>
       );
     }
   });
 
   const ContactDropdown = React.createClass({
     propTypes: {
-      handleAction: React.PropTypes.func.isRequired,
-      canEdit: React.PropTypes.bool
+      canEdit: React.PropTypes.bool,
+      handleAction: React.PropTypes.func.isRequired
     },
 
     getInitialState: function () {
       return {
         openDirUp: false
       };
     },
 
@@ -186,59 +186,63 @@ loop.contacts = (function(_, mozL10n) {
       let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
                                           : "block_contact_menu_button";
 
       return (
         <ul className={cx({ "dropdown-menu": true,
                             "dropdown-menu-up": this.state.openDirUp })}>
           <li className={cx({ "dropdown-menu-item": true,
                               "disabled": this.props.blocked })}
-              onClick={this.onItemClick}
-              data-action="video-call">
+              data-action="video-call"
+              onClick={this.onItemClick}>
             <i className="icon icon-video-call" />
             {mozL10n.get("video_call_menu_button")}
           </li>
           <li className={cx({ "dropdown-menu-item": true,
                               "disabled": this.props.blocked })}
-              onClick={this.onItemClick} data-action="audio-call">
+              data-action="audio-call"
+              onClick={this.onItemClick}>
             <i className="icon icon-audio-call" />
             {mozL10n.get("audio_call_menu_button")}
           </li>
           <li className={cx({ "dropdown-menu-item": true,
                               "disabled": !this.props.canEdit })}
-              onClick={this.onItemClick} data-action="edit">
+              data-action="edit"
+              onClick={this.onItemClick}>
             <i className="icon icon-edit" />
             {mozL10n.get("edit_contact_menu_button")}
           </li>
           <li className="dropdown-menu-item"
-              onClick={this.onItemClick} data-action={blockAction}>
+              data-action={blockAction}
+              onClick={this.onItemClick}>
             <i className={"icon icon-" + blockAction} />
             {mozL10n.get(blockLabel)}
           </li>
           <li className={cx({ "dropdown-menu-item": true,
                               "disabled": !this.props.canEdit })}
-              onClick={this.onItemClick} data-action="remove">
+               data-action="remove"
+               onClick={this.onItemClick}>
             <i className="icon icon-remove" />
             {mozL10n.get("remove_contact_menu_button2")}
           </li>
         </ul>
       );
     }
   });
 
   const ContactDetail = React.createClass({
     getInitialState: function() {
       return {
         showMenu: false
       };
     },
 
     propTypes: {
-      handleContactAction: React.PropTypes.func,
-      contact: React.PropTypes.object.isRequired
+      contact: React.PropTypes.object.isRequired,
+      handleContactAction: React.PropTypes.func
     },
 
     _onBodyClick: function() {
       // Hide the menu after other click handlers have been invoked.
       setTimeout(this.hideDropdownMenu, 10);
     },
 
     showDropdownMenu: function() {
@@ -307,19 +311,19 @@ loop.contacts = (function(_, mozL10n) {
           </div>
           <div className="icons">
             <i className="icon icon-video"
                onClick={this.handleAction.bind(null, "video-call")} />
             <i className="icon icon-caret-down"
                onClick={this.showDropdownMenu} />
           </div>
           {this.state.showMenu
-            ? <ContactDropdown handleAction={this.handleAction}
+            ? <ContactDropdown blocked={this.props.contact.blocked}
                                canEdit={this.canEdit()}
-                               blocked={this.props.contact.blocked} />
+                               handleAction={this.handleAction} />
             : null
           }
         </li>
       );
     }
   });
 
   const ContactsList = React.createClass({
@@ -544,18 +548,19 @@ loop.contacts = (function(_, mozL10n) {
       return contact1._guid - contact2._guid;
     },
 
     render: function() {
       let cx = React.addons.classSet;
 
       let viewForItem = item => {
         return (
-          <ContactDetail key={item._guid} contact={item}
-                         handleContactAction={this.handleContactAction} />
+          <ContactDetail contact={item}
+                         handleContactAction={this.handleContactAction}
+                         key={item._guid} />
         );
       };
 
       let shownContacts = _.groupBy(this.contacts, function(contact) {
         return contact.blocked ? "blocked" : "available";
       });
 
       let showFilter = Object.getOwnPropertyNames(this.contacts).length >=
@@ -712,26 +717,33 @@ loop.contacts = (function(_, mozL10n) {
       let phoneOrEmailRequired = !this.state.email && !this.state.tel;
 
       return (
         <div className="content-area contact-form">
           <header>{this.props.mode == "add"
                    ? mozL10n.get("add_contact_button")
                    : mozL10n.get("edit_contact_title")}</header>
           <label>{mozL10n.get("edit_contact_name_label")}</label>
-          <input ref="name" required pattern="\s*\S.*" type="text"
-                 className={cx({pristine: this.state.pristine})}
+          <input className={cx({pristine: this.state.pristine})}
+                 pattern="\s*\S.*"
+                 ref="name"
+                 required
+                 type="text"
                  valueLink={this.linkState("name")} />
           <label>{mozL10n.get("edit_contact_email_label")}</label>
-          <input ref="email" type="email" required={phoneOrEmailRequired}
-                 className={cx({pristine: this.state.pristine})}
+          <input className={cx({pristine: this.state.pristine})}
+                 ref="email"
+                 required={phoneOrEmailRequired}
+                 type="email"
                  valueLink={this.linkState("email")} />
           <label>{mozL10n.get("new_contact_fxos_phone_placeholder")}</label>
-          <input ref="tel" type="tel" required={phoneOrEmailRequired}
-                 className={cx({pristine: this.state.pristine})}
+          <input className={cx({pristine: this.state.pristine})}
+                 ref="tel"
+                 required={phoneOrEmailRequired}
+                 type="tel"
                  valueLink={this.linkState("tel")} />
           <ButtonGroup>
             <Button additionalClass="button-cancel"
                     caption={mozL10n.get("cancel_button")}
                     onClick={this.handleCancelButtonClick} />
             <Button additionalClass="button-accept"
                     caption={this.props.mode == "add"
                              ? mozL10n.get("add_contact_button")
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -24,18 +24,18 @@ loop.conversation = (function(mozL10n) {
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("conversationAppStore"),
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
-      mozLoop: React.PropTypes.object.isRequired
+      mozLoop: React.PropTypes.object.isRequired,
+      roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
     },
 
     getInitialState: function() {
       return this.getStoreState();
     },
 
     render: function() {
       switch(this.state.windowType) {
@@ -153,20 +153,21 @@ loop.conversation = (function(mozL10n) {
     if (hash) {
       windowId = hash[1];
     }
 
     window.addEventListener("unload", function(event) {
       dispatcher.dispatch(new sharedActions.WindowUnload());
     });
 
-    React.render(React.createElement(AppControllerView, {
-      roomStore: roomStore, 
-      dispatcher: dispatcher, 
-      mozLoop: navigator.mozLoop}), document.querySelector("#main"));
+    React.render(
+      React.createElement(AppControllerView, {
+        dispatcher: dispatcher, 
+        mozLoop: navigator.mozLoop, 
+        roomStore: roomStore}), document.querySelector("#main"));
 
     document.documentElement.setAttribute("lang", mozL10n.getLanguage());
     document.documentElement.setAttribute("dir", mozL10n.getDirection());
     document.body.setAttribute("platform", loop.shared.utils.getPlatform());
 
     dispatcher.dispatch(new sharedActions.GetWindowData({
       windowId: windowId
     }));
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -24,18 +24,18 @@ loop.conversation = (function(mozL10n) {
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("conversationAppStore"),
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      roomStore: React.PropTypes.instanceOf(loop.store.RoomStore),
-      mozLoop: React.PropTypes.object.isRequired
+      mozLoop: React.PropTypes.object.isRequired,
+      roomStore: React.PropTypes.instanceOf(loop.store.RoomStore)
     },
 
     getInitialState: function() {
       return this.getStoreState();
     },
 
     render: function() {
       switch(this.state.windowType) {
@@ -153,20 +153,21 @@ loop.conversation = (function(mozL10n) {
     if (hash) {
       windowId = hash[1];
     }
 
     window.addEventListener("unload", function(event) {
       dispatcher.dispatch(new sharedActions.WindowUnload());
     });
 
-    React.render(<AppControllerView
-      roomStore={roomStore}
-      dispatcher={dispatcher}
-      mozLoop={navigator.mozLoop} />, document.querySelector("#main"));
+    React.render(
+      <AppControllerView
+        dispatcher={dispatcher}
+        mozLoop={navigator.mozLoop}
+        roomStore={roomStore} />, document.querySelector("#main"));
 
     document.documentElement.setAttribute("lang", mozL10n.getLanguage());
     document.documentElement.setAttribute("dir", mozL10n.getDirection());
     document.body.setAttribute("platform", loop.shared.utils.getPlatform());
 
     dispatcher.dispatch(new sharedActions.GetWindowData({
       windowId: windowId
     }));
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -227,19 +227,20 @@ loop.conversationViews = (function(mozL1
       var dropdownMenuClassesDecline = React.addons.classSet({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
 
       return (
         React.createElement("div", {className: "call-window"}, 
-          React.createElement(CallIdentifierView, {video: this.props.callType === CALL_TYPES.AUDIO_VIDEO, 
+          React.createElement(CallIdentifierView, {
             peerIdentifier: this.props.callerId, 
-            showIcons: true}), 
+            showIcons: true, 
+            video: this.props.callType === CALL_TYPES.AUDIO_VIDEO}), 
 
           React.createElement("div", {className: "btn-group call-action-group"}, 
 
             React.createElement("div", {className: "fx-embedded-call-button-spacer"}), 
 
             React.createElement("div", {className: "btn-chevron-menu-group"}, 
               React.createElement("div", {className: "btn-group-chevron"}, 
                 React.createElement("div", {className: "btn-group"}, 
@@ -356,19 +357,19 @@ loop.conversationViews = (function(mozL1
   /**
    * View for pending conversations. Displays a cancel button and appropriate
    * pending/ringing strings.
    */
   var PendingConversationView = React.createClass({displayName: "PendingConversationView",
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       callState: React.PropTypes.string,
       contact: React.PropTypes.object,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       enableCancelButton: React.PropTypes.bool
     },
 
     getDefaultProps: function() {
       return {
         enableCancelButton: false
       };
     },
@@ -420,18 +421,18 @@ loop.conversationViews = (function(mozL1
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("conversationStore"),
       sharedMixins.AudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
+      contact: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      contact: React.PropTypes.object.isRequired,
       // This is used by the UI showcase.
       emailLinkError: React.PropTypes.bool,
       outgoing: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       return {
         emailLinkError: this.props.emailLinkError,
@@ -548,45 +549,45 @@ loop.conversationViews = (function(mozL1
                     onClick: this.cancelCall}, 
               mozL10n.get("cancel_button")
             ), 
             React.createElement("button", {className: retryClasses, 
                     onClick: this.retryCall}, 
               mozL10n.get("retry_call_button")
             ), 
             React.createElement("button", {className: emailClasses, 
-                    onClick: this.emailLink, 
-                    disabled: this.state.emailLinkButtonDisabled}, 
+                    disabled: this.state.emailLinkButtonDisabled, 
+                    onClick: this.emailLink}, 
               mozL10n.get("share_button3")
             )
           )
         )
       );
     }
   });
 
   var OngoingConversationView = React.createClass({displayName: "OngoingConversationView",
     mixins: [
       loop.store.StoreMixin("conversationStore"),
       sharedMixins.MediaSetupMixin
     ],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      // local
-      video: React.PropTypes.object,
       // local
       audio: React.PropTypes.object,
-      remoteVideoEnabled: React.PropTypes.bool,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      // The poster URLs are for UI-showcase testing and development.
+      localPosterUrl: React.PropTypes.string,
       // This is used from the props rather than the state to make it easier for
       // the ui-showcase.
       mediaConnected: React.PropTypes.bool,
-      // The poster URLs are for UI-showcase testing and development.
-      localPosterUrl: React.PropTypes.string,
-      remotePosterUrl: React.PropTypes.string
+      remotePosterUrl: React.PropTypes.string,
+      remoteVideoEnabled: React.PropTypes.bool,
+      // local
+      video: React.PropTypes.object
     },
 
     getDefaultProps: function() {
       return {
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true}
       };
     },
@@ -649,34 +650,34 @@ loop.conversationViews = (function(mozL1
 
       return (
         React.createElement("div", {className: "video-layout-wrapper"}, 
           React.createElement("div", {className: "conversation"}, 
             React.createElement("div", {className: "media nested"}, 
               React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                 React.createElement("div", {className: "video_inner remote focus-stream"}, 
                   React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
+                    mediaType: "remote", 
                     posterUrl: this.props.remotePosterUrl, 
-                    mediaType: "remote", 
                     srcVideoObject: this.state.remoteSrcVideoObject})
                 )
               ), 
               React.createElement("div", {className: localStreamClasses}, 
                 React.createElement(sharedViews.MediaView, {displayAvatar: !this.props.video.enabled, 
+                  mediaType: "local", 
                   posterUrl: this.props.localPosterUrl, 
-                  mediaType: "local", 
                   srcVideoObject: this.state.localSrcVideoObject})
               )
             ), 
             React.createElement(loop.shared.views.ConversationToolbar, {
+              audio: this.props.audio, 
               dispatcher: this.props.dispatcher, 
-              video: this.props.video, 
-              audio: this.props.audio, 
+              hangup: this.hangup, 
               publishStream: this.publishStream, 
-              hangup: this.hangup})
+              video: this.props.video})
           )
         )
       );
     }
   });
 
   /**
    * Master View Controller for outgoing calls. This manages
@@ -724,21 +725,20 @@ loop.conversationViews = (function(mozL1
       );
     },
 
     _renderViewFromCallType: function() {
       // For outgoing calls we can display the pending conversation view
       // for any state that render() doesn't manage.
       if (this.state.outgoing) {
         return (React.createElement(PendingConversationView, {
-          dispatcher: this.props.dispatcher, 
           callState: this.state.callState, 
           contact: this.state.contact, 
-          enableCancelButton: this._isCancellable()}
-        ));
+          dispatcher: this.props.dispatcher, 
+          enableCancelButton: this._isCancellable()}));
       }
 
       // For incoming calls that are in accepting state, display the
       // accept call view.
       if (this.state.callState === CALL_STATES.ALERTING) {
         return (React.createElement(AcceptCallView, {
           callType: this.state.callType, 
           callerId: this.state.callerId, 
@@ -763,30 +763,28 @@ loop.conversationViews = (function(mozL1
 
       switch (this.state.callState) {
         case CALL_STATES.CLOSE: {
           this._closeWindow();
           return null;
         }
         case CALL_STATES.TERMINATED: {
           return (React.createElement(CallFailedView, {
+            contact: this.state.contact, 
             dispatcher: this.props.dispatcher, 
-            contact: this.state.contact, 
-            outgoing: this.state.outgoing}
-          ));
+            outgoing: this.state.outgoing}));
         }
         case CALL_STATES.ONGOING: {
           return (React.createElement(OngoingConversationView, {
+            audio: {enabled: !this.state.audioMuted}, 
             dispatcher: this.props.dispatcher, 
-            video: {enabled: !this.state.videoMuted}, 
-            audio: {enabled: !this.state.audioMuted}, 
+            mediaConnected: this.state.mediaConnected, 
+            remoteSrcVideoObject: this.state.remoteSrcVideoObject, 
             remoteVideoEnabled: this.state.remoteVideoEnabled, 
-            mediaConnected: this.state.mediaConnected, 
-            remoteSrcVideoObject: this.state.remoteSrcVideoObject}
-            )
+            video: {enabled: !this.state.videoMuted}})
           );
         }
         case CALL_STATES.FINISHED: {
           this.play("terminated");
           return this._renderFeedbackView();
         }
         case CALL_STATES.INIT: {
           // We know what we are, but we haven't got the data yet.
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -227,19 +227,20 @@ loop.conversationViews = (function(mozL1
       var dropdownMenuClassesDecline = React.addons.classSet({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
 
       return (
         <div className="call-window">
-          <CallIdentifierView video={this.props.callType === CALL_TYPES.AUDIO_VIDEO}
+          <CallIdentifierView
             peerIdentifier={this.props.callerId}
-            showIcons={true} />
+            showIcons={true}
+            video={this.props.callType === CALL_TYPES.AUDIO_VIDEO} />
 
           <div className="btn-group call-action-group">
 
             <div className="fx-embedded-call-button-spacer"></div>
 
             <div className="btn-chevron-menu-group">
               <div className="btn-group-chevron">
                 <div className="btn-group">
@@ -356,19 +357,19 @@ loop.conversationViews = (function(mozL1
   /**
    * View for pending conversations. Displays a cancel button and appropriate
    * pending/ringing strings.
    */
   var PendingConversationView = React.createClass({
     mixins: [sharedMixins.AudioMixin],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       callState: React.PropTypes.string,
       contact: React.PropTypes.object,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       enableCancelButton: React.PropTypes.bool
     },
 
     getDefaultProps: function() {
       return {
         enableCancelButton: false
       };
     },
@@ -420,18 +421,18 @@ loop.conversationViews = (function(mozL1
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("conversationStore"),
       sharedMixins.AudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
+      contact: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      contact: React.PropTypes.object.isRequired,
       // This is used by the UI showcase.
       emailLinkError: React.PropTypes.bool,
       outgoing: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       return {
         emailLinkError: this.props.emailLinkError,
@@ -548,45 +549,45 @@ loop.conversationViews = (function(mozL1
                     onClick={this.cancelCall}>
               {mozL10n.get("cancel_button")}
             </button>
             <button className={retryClasses}
                     onClick={this.retryCall}>
               {mozL10n.get("retry_call_button")}
             </button>
             <button className={emailClasses}
-                    onClick={this.emailLink}
-                    disabled={this.state.emailLinkButtonDisabled}>
+                    disabled={this.state.emailLinkButtonDisabled}
+                    onClick={this.emailLink}>
               {mozL10n.get("share_button3")}
             </button>
           </div>
         </div>
       );
     }
   });
 
   var OngoingConversationView = React.createClass({
     mixins: [
       loop.store.StoreMixin("conversationStore"),
       sharedMixins.MediaSetupMixin
     ],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      // local
-      video: React.PropTypes.object,
       // local
       audio: React.PropTypes.object,
-      remoteVideoEnabled: React.PropTypes.bool,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      // The poster URLs are for UI-showcase testing and development.
+      localPosterUrl: React.PropTypes.string,
       // This is used from the props rather than the state to make it easier for
       // the ui-showcase.
       mediaConnected: React.PropTypes.bool,
-      // The poster URLs are for UI-showcase testing and development.
-      localPosterUrl: React.PropTypes.string,
-      remotePosterUrl: React.PropTypes.string
+      remotePosterUrl: React.PropTypes.string,
+      remoteVideoEnabled: React.PropTypes.bool,
+      // local
+      video: React.PropTypes.object
     },
 
     getDefaultProps: function() {
       return {
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true}
       };
     },
@@ -649,34 +650,34 @@ loop.conversationViews = (function(mozL1
 
       return (
         <div className="video-layout-wrapper">
           <div className="conversation">
             <div className="media nested">
               <div className="video_wrapper remote_wrapper">
                 <div className="video_inner remote focus-stream">
                   <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
+                    mediaType="remote"
                     posterUrl={this.props.remotePosterUrl}
-                    mediaType="remote"
                     srcVideoObject={this.state.remoteSrcVideoObject} />
                 </div>
               </div>
               <div className={localStreamClasses}>
                 <sharedViews.MediaView displayAvatar={!this.props.video.enabled}
+                  mediaType="local"
                   posterUrl={this.props.localPosterUrl}
-                  mediaType="local"
                   srcVideoObject={this.state.localSrcVideoObject} />
               </div>
             </div>
             <loop.shared.views.ConversationToolbar
+              audio={this.props.audio}
               dispatcher={this.props.dispatcher}
-              video={this.props.video}
-              audio={this.props.audio}
+              hangup={this.hangup}
               publishStream={this.publishStream}
-              hangup={this.hangup} />
+              video={this.props.video} />
           </div>
         </div>
       );
     }
   });
 
   /**
    * Master View Controller for outgoing calls. This manages
@@ -724,21 +725,20 @@ loop.conversationViews = (function(mozL1
       );
     },
 
     _renderViewFromCallType: function() {
       // For outgoing calls we can display the pending conversation view
       // for any state that render() doesn't manage.
       if (this.state.outgoing) {
         return (<PendingConversationView
-          dispatcher={this.props.dispatcher}
           callState={this.state.callState}
           contact={this.state.contact}
-          enableCancelButton={this._isCancellable()}
-        />);
+          dispatcher={this.props.dispatcher}
+          enableCancelButton={this._isCancellable()} />);
       }
 
       // For incoming calls that are in accepting state, display the
       // accept call view.
       if (this.state.callState === CALL_STATES.ALERTING) {
         return (<AcceptCallView
           callType={this.state.callType}
           callerId={this.state.callerId}
@@ -763,30 +763,28 @@ loop.conversationViews = (function(mozL1
 
       switch (this.state.callState) {
         case CALL_STATES.CLOSE: {
           this._closeWindow();
           return null;
         }
         case CALL_STATES.TERMINATED: {
           return (<CallFailedView
+            contact={this.state.contact}
             dispatcher={this.props.dispatcher}
-            contact={this.state.contact}
-            outgoing={this.state.outgoing}
-          />);
+            outgoing={this.state.outgoing} />);
         }
         case CALL_STATES.ONGOING: {
           return (<OngoingConversationView
+            audio={{enabled: !this.state.audioMuted}}
             dispatcher={this.props.dispatcher}
-            video={{enabled: !this.state.videoMuted}}
-            audio={{enabled: !this.state.audioMuted}}
-            remoteVideoEnabled={this.state.remoteVideoEnabled}
             mediaConnected={this.state.mediaConnected}
             remoteSrcVideoObject={this.state.remoteSrcVideoObject}
-            />
+            remoteVideoEnabled={this.state.remoteVideoEnabled}
+            video={{enabled: !this.state.videoMuted}} />
           );
         }
         case CALL_STATES.FINISHED: {
           this.play("terminated");
           return this._renderFeedbackView();
         }
         case CALL_STATES.INIT: {
           // We know what we are, but we haven't got the data yet.
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -15,19 +15,19 @@ loop.panel = (function(_, mozL10n) {
   var ButtonGroup = sharedViews.ButtonGroup;
   var Checkbox = sharedViews.Checkbox;
   var ContactsList = loop.contacts.ContactsList;
   var ContactDetailsForm = loop.contacts.ContactDetailsForm;
 
   var TabView = React.createClass({displayName: "TabView",
     propTypes: {
       buttonsHidden: React.PropTypes.array,
+      mozLoop: React.PropTypes.object,
       // The selectedTab prop is used by the UI showcase.
-      selectedTab: React.PropTypes.string,
-      mozLoop: React.PropTypes.object
+      selectedTab: React.PropTypes.string
     },
 
     getDefaultProps: function() {
       return {
         buttonsHidden: []
       };
     },
 
@@ -77,24 +77,24 @@ loop.panel = (function(_, mozL10n) {
         var tabName = tab.props.name;
         if (this.props.buttonsHidden.indexOf(tabName) > -1) {
           return;
         }
         var isSelected = (this.state.selectedTab == tabName);
         if (!tab.props.hidden) {
           tabButtons.push(
             React.createElement("li", {className: cx({selected: isSelected}), 
+                "data-tab-name": tabName, 
                 key: i, 
-                "data-tab-name": tabName, 
-                title: mozL10n.get(tabName + "_tab_button_tooltip"), 
-                onClick: this.handleSelectTab})
+                onClick: this.handleSelectTab, 
+                title: mozL10n.get(tabName + "_tab_button_tooltip")})
           );
         }
         tabs.push(
-          React.createElement("div", {key: i, className: cx({tab: true, selected: isSelected})}, 
+          React.createElement("div", {className: cx({tab: true, selected: isSelected}), key: i}, 
             tab.props.children
           )
         );
       }, this);
       return (
         React.createElement("div", {className: "tab-view-container"}, 
           React.createElement("ul", {className: "tab-view"}, tabButtons), 
           tabs
@@ -159,23 +159,23 @@ loop.panel = (function(_, mozL10n) {
 
       return (
         React.createElement("div", {className: "dropdown"}, 
           React.createElement("p", {className: "dnd-status", onClick: this.toggleDropdownMenu, ref: "menu-button"}, 
             React.createElement("span", null, availabilityText), 
             React.createElement("i", {className: availabilityStatus})
           ), 
           React.createElement("ul", {className: availabilityDropdown}, 
-            React.createElement("li", {onClick: this.changeAvailability("available"), 
-                className: "dropdown-menu-item dnd-make-available"}, 
+            React.createElement("li", {className: "dropdown-menu-item dnd-make-available", 
+                onClick: this.changeAvailability("available")}, 
               React.createElement("i", {className: "status status-available"}), 
               React.createElement("span", null, mozL10n.get("display_name_available_status"))
             ), 
-            React.createElement("li", {onClick: this.changeAvailability("do-not-disturb"), 
-                className: "dropdown-menu-item dnd-make-unavailable"}, 
+            React.createElement("li", {className: "dropdown-menu-item dnd-make-unavailable", 
+                onClick: this.changeAvailability("do-not-disturb")}, 
               React.createElement("i", {className: "status status-dnd"}), 
               React.createElement("span", null, mozL10n.get("display_name_dnd_status"))
             )
           )
         )
       );
     }
   });
@@ -197,19 +197,19 @@ loop.panel = (function(_, mozL10n) {
       }
       return (
         React.createElement("div", {id: "fte-getstarted"}, 
           React.createElement("header", {id: "fte-title"}, 
             mozL10n.get("first_time_experience_title", {
               "clientShortname": mozL10n.get("clientShortname2")
             })
           ), 
-          React.createElement(Button, {htmlId: "fte-button", 
-                  onClick: this.handleButtonClick, 
-                  caption: mozL10n.get("first_time_experience_button_label")})
+          React.createElement(Button, {caption: mozL10n.get("first_time_experience_button_label"), 
+                  htmlId: "fte-button", 
+                  onClick: this.handleButtonClick})
         )
       );
     }
   });
 
   /**
    * Displays a view requesting the user to sign-in again.
    */
@@ -286,19 +286,19 @@ loop.panel = (function(_, mozL10n) {
     renderPartnerLogo: function() {
       if (!this.state.showPartnerLogo) {
         return null;
       }
 
       var locale = mozL10n.getLanguage();
       navigator.mozLoop.setLoopPref("showPartnerLogo", false);
       return (
-        React.createElement("p", {id: "powered-by", className: "powered-by"}, 
+        React.createElement("p", {className: "powered-by", id: "powered-by"}, 
           mozL10n.get("powered_by_beforeLogo"), 
-          React.createElement("img", {id: "powered-by-logo", className: locale}), 
+          React.createElement("img", {className: locale, id: "powered-by-logo"}), 
           mozL10n.get("powered_by_afterLogo")
         )
       );
     },
 
     render: function() {
       if (!this.state.gettingStartedSeen || this.state.seenToS == "unseen") {
         var terms_of_use_url = navigator.mozLoop.getLoopPref("legal.ToS_url");
@@ -330,32 +330,32 @@ loop.panel = (function(_, mozL10n) {
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
    */
   var SettingsDropdownEntry = React.createClass({displayName: "SettingsDropdownEntry",
     propTypes: {
-      onClick: React.PropTypes.func.isRequired,
+      displayed: React.PropTypes.bool,
+      icon: React.PropTypes.string,
       label: React.PropTypes.string.isRequired,
-      icon: React.PropTypes.string,
-      displayed: React.PropTypes.bool
+      onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {displayed: true};
     },
 
     render: function() {
       if (!this.props.displayed) {
         return null;
       }
       return (
-        React.createElement("li", {onClick: this.props.onClick, className: "dropdown-menu-item"}, 
+        React.createElement("li", {className: "dropdown-menu-item", onClick: this.props.onClick}, 
           this.props.icon ?
             React.createElement("i", {className: "icon icon-" + this.props.icon}) :
             null, 
           React.createElement("span", null, this.props.label)
         )
       );
     }
   });
@@ -405,39 +405,39 @@ loop.panel = (function(_, mozL10n) {
 
     render: function() {
       var cx = React.addons.classSet;
 
       return (
         React.createElement("div", {className: "settings-menu dropdown"}, 
           React.createElement("a", {className: "button-settings", 
              onClick: this.toggleDropdownMenu, 
-             title: mozL10n.get("settings_menu_button_tooltip"), 
-             ref: "menu-button"}), 
+             ref: "menu-button", 
+             title: mozL10n.get("settings_menu_button_tooltip")}), 
           React.createElement("ul", {className: cx({"dropdown-menu": true, hide: !this.state.showMenu})}, 
-            React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_settings"), 
-                                   onClick: this.handleClickSettingsEntry, 
-                                   displayed: false, 
-                                   icon: "settings"}), 
-            React.createElement(SettingsDropdownEntry, {label: mozL10n.get("settings_menu_item_account"), 
-                                   onClick: this.handleClickAccountEntry, 
+            React.createElement(SettingsDropdownEntry, {displayed: false, 
+                                   icon: "settings", 
+                                   label: mozL10n.get("settings_menu_item_settings"), 
+                                   onClick: this.handleClickSettingsEntry}), 
+            React.createElement(SettingsDropdownEntry, {displayed: this._isSignedIn() && this.props.mozLoop.fxAEnabled, 
                                    icon: "account", 
-                                   displayed: this._isSignedIn() && this.props.mozLoop.fxAEnabled}), 
+                                   label: mozL10n.get("settings_menu_item_account"), 
+                                   onClick: this.handleClickAccountEntry}), 
             React.createElement(SettingsDropdownEntry, {icon: "tour", 
                                    label: mozL10n.get("tour_label"), 
                                    onClick: this.openGettingStartedTour}), 
-            React.createElement(SettingsDropdownEntry, {label: this._isSignedIn() ?
+            React.createElement(SettingsDropdownEntry, {displayed: this.props.mozLoop.fxAEnabled, 
+                                   icon: this._isSignedIn() ? "signout" : "signin", 
+                                   label: this._isSignedIn() ?
                                           mozL10n.get("settings_menu_item_signout") :
                                           mozL10n.get("settings_menu_item_signin"), 
-                                   onClick: this.handleClickAuthEntry, 
-                                   displayed: this.props.mozLoop.fxAEnabled, 
-                                   icon: this._isSignedIn() ? "signout" : "signin"}), 
-            React.createElement(SettingsDropdownEntry, {label: mozL10n.get("help_label"), 
-                                   onClick: this.handleHelpEntry, 
-                                   icon: "help"})
+                                   onClick: this.handleClickAuthEntry}), 
+            React.createElement(SettingsDropdownEntry, {icon: "help", 
+                                   label: mozL10n.get("help_label"), 
+                                   onClick: this.handleHelpEntry})
           )
         )
       );
     }
   });
 
   /**
    * FxA sign in/up link component.
@@ -495,17 +495,17 @@ loop.panel = (function(_, mozL10n) {
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (!roomUrl) {
         return null;
       }
 
       return (
         React.createElement("div", {className: "room-entry-context-item"}, 
-          React.createElement("a", {href: roomUrl.location, title: roomUrl.description, onClick: this.handleClick}, 
+          React.createElement("a", {href: roomUrl.location, onClick: this.handleClick, title: roomUrl.description}, 
             React.createElement("img", {src: roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"})
           )
         )
       );
     }
   });
 
   /**
@@ -582,45 +582,45 @@ loop.panel = (function(_, mozL10n) {
         "room-active": this._isActive()
       });
       var copyButtonClasses = React.addons.classSet({
         "copy-link": true,
         "checked": this.state.urlCopied
       });
 
       return (
-        React.createElement("div", {className: roomClasses, onMouseLeave: this.handleMouseLeave, 
-             onClick: this.handleClickEntry}, 
+        React.createElement("div", {className: roomClasses, onClick: this.handleClickEntry, 
+             onMouseLeave: this.handleMouseLeave}, 
           React.createElement("h2", null, 
             React.createElement("span", {className: "room-notification"}), 
             this.props.room.decryptedContext.roomName, 
             React.createElement("button", {className: copyButtonClasses, 
-              title: mozL10n.get("rooms_list_copy_url_tooltip"), 
-              onClick: this.handleCopyButtonClick}), 
+              onClick: this.handleCopyButtonClick, 
+              title: mozL10n.get("rooms_list_copy_url_tooltip")}), 
             React.createElement("button", {className: "delete-link", 
-              title: mozL10n.get("rooms_list_delete_tooltip"), 
-              onClick: this.handleDeleteButtonClick})
+              onClick: this.handleDeleteButtonClick, 
+              title: mozL10n.get("rooms_list_delete_tooltip")})
           ), 
           React.createElement(RoomEntryContextItem, {mozLoop: this.props.mozLoop, 
                                 roomUrls: this.props.room.decryptedContext.urls})
         )
       );
     }
   });
 
   /**
    * Room list.
    */
   var RoomList = React.createClass({displayName: "RoomList",
     mixins: [Backbone.Events, sharedMixins.WindowCloseMixin],
 
     propTypes: {
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       mozLoop: React.PropTypes.object.isRequired,
       store: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       userDisplayName: React.PropTypes.string.isRequired  // for room creation
     },
 
     getInitialState: function() {
       return this.props.store.getStoreState();
     },
 
     componentDidMount: function() {
@@ -665,21 +665,20 @@ loop.panel = (function(_, mozL10n) {
 
       return (
         React.createElement("div", {className: "rooms"}, 
           React.createElement("h1", null, this._getListHeading()), 
           React.createElement("div", {className: "room-list"}, 
             this.state.rooms.map(function(room, i) {
               return (
                 React.createElement(RoomEntry, {
+                  dispatcher: this.props.dispatcher, 
                   key: room.roomToken, 
-                  dispatcher: this.props.dispatcher, 
                   mozLoop: this.props.mozLoop, 
-                  room: room}
-                )
+                  room: room})
               );
             }, this)
           ), 
           React.createElement(NewRoomView, {dispatcher: this.props.dispatcher, 
             mozLoop: this.props.mozLoop, 
             pendingOperation: this.state.pendingCreation ||
               this.state.pendingInitialRetrieval, 
             userDisplayName: this.props.userDisplayName})
@@ -783,40 +782,40 @@ loop.panel = (function(_, mozL10n) {
               allowClick: false, 
               description: this.state.description, 
               showContextTitle: false, 
               thumbnail: this.state.previewImage, 
               url: this.state.url, 
               useDesktopPaths: true})
           ), 
           React.createElement("button", {className: "btn btn-info new-room-button", 
-                  onClick: this.handleCreateButtonClick, 
-                  disabled: this.props.pendingOperation}, 
+                  disabled: this.props.pendingOperation, 
+                  onClick: this.handleCreateButtonClick}, 
             mozL10n.get("rooms_new_room_button_label")
           )
         )
       );
     }
   });
 
   /**
    * Panel view.
    */
   var PanelView = React.createClass({displayName: "PanelView",
     propTypes: {
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      mozLoop: React.PropTypes.object.isRequired,
       notifications: React.PropTypes.object.isRequired,
-      // Mostly used for UI components showcase and unit tests
-      userProfile: React.PropTypes.object,
+      roomStore:
+        React.PropTypes.instanceOf(loop.store.RoomStore).isRequired,
+      selectedTab: React.PropTypes.string,
       // Used only for unit tests.
       showTabButtons: React.PropTypes.bool,
-      selectedTab: React.PropTypes.string,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      mozLoop: React.PropTypes.object.isRequired,
-      roomStore:
-        React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
+      // Mostly used for UI components showcase and unit tests
+      userProfile: React.PropTypes.object
     },
 
     getInitialState: function() {
       return {
         hasEncryptionKey: this.props.mozLoop.hasEncryptionKey,
         userProfile: this.props.userProfile || this.props.mozLoop.userProfile,
         gettingStartedSeen: this.props.mozLoop.getLoopPref("gettingStarted.seen")
       };
@@ -918,18 +917,19 @@ loop.panel = (function(_, mozL10n) {
     },
 
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
 
       if (!this.state.gettingStartedSeen) {
         return (
           React.createElement("div", null, 
-            React.createElement(NotificationListView, {notifications: this.props.notifications, 
-                                  clearOnDocumentHidden: true}), 
+            React.createElement(NotificationListView, {
+              clearOnDocumentHidden: true, 
+              notifications: this.props.notifications}), 
             React.createElement(GettingStartedView, null), 
             React.createElement(ToSView, null)
           )
         );
       }
 
       if (!this.state.hasEncryptionKey) {
         return React.createElement(SignInRequestView, {mozLoop: this.props.mozLoop});
@@ -938,43 +938,54 @@ loop.panel = (function(_, mozL10n) {
       // Determine which buttons to NOT show.
       var hideButtons = [];
       if (!this.state.userProfile && !this.props.showTabButtons) {
         hideButtons.push("contacts");
       }
 
       return (
         React.createElement("div", null, 
-          React.createElement(NotificationListView, {notifications: this.props.notifications, 
-                                clearOnDocumentHidden: true}), 
-          React.createElement(TabView, {ref: "tabView", selectedTab: this.props.selectedTab, 
-            buttonsHidden: hideButtons, mozLoop: this.props.mozLoop}, 
+          React.createElement(NotificationListView, {
+            clearOnDocumentHidden: true, 
+            notifications: this.props.notifications}), 
+          React.createElement(TabView, {
+            buttonsHidden: hideButtons, 
+            mozLoop: this.props.mozLoop, 
+            ref: "tabView", 
+            selectedTab: this.props.selectedTab}, 
             React.createElement(Tab, {name: "rooms"}, 
               React.createElement(RoomList, {dispatcher: this.props.dispatcher, 
+                        mozLoop: this.props.mozLoop, 
                         store: this.props.roomStore, 
-                        userDisplayName: this._getUserDisplayName(), 
-                        mozLoop: this.props.mozLoop}), 
+                        userDisplayName: this._getUserDisplayName()}), 
               React.createElement(ToSView, null)
             ), 
             React.createElement(Tab, {name: "contacts"}, 
-              React.createElement(ContactsList, {selectTab: this.selectTab, 
-                            startForm: this.startForm, 
-                            notifications: this.props.notifications})
+              React.createElement(ContactsList, {
+                notifications: this.props.notifications, 
+                selectTab: this.selectTab, 
+                startForm: this.startForm})
             ), 
-            React.createElement(Tab, {name: "contacts_add", hidden: true}, 
-              React.createElement(ContactDetailsForm, {ref: "contacts_add", mode: "add", 
-                                  selectTab: this.selectTab})
+            React.createElement(Tab, {hidden: true, name: "contacts_add"}, 
+              React.createElement(ContactDetailsForm, {
+                mode: "add", 
+                ref: "contacts_add", 
+                selectTab: this.selectTab})
             ), 
-            React.createElement(Tab, {name: "contacts_edit", hidden: true}, 
-              React.createElement(ContactDetailsForm, {ref: "contacts_edit", mode: "edit", 
-                                  selectTab: this.selectTab})
+            React.createElement(Tab, {hidden: true, name: "contacts_edit"}, 
+              React.createElement(ContactDetailsForm, {
+                mode: "edit", 
+                ref: "contacts_edit", 
+                selectTab: this.selectTab})
             ), 
-            React.createElement(Tab, {name: "contacts_import", hidden: true}, 
-              React.createElement(ContactDetailsForm, {ref: "contacts_import", mode: "import", 
-                                  selectTab: this.selectTab})
+            React.createElement(Tab, {hidden: true, name: "contacts_import"}, 
+              React.createElement(ContactDetailsForm, {
+                mode: "import", 
+                ref: "contacts_import", 
+                selectTab: this.selectTab})
             )
           ), 
           React.createElement("div", {className: "footer"}, 
             React.createElement("div", {className: "user-details"}, 
               React.createElement(UserIdentity, {displayName: this._getUserDisplayName()}), 
               React.createElement(AvailabilityDropdown, null)
             ), 
             React.createElement("div", {className: "signin-details"}, 
@@ -999,20 +1010,20 @@ loop.panel = (function(_, mozL10n) {
     var notifications = new sharedModels.NotificationCollection();
     var dispatcher = new loop.Dispatcher();
     var roomStore = new loop.store.RoomStore(dispatcher, {
       mozLoop: navigator.mozLoop,
       notifications: notifications
     });
 
     React.render(React.createElement(PanelView, {
+      dispatcher: dispatcher, 
+      mozLoop: navigator.mozLoop, 
       notifications: notifications, 
-      roomStore: roomStore, 
-      mozLoop: navigator.mozLoop, 
-      dispatcher: dispatcher}), document.querySelector("#main"));
+      roomStore: roomStore}), document.querySelector("#main"));
 
     document.documentElement.setAttribute("lang", mozL10n.getLanguage());
     document.documentElement.setAttribute("dir", mozL10n.getDirection());
     document.body.setAttribute("platform", loop.shared.utils.getPlatform());
 
     // Notify the window that we've finished initalization and initial layout
     var evtObject = document.createEvent("Event");
     evtObject.initEvent("loopPanelInitialized", true, false);
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -15,19 +15,19 @@ loop.panel = (function(_, mozL10n) {
   var ButtonGroup = sharedViews.ButtonGroup;
   var Checkbox = sharedViews.Checkbox;
   var ContactsList = loop.contacts.ContactsList;
   var ContactDetailsForm = loop.contacts.ContactDetailsForm;
 
   var TabView = React.createClass({
     propTypes: {
       buttonsHidden: React.PropTypes.array,
+      mozLoop: React.PropTypes.object,
       // The selectedTab prop is used by the UI showcase.
-      selectedTab: React.PropTypes.string,
-      mozLoop: React.PropTypes.object
+      selectedTab: React.PropTypes.string
     },
 
     getDefaultProps: function() {
       return {
         buttonsHidden: []
       };
     },
 
@@ -77,24 +77,24 @@ loop.panel = (function(_, mozL10n) {
         var tabName = tab.props.name;
         if (this.props.buttonsHidden.indexOf(tabName) > -1) {
           return;
         }
         var isSelected = (this.state.selectedTab == tabName);
         if (!tab.props.hidden) {
           tabButtons.push(
             <li className={cx({selected: isSelected})}
+                data-tab-name={tabName}
                 key={i}
-                data-tab-name={tabName}
-                title={mozL10n.get(tabName + "_tab_button_tooltip")}
-                onClick={this.handleSelectTab} />
+                onClick={this.handleSelectTab}
+                title={mozL10n.get(tabName + "_tab_button_tooltip")} />
           );
         }
         tabs.push(
-          <div key={i} className={cx({tab: true, selected: isSelected})}>
+          <div className={cx({tab: true, selected: isSelected})} key={i}>
             {tab.props.children}
           </div>
         );
       }, this);
       return (
         <div className="tab-view-container">
           <ul className="tab-view">{tabButtons}</ul>
           {tabs}
@@ -159,23 +159,23 @@ loop.panel = (function(_, mozL10n) {
 
       return (
         <div className="dropdown">
           <p className="dnd-status" onClick={this.toggleDropdownMenu} ref="menu-button">
             <span>{availabilityText}</span>
             <i className={availabilityStatus}></i>
           </p>
           <ul className={availabilityDropdown}>
-            <li onClick={this.changeAvailability("available")}
-                className="dropdown-menu-item dnd-make-available">
+            <li className="dropdown-menu-item dnd-make-available"
+                onClick={this.changeAvailability("available")}>
               <i className="status status-available"></i>
               <span>{mozL10n.get("display_name_available_status")}</span>
             </li>
-            <li onClick={this.changeAvailability("do-not-disturb")}
-                className="dropdown-menu-item dnd-make-unavailable">
+            <li className="dropdown-menu-item dnd-make-unavailable"
+                onClick={this.changeAvailability("do-not-disturb")}>
               <i className="status status-dnd"></i>
               <span>{mozL10n.get("display_name_dnd_status")}</span>
             </li>
           </ul>
         </div>
       );
     }
   });
@@ -197,19 +197,19 @@ loop.panel = (function(_, mozL10n) {
       }
       return (
         <div id="fte-getstarted">
           <header id="fte-title">
             {mozL10n.get("first_time_experience_title", {
               "clientShortname": mozL10n.get("clientShortname2")
             })}
           </header>
-          <Button htmlId="fte-button"
-                  onClick={this.handleButtonClick}
-                  caption={mozL10n.get("first_time_experience_button_label")} />
+          <Button caption={mozL10n.get("first_time_experience_button_label")}
+                  htmlId="fte-button"
+                  onClick={this.handleButtonClick} />
         </div>
       );
     }
   });
 
   /**
    * Displays a view requesting the user to sign-in again.
    */
@@ -286,19 +286,19 @@ loop.panel = (function(_, mozL10n) {
     renderPartnerLogo: function() {
       if (!this.state.showPartnerLogo) {
         return null;
       }
 
       var locale = mozL10n.getLanguage();
       navigator.mozLoop.setLoopPref("showPartnerLogo", false);
       return (
-        <p id="powered-by" className="powered-by">
+        <p className="powered-by" id="powered-by">
           {mozL10n.get("powered_by_beforeLogo")}
-          <img id="powered-by-logo" className={locale} />
+          <img className={locale} id="powered-by-logo" />
           {mozL10n.get("powered_by_afterLogo")}
         </p>
       );
     },
 
     render: function() {
       if (!this.state.gettingStartedSeen || this.state.seenToS == "unseen") {
         var terms_of_use_url = navigator.mozLoop.getLoopPref("legal.ToS_url");
@@ -330,32 +330,32 @@ loop.panel = (function(_, mozL10n) {
     }
   });
 
   /**
    * Panel settings (gear) menu entry.
    */
   var SettingsDropdownEntry = React.createClass({
     propTypes: {
-      onClick: React.PropTypes.func.isRequired,
+      displayed: React.PropTypes.bool,
+      icon: React.PropTypes.string,
       label: React.PropTypes.string.isRequired,
-      icon: React.PropTypes.string,
-      displayed: React.PropTypes.bool
+      onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {displayed: true};
     },
 
     render: function() {
       if (!this.props.displayed) {
         return null;
       }
       return (
-        <li onClick={this.props.onClick} className="dropdown-menu-item">
+        <li className="dropdown-menu-item" onClick={this.props.onClick}>
           {this.props.icon ?
             <i className={"icon icon-" + this.props.icon}></i> :
             null}
           <span>{this.props.label}</span>
         </li>
       );
     }
   });
@@ -405,39 +405,39 @@ loop.panel = (function(_, mozL10n) {
 
     render: function() {
       var cx = React.addons.classSet;
 
       return (
         <div className="settings-menu dropdown">
           <a className="button-settings"
              onClick={this.toggleDropdownMenu}
-             title={mozL10n.get("settings_menu_button_tooltip")}
-             ref="menu-button" />
+             ref="menu-button"
+             title={mozL10n.get("settings_menu_button_tooltip")} />
           <ul className={cx({"dropdown-menu": true, hide: !this.state.showMenu})}>
-            <SettingsDropdownEntry label={mozL10n.get("settings_menu_item_settings")}
-                                   onClick={this.handleClickSettingsEntry}
-                                   displayed={false}
-                                   icon="settings" />
-            <SettingsDropdownEntry label={mozL10n.get("settings_menu_item_account")}
-                                   onClick={this.handleClickAccountEntry}
+            <SettingsDropdownEntry displayed={false}
+                                   icon="settings"
+                                   label={mozL10n.get("settings_menu_item_settings")}
+                                   onClick={this.handleClickSettingsEntry} />
+            <SettingsDropdownEntry displayed={this._isSignedIn() && this.props.mozLoop.fxAEnabled}
                                    icon="account"
-                                   displayed={this._isSignedIn() && this.props.mozLoop.fxAEnabled} />
+                                   label={mozL10n.get("settings_menu_item_account")}
+                                   onClick={this.handleClickAccountEntry} />
             <SettingsDropdownEntry icon="tour"
                                    label={mozL10n.get("tour_label")}
                                    onClick={this.openGettingStartedTour} />
-            <SettingsDropdownEntry label={this._isSignedIn() ?
+            <SettingsDropdownEntry displayed={this.props.mozLoop.fxAEnabled}
+                                   icon={this._isSignedIn() ? "signout" : "signin"}
+                                   label={this._isSignedIn() ?
                                           mozL10n.get("settings_menu_item_signout") :
                                           mozL10n.get("settings_menu_item_signin")}
-                                   onClick={this.handleClickAuthEntry}
-                                   displayed={this.props.mozLoop.fxAEnabled}
-                                   icon={this._isSignedIn() ? "signout" : "signin"} />
-            <SettingsDropdownEntry label={mozL10n.get("help_label")}
-                                   onClick={this.handleHelpEntry}
-                                   icon="help" />
+                                   onClick={this.handleClickAuthEntry} />
+            <SettingsDropdownEntry icon="help"
+                                   label={mozL10n.get("help_label")}
+                                   onClick={this.handleHelpEntry} />
           </ul>
         </div>
       );
     }
   });
 
   /**
    * FxA sign in/up link component.
@@ -495,17 +495,17 @@ loop.panel = (function(_, mozL10n) {
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (!roomUrl) {
         return null;
       }
 
       return (
         <div className="room-entry-context-item">
-          <a href={roomUrl.location} title={roomUrl.description} onClick={this.handleClick}>
+          <a href={roomUrl.location} onClick={this.handleClick} title={roomUrl.description}>
             <img src={roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"} />
           </a>
         </div>
       );
     }
   });
 
   /**
@@ -582,45 +582,45 @@ loop.panel = (function(_, mozL10n) {
         "room-active": this._isActive()
       });
       var copyButtonClasses = React.addons.classSet({
         "copy-link": true,
         "checked": this.state.urlCopied
       });
 
       return (
-        <div className={roomClasses} onMouseLeave={this.handleMouseLeave}
-             onClick={this.handleClickEntry}>
+        <div className={roomClasses} onClick={this.handleClickEntry}
+             onMouseLeave={this.handleMouseLeave}>
           <h2>
             <span className="room-notification" />
             {this.props.room.decryptedContext.roomName}
             <button className={copyButtonClasses}
-              title={mozL10n.get("rooms_list_copy_url_tooltip")}
-              onClick={this.handleCopyButtonClick} />
+              onClick={this.handleCopyButtonClick}
+              title={mozL10n.get("rooms_list_copy_url_tooltip")} />
             <button className="delete-link"
-              title={mozL10n.get("rooms_list_delete_tooltip")}
-              onClick={this.handleDeleteButtonClick} />
+              onClick={this.handleDeleteButtonClick}
+              title={mozL10n.get("rooms_list_delete_tooltip")} />
           </h2>
           <RoomEntryContextItem mozLoop={this.props.mozLoop}
                                 roomUrls={this.props.room.decryptedContext.urls} />
         </div>
       );
     }
   });
 
   /**
    * Room list.
    */
   var RoomList = React.createClass({
     mixins: [Backbone.Events, sharedMixins.WindowCloseMixin],
 
     propTypes: {
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       mozLoop: React.PropTypes.object.isRequired,
       store: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       userDisplayName: React.PropTypes.string.isRequired  // for room creation
     },
 
     getInitialState: function() {
       return this.props.store.getStoreState();
     },
 
     componentDidMount: function() {
@@ -665,21 +665,20 @@ loop.panel = (function(_, mozL10n) {
 
       return (
         <div className="rooms">
           <h1>{this._getListHeading()}</h1>
           <div className="room-list">{
             this.state.rooms.map(function(room, i) {
               return (
                 <RoomEntry
+                  dispatcher={this.props.dispatcher}
                   key={room.roomToken}
-                  dispatcher={this.props.dispatcher}
                   mozLoop={this.props.mozLoop}
-                  room={room}
-                />
+                  room={room} />
               );
             }, this)
           }</div>
           <NewRoomView dispatcher={this.props.dispatcher}
             mozLoop={this.props.mozLoop}
             pendingOperation={this.state.pendingCreation ||
               this.state.pendingInitialRetrieval}
             userDisplayName={this.props.userDisplayName} />
@@ -783,40 +782,40 @@ loop.panel = (function(_, mozL10n) {
               allowClick={false}
               description={this.state.description}
               showContextTitle={false}
               thumbnail={this.state.previewImage}
               url={this.state.url}
               useDesktopPaths={true} />
           </div>
           <button className="btn btn-info new-room-button"
-                  onClick={this.handleCreateButtonClick}
-                  disabled={this.props.pendingOperation}>
+                  disabled={this.props.pendingOperation}
+                  onClick={this.handleCreateButtonClick}>
             {mozL10n.get("rooms_new_room_button_label")}
           </button>
         </div>
       );
     }
   });
 
   /**
    * Panel view.
    */
   var PanelView = React.createClass({
     propTypes: {
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      mozLoop: React.PropTypes.object.isRequired,
       notifications: React.PropTypes.object.isRequired,
-      // Mostly used for UI components showcase and unit tests
-      userProfile: React.PropTypes.object,
+      roomStore:
+        React.PropTypes.instanceOf(loop.store.RoomStore).isRequired,
+      selectedTab: React.PropTypes.string,
       // Used only for unit tests.
       showTabButtons: React.PropTypes.bool,
-      selectedTab: React.PropTypes.string,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      mozLoop: React.PropTypes.object.isRequired,
-      roomStore:
-        React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
+      // Mostly used for UI components showcase and unit tests
+      userProfile: React.PropTypes.object
     },
 
     getInitialState: function() {
       return {
         hasEncryptionKey: this.props.mozLoop.hasEncryptionKey,
         userProfile: this.props.userProfile || this.props.mozLoop.userProfile,
         gettingStartedSeen: this.props.mozLoop.getLoopPref("gettingStarted.seen")
       };
@@ -918,18 +917,19 @@ loop.panel = (function(_, mozL10n) {
     },
 
     render: function() {
       var NotificationListView = sharedViews.NotificationListView;
 
       if (!this.state.gettingStartedSeen) {
         return (
           <div>
-            <NotificationListView notifications={this.props.notifications}
-                                  clearOnDocumentHidden={true} />
+            <NotificationListView
+              clearOnDocumentHidden={true}
+              notifications={this.props.notifications} />
             <GettingStartedView />
             <ToSView />
           </div>
         );
       }
 
       if (!this.state.hasEncryptionKey) {
         return <SignInRequestView mozLoop={this.props.mozLoop} />;
@@ -938,43 +938,54 @@ loop.panel = (function(_, mozL10n) {
       // Determine which buttons to NOT show.
       var hideButtons = [];
       if (!this.state.userProfile && !this.props.showTabButtons) {
         hideButtons.push("contacts");
       }
 
       return (
         <div>
-          <NotificationListView notifications={this.props.notifications}
-                                clearOnDocumentHidden={true} />
-          <TabView ref="tabView" selectedTab={this.props.selectedTab}
-            buttonsHidden={hideButtons} mozLoop={this.props.mozLoop}>
+          <NotificationListView
+            clearOnDocumentHidden={true}
+            notifications={this.props.notifications} />
+          <TabView
+            buttonsHidden={hideButtons}
+            mozLoop={this.props.mozLoop}
+            ref="tabView"
+            selectedTab={this.props.selectedTab}>
             <Tab name="rooms">
               <RoomList dispatcher={this.props.dispatcher}
+                        mozLoop={this.props.mozLoop}
                         store={this.props.roomStore}
-                        userDisplayName={this._getUserDisplayName()}
-                        mozLoop={this.props.mozLoop}/>
+                        userDisplayName={this._getUserDisplayName()} />
               <ToSView />
             </Tab>
             <Tab name="contacts">
-              <ContactsList selectTab={this.selectTab}
-                            startForm={this.startForm}
-                            notifications={this.props.notifications} />
+              <ContactsList
+                notifications={this.props.notifications}
+                selectTab={this.selectTab}
+                startForm={this.startForm} />
             </Tab>
-            <Tab name="contacts_add" hidden={true}>
-              <ContactDetailsForm ref="contacts_add" mode="add"
-                                  selectTab={this.selectTab} />
+            <Tab hidden={true} name="contacts_add">
+              <ContactDetailsForm
+                mode="add"
+                ref="contacts_add"
+                selectTab={this.selectTab} />
             </Tab>
-            <Tab name="contacts_edit" hidden={true}>
-              <ContactDetailsForm ref="contacts_edit" mode="edit"
-                                  selectTab={this.selectTab} />
+            <Tab hidden={true} name="contacts_edit">
+              <ContactDetailsForm
+                mode="edit"
+                ref="contacts_edit"
+                selectTab={this.selectTab} />
             </Tab>
-            <Tab name="contacts_import" hidden={true}>
-              <ContactDetailsForm ref="contacts_import" mode="import"
-                                  selectTab={this.selectTab}/>
+            <Tab hidden={true} name="contacts_import">
+              <ContactDetailsForm
+                mode="import"
+                ref="contacts_import"
+                selectTab={this.selectTab}/>
             </Tab>
           </TabView>
           <div className="footer">
             <div className="user-details">
               <UserIdentity displayName={this._getUserDisplayName()} />
               <AvailabilityDropdown />
             </div>
             <div className="signin-details">
@@ -999,20 +1010,20 @@ loop.panel = (function(_, mozL10n) {
     var notifications = new sharedModels.NotificationCollection();
     var dispatcher = new loop.Dispatcher();
     var roomStore = new loop.store.RoomStore(dispatcher, {
       mozLoop: navigator.mozLoop,
       notifications: notifications
     });
 
     React.render(<PanelView
+      dispatcher={dispatcher}
+      mozLoop={navigator.mozLoop}
       notifications={notifications}
-      roomStore={roomStore}
-      mozLoop={navigator.mozLoop}
-      dispatcher={dispatcher} />, document.querySelector("#main"));
+      roomStore={roomStore} />, document.querySelector("#main"));
 
     document.documentElement.setAttribute("lang", mozL10n.getLanguage());
     document.documentElement.setAttribute("dir", mozL10n.getDirection());
     document.body.setAttribute("platform", loop.shared.utils.getPlatform());
 
     // Notify the window that we've finished initalization and initial layout
     var evtObject = document.createEvent("Event");
     evtObject.initEvent("loopPanelInitialized", true, false);
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -124,18 +124,18 @@ loop.roomViews = (function(mozL10n) {
             React.createElement("i", {className: "icon icon-add-share-service"}), 
             React.createElement("span", null, mozL10n.get("share_add_service_button"))
           ), 
           this.props.socialShareProviders.length ? React.createElement("li", {className: "dropdown-menu-separator"}) : null, 
           
             this.props.socialShareProviders.map(function(provider, idx) {
               return (
                 React.createElement("li", {className: "dropdown-menu-item", 
+                    "data-provider": provider.origin, 
                     key: "provider-" + idx, 
-                    "data-provider": provider.origin, 
                     onClick: this.handleProviderClick}, 
                   React.createElement("img", {className: "icon", src: provider.iconURL}), 
                   React.createElement("span", null, provider.name)
                 )
               );
             }.bind(this))
           
         )
@@ -244,35 +244,35 @@ loop.roomViews = (function(mozL10n) {
               mozL10n.get("email_link_button")
             ), 
             React.createElement("button", {className: "btn btn-info btn-copy", 
                     onClick: this.handleCopyButtonClick}, 
               this.state.copiedUrl ? mozL10n.get("copied_url_button") :
                                       mozL10n.get("copy_url_button2")
             ), 
             React.createElement("button", {className: "btn btn-info btn-share", 
-                    ref: "anchor", 
-                    onClick: this.handleShareButtonClick}, 
+                    onClick: this.handleShareButtonClick, 
+                    ref: "anchor"}, 
               mozL10n.get("share_button3")
             )
           ), 
           React.createElement(SocialShareDropdown, {
             dispatcher: this.props.dispatcher, 
+            ref: "menu", 
             roomUrl: this.props.roomData.roomUrl, 
             show: this.state.showMenu, 
-            socialShareProviders: this.props.socialShareProviders, 
-            ref: "menu"}), 
+            socialShareProviders: this.props.socialShareProviders}), 
           React.createElement(DesktopRoomContextView, {
             dispatcher: this.props.dispatcher, 
             editMode: this.state.editMode, 
             error: this.props.error, 
-            savingContext: this.props.savingContext, 
             mozLoop: this.props.mozLoop, 
             onEditModeChange: this.handleEditModeChange, 
             roomData: this.props.roomData, 
+            savingContext: this.props.savingContext, 
             show: this.props.showContext || this.state.editMode})
         )
       );
     }
   });
 
   var DesktopRoomContextView = React.createClass({displayName: "DesktopRoomContextView",
     mixins: [React.addons.LinkedStateMixin],
@@ -514,28 +514,31 @@ loop.roomViews = (function(mozL10n) {
             React.createElement(sharedViews.Checkbox, {
               additionalClass: cx({ hide: !checkboxLabel }), 
               checked: checked, 
               disabled: checked, 
               label: checkboxLabel, 
               onChange: this.handleCheckboxChange, 
               value: location}), 
             React.createElement("form", {onSubmit: this.handleFormSubmit}, 
-              React.createElement("input", {type: "text", className: "room-context-name", 
+              React.createElement("input", {className: "room-context-name", 
                 onKeyDown: this.handleTextareaKeyDown, 
                 placeholder: mozL10n.get("context_edit_name_placeholder"), 
+                type: "text", 
                 valueLink: this.linkState("newRoomName")}), 
-              React.createElement("input", {type: "text", className: "room-context-url", 
+              React.createElement("input", {className: "room-context-url", 
+                disabled: availableContext && availableContext.url === this.state.newRoomURL, 
                 onKeyDown: this.handleTextareaKeyDown, 
                 placeholder: "https://", 
-                disabled: availableContext && availableContext.url === this.state.newRoomURL, 
+                type: "text", 
                 valueLink: this.linkState("newRoomURL")}), 
-              React.createElement("textarea", {rows: "3", type: "text", className: "room-context-comments", 
+              React.createElement("textarea", {className: "room-context-comments", 
                 onKeyDown: this.handleTextareaKeyDown, 
                 placeholder: mozL10n.get("context_edit_comments_placeholder"), 
+                rows: "3", type: "text", 
                 valueLink: this.linkState("newRoomDescription")})
             ), 
             React.createElement("button", {className: "btn btn-info", 
                     disabled: this.props.savingContext, 
                     onClick: this.handleFormSubmit}, 
               mozL10n.get("context_save_label2")
             ), 
             React.createElement("button", {className: "room-context-btn-close", 
@@ -582,19 +585,19 @@ loop.roomViews = (function(mozL10n) {
       sharedMixins.DocumentTitleMixin,
       sharedMixins.MediaSetupMixin,
       sharedMixins.RoomsAudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      mozLoop: React.PropTypes.object.isRequired,
       // The poster URLs are for UI-showcase testing and development.
       localPosterUrl: React.PropTypes.string,
+      mozLoop: React.PropTypes.object.isRequired,
       remotePosterUrl: React.PropTypes.string
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
       // the sdk wouldn't need to know this, but we can't change that.
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
@@ -764,45 +767,45 @@ loop.roomViews = (function(mozL10n) {
                 showContext: shouldRenderContextView, 
                 socialShareProviders: this.state.socialShareProviders}), 
               React.createElement("div", {className: "video-layout-wrapper"}, 
                 React.createElement("div", {className: "conversation room-conversation"}, 
                   React.createElement("div", {className: "media nested"}, 
                     React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                       React.createElement("div", {className: "video_inner remote focus-stream"}, 
                         React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
-                          posterUrl: this.props.remotePosterUrl, 
                           isLoading: this._shouldRenderRemoteLoading(), 
                           mediaType: "remote", 
+                          posterUrl: this.props.remotePosterUrl, 
                           srcVideoObject: this.state.remoteSrcVideoObject})
                       )
                     ), 
                     React.createElement("div", {className: localStreamClasses}, 
                       React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted, 
-                        posterUrl: this.props.localPosterUrl, 
                         isLoading: this._shouldRenderLocalLoading(), 
                         mediaType: "local", 
+                        posterUrl: this.props.localPosterUrl, 
                         srcVideoObject: this.state.localSrcVideoObject})
                     )
                   ), 
                   React.createElement(sharedViews.ConversationToolbar, {
+                    audio: {enabled: !this.state.audioMuted, visible: true}, 
                     dispatcher: this.props.dispatcher, 
-                    video: {enabled: !this.state.videoMuted, visible: true}, 
-                    audio: {enabled: !this.state.audioMuted, visible: true}, 
+                    hangup: this.leaveRoom, 
                     publishStream: this.publishStream, 
-                    hangup: this.leaveRoom, 
-                    screenShare: screenShareData})
+                    screenShare: screenShareData, 
+                    video: {enabled: !this.state.videoMuted, visible: true}})
                 )
               ), 
               React.createElement(DesktopRoomContextView, {
                 dispatcher: this.props.dispatcher, 
                 error: this.state.error, 
-                savingContext: this.state.savingContext, 
                 mozLoop: this.props.mozLoop, 
                 roomData: roomData, 
+                savingContext: this.state.savingContext, 
                 show: !shouldRenderInvitationOverlay && shouldRenderContextView}), 
               React.createElement(sharedViews.TextChatView, {
                 dispatcher: this.props.dispatcher, 
                 showAlways: false, 
                 showRoomName: false})
             )
           );
         }
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -124,18 +124,18 @@ loop.roomViews = (function(mozL10n) {
             <i className="icon icon-add-share-service"></i>
             <span>{mozL10n.get("share_add_service_button")}</span>
           </li>
           {this.props.socialShareProviders.length ? <li className="dropdown-menu-separator"/> : null}
           {
             this.props.socialShareProviders.map(function(provider, idx) {
               return (
                 <li className="dropdown-menu-item"
+                    data-provider={provider.origin}
                     key={"provider-" + idx}
-                    data-provider={provider.origin}
                     onClick={this.handleProviderClick}>
                   <img className="icon" src={provider.iconURL}/>
                   <span>{provider.name}</span>
                 </li>
               );
             }.bind(this))
           }
         </ul>
@@ -244,35 +244,35 @@ loop.roomViews = (function(mozL10n) {
               {mozL10n.get("email_link_button")}
             </button>
             <button className="btn btn-info btn-copy"
                     onClick={this.handleCopyButtonClick}>
               {this.state.copiedUrl ? mozL10n.get("copied_url_button") :
                                       mozL10n.get("copy_url_button2")}
             </button>
             <button className="btn btn-info btn-share"
-                    ref="anchor"
-                    onClick={this.handleShareButtonClick}>
+                    onClick={this.handleShareButtonClick}
+                    ref="anchor">
               {mozL10n.get("share_button3")}
             </button>
           </div>
           <SocialShareDropdown
             dispatcher={this.props.dispatcher}
+            ref="menu"
             roomUrl={this.props.roomData.roomUrl}
             show={this.state.showMenu}
-            socialShareProviders={this.props.socialShareProviders}
-            ref="menu" />
+            socialShareProviders={this.props.socialShareProviders} />
           <DesktopRoomContextView
             dispatcher={this.props.dispatcher}
             editMode={this.state.editMode}
             error={this.props.error}
-            savingContext={this.props.savingContext}
             mozLoop={this.props.mozLoop}
             onEditModeChange={this.handleEditModeChange}
             roomData={this.props.roomData}
+            savingContext={this.props.savingContext}
             show={this.props.showContext || this.state.editMode} />
         </div>
       );
     }
   });
 
   var DesktopRoomContextView = React.createClass({
     mixins: [React.addons.LinkedStateMixin],
@@ -514,28 +514,31 @@ loop.roomViews = (function(mozL10n) {
             <sharedViews.Checkbox
               additionalClass={cx({ hide: !checkboxLabel })}
               checked={checked}
               disabled={checked}
               label={checkboxLabel}
               onChange={this.handleCheckboxChange}
               value={location} />
             <form onSubmit={this.handleFormSubmit}>
-              <input type="text" className="room-context-name"
+              <input className="room-context-name"
                 onKeyDown={this.handleTextareaKeyDown}
                 placeholder={mozL10n.get("context_edit_name_placeholder")}
+                type="text"
                 valueLink={this.linkState("newRoomName")} />
-              <input type="text" className="room-context-url"
+              <input className="room-context-url"
+                disabled={availableContext && availableContext.url === this.state.newRoomURL}
                 onKeyDown={this.handleTextareaKeyDown}
                 placeholder="https://"
-                disabled={availableContext && availableContext.url === this.state.newRoomURL}
+                type="text"
                 valueLink={this.linkState("newRoomURL")} />
-              <textarea rows="3" type="text" className="room-context-comments"
+              <textarea className="room-context-comments"
                 onKeyDown={this.handleTextareaKeyDown}
                 placeholder={mozL10n.get("context_edit_comments_placeholder")}
+                rows="3" type="text"
                 valueLink={this.linkState("newRoomDescription")} />
             </form>
             <button className="btn btn-info"
                     disabled={this.props.savingContext}
                     onClick={this.handleFormSubmit}>
               {mozL10n.get("context_save_label2")}
             </button>
             <button className="room-context-btn-close"
@@ -582,19 +585,19 @@ loop.roomViews = (function(mozL10n) {
       sharedMixins.DocumentTitleMixin,
       sharedMixins.MediaSetupMixin,
       sharedMixins.RoomsAudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      mozLoop: React.PropTypes.object.isRequired,
       // The poster URLs are for UI-showcase testing and development.
       localPosterUrl: React.PropTypes.string,
+      mozLoop: React.PropTypes.object.isRequired,
       remotePosterUrl: React.PropTypes.string
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
       // the sdk wouldn't need to know this, but we can't change that.
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
@@ -764,45 +767,45 @@ loop.roomViews = (function(mozL10n) {
                 showContext={shouldRenderContextView}
                 socialShareProviders={this.state.socialShareProviders} />
               <div className="video-layout-wrapper">
                 <div className="conversation room-conversation">
                   <div className="media nested">
                     <div className="video_wrapper remote_wrapper">
                       <div className="video_inner remote focus-stream">
                         <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
-                          posterUrl={this.props.remotePosterUrl}
                           isLoading={this._shouldRenderRemoteLoading()}
                           mediaType="remote"
+                          posterUrl={this.props.remotePosterUrl}
                           srcVideoObject={this.state.remoteSrcVideoObject} />
                       </div>
                     </div>
                     <div className={localStreamClasses}>
                       <sharedViews.MediaView displayAvatar={this.state.videoMuted}
-                        posterUrl={this.props.localPosterUrl}
                         isLoading={this._shouldRenderLocalLoading()}
                         mediaType="local"
+                        posterUrl={this.props.localPosterUrl}
                         srcVideoObject={this.state.localSrcVideoObject} />
                     </div>
                   </div>
                   <sharedViews.ConversationToolbar
+                    audio={{enabled: !this.state.audioMuted, visible: true}}
                     dispatcher={this.props.dispatcher}
-                    video={{enabled: !this.state.videoMuted, visible: true}}
-                    audio={{enabled: !this.state.audioMuted, visible: true}}
+                    hangup={this.leaveRoom}
                     publishStream={this.publishStream}
-                    hangup={this.leaveRoom}
-                    screenShare={screenShareData} />
+                    screenShare={screenShareData}
+                    video={{enabled: !this.state.videoMuted, visible: true}} />
                 </div>
               </div>
               <DesktopRoomContextView
                 dispatcher={this.props.dispatcher}
                 error={this.state.error}
-                savingContext={this.state.savingContext}
                 mozLoop={this.props.mozLoop}
                 roomData={roomData}
+                savingContext={this.state.savingContext}
                 show={!shouldRenderInvitationOverlay && shouldRenderContextView} />
               <sharedViews.TextChatView
                 dispatcher={this.props.dispatcher}
                 showAlways={false}
                 showRoomName={false} />
             </div>
           );
         }
--- a/browser/components/loop/content/shared/js/feedbackViews.js
+++ b/browser/components/loop/content/shared/js/feedbackViews.js
@@ -19,26 +19,27 @@ loop.shared.views.FeedbackView = (functi
    * Feedback outer layout.
    *
    * Props:
    * -
    */
   var FeedbackLayout = React.createClass({displayName: "FeedbackLayout",
     propTypes: {
       children: React.PropTypes.component.isRequired,
-      title: React.PropTypes.string.isRequired,
-      reset: React.PropTypes.func // if not specified, no Back btn is shown
+      reset: React.PropTypes.func, // if not specified, no Back btn is shown
+      title: React.PropTypes.string.isRequired
     },
 
     render: function() {
       var backButton = React.createElement("div", null);
       if (this.props.reset) {
         backButton = (
-          React.createElement("button", {className: "fx-embedded-btn-back", type: "button", 
-                  onClick: this.props.reset}, 
+          React.createElement("button", {className: "fx-embedded-btn-back", 
+            onClick: this.props.reset, 
+            type: "button"}, 
             "« ", l10n.get("feedback_back_button")
           )
         );
       }
       return (
         React.createElement("div", {className: "feedback"}, 
           backButton, 
           React.createElement("h3", null, this.props.title), 
@@ -75,22 +76,25 @@ loop.shared.views.FeedbackView = (functi
         other: l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
-          React.createElement("label", {key: key, className: "feedback-category-label"}, 
-            React.createElement("input", {type: "radio", ref: "category", name: "category", 
-                   className: "feedback-category-radio", 
-                   value: category, 
-                   onChange: this.handleCategoryChange, 
-                   checked: this.state.category === category}), 
+          React.createElement("label", {className: "feedback-category-label", key: key}, 
+            React.createElement("input", {
+              checked: this.state.category === category, 
+              className: "feedback-category-radio", 
+              name: "category", 
+              onChange: this.handleCategoryChange, 
+              ref: "category", 
+              type: "radio", 
+              value: category}), 
             categories[category]
           )
         );
       }, this);
     },
 
     /**
      * Checks if the form is ready for submission:
@@ -133,30 +137,34 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        React.createElement(FeedbackLayout, {title: l10n.get("feedback_category_list_heading"), 
-                        reset: this.props.reset}, 
+        React.createElement(FeedbackLayout, {
+          reset: this.props.reset, 
+          title: l10n.get("feedback_category_list_heading")}, 
           React.createElement("form", {onSubmit: this.handleFormSubmit}, 
             this._getCategoryFields(), 
             React.createElement("p", null, 
-              React.createElement("input", {type: "text", ref: "description", name: "description", 
-                className: "feedback-description", 
+              React.createElement("input", {className: "feedback-description", 
+                name: "description", 
                 onChange: this.handleDescriptionFieldChange, 
-                value: this.state.description, 
                 placeholder: 
-                  l10n.get("feedback_custom_category_text_placeholder")})
+                  l10n.get("feedback_custom_category_text_placeholder"), 
+                ref: "description", 
+                type: "text", 
+                value: this.state.description})
             ), 
-            React.createElement("button", {type: "submit", className: "btn btn-success", 
-                    disabled: !this._isFormReady()}, 
+            React.createElement("button", {className: "btn btn-success", 
+              disabled: !this._isFormReady(), 
+              type: "submit"}, 
               l10n.get("feedback_submit_button")
             )
           )
         )
       );
     }
   });
 
@@ -224,20 +232,20 @@ loop.shared.views.FeedbackView = (functi
    */
   var FeedbackView = React.createClass({displayName: "FeedbackView",
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("feedbackStore")
     ],
 
     propTypes: {
-      onAfterFeedbackReceived: React.PropTypes.func,
       // Used by the UI showcase.
       feedbackState: React.PropTypes.string,
-      noCloseText: React.PropTypes.bool
+      noCloseText: React.PropTypes.bool,
+      onAfterFeedbackReceived: React.PropTypes.func
     },
 
     getInitialState: function() {
       var storeState = this.getStoreState();
       return _.extend({}, storeState, {
         feedbackState: this.props.feedbackState || storeState.feedbackState
       });
     },
@@ -284,18 +292,18 @@ loop.shared.views.FeedbackView = (functi
               )
             )
           );
         }
         case FEEDBACK_STATES.DETAILS: {
           return (
             React.createElement(FeedbackForm, {
               feedbackStore: this.getStore(), 
-              reset: this.reset, 
-              pending: this.state.feedbackState === FEEDBACK_STATES.PENDING})
+              pending: this.state.feedbackState === FEEDBACK_STATES.PENDING, 
+              reset: this.reset})
             );
         }
         case FEEDBACK_STATES.PENDING:
         case FEEDBACK_STATES.SENT:
         case FEEDBACK_STATES.FAILED: {
           if (this.state.error) {
             // XXX better end user error reporting, see bug 1046738
             console.error("Error encountered while submitting feedback",
--- a/browser/components/loop/content/shared/js/feedbackViews.jsx
+++ b/browser/components/loop/content/shared/js/feedbackViews.jsx
@@ -19,26 +19,27 @@ loop.shared.views.FeedbackView = (functi
    * Feedback outer layout.
    *
    * Props:
    * -
    */
   var FeedbackLayout = React.createClass({
     propTypes: {
       children: React.PropTypes.component.isRequired,
-      title: React.PropTypes.string.isRequired,
-      reset: React.PropTypes.func // if not specified, no Back btn is shown
+      reset: React.PropTypes.func, // if not specified, no Back btn is shown
+      title: React.PropTypes.string.isRequired
     },
 
     render: function() {
       var backButton = <div />;
       if (this.props.reset) {
         backButton = (
-          <button className="fx-embedded-btn-back" type="button"
-                  onClick={this.props.reset}>
+          <button className="fx-embedded-btn-back"
+            onClick={this.props.reset}
+            type="button" >
             &laquo;&nbsp;{l10n.get("feedback_back_button")}
           </button>
         );
       }
       return (
         <div className="feedback">
           {backButton}
           <h3>{this.props.title}</h3>
@@ -75,22 +76,25 @@ loop.shared.views.FeedbackView = (functi
         other: l10n.get("feedback_category_other2")
       };
     },
 
     _getCategoryFields: function() {
       var categories = this._getCategories();
       return Object.keys(categories).map(function(category, key) {
         return (
-          <label key={key} className="feedback-category-label">
-            <input type="radio" ref="category" name="category"
-                   className="feedback-category-radio"
-                   value={category}
-                   onChange={this.handleCategoryChange}
-                   checked={this.state.category === category} />
+          <label className="feedback-category-label" key={key}>
+            <input
+              checked={this.state.category === category}
+              className="feedback-category-radio"
+              name="category"
+              onChange={this.handleCategoryChange}
+              ref="category"
+              type="radio"
+              value={category} />
             {categories[category]}
           </label>
         );
       }, this);
     },
 
     /**
      * Checks if the form is ready for submission:
@@ -133,30 +137,34 @@ loop.shared.views.FeedbackView = (functi
         happy: false,
         category: this.state.category,
         description: this.state.description
       }));
     },
 
     render: function() {
       return (
-        <FeedbackLayout title={l10n.get("feedback_category_list_heading")}
-                        reset={this.props.reset}>
+        <FeedbackLayout
+          reset={this.props.reset}
+          title={l10n.get("feedback_category_list_heading")}>
           <form onSubmit={this.handleFormSubmit}>
             {this._getCategoryFields()}
             <p>
-              <input type="text" ref="description" name="description"
-                className="feedback-description"
+              <input className="feedback-description"
+                name="description"
                 onChange={this.handleDescriptionFieldChange}
-                value={this.state.description}
                 placeholder={
-                  l10n.get("feedback_custom_category_text_placeholder")} />
+                  l10n.get("feedback_custom_category_text_placeholder")}
+                ref="description"
+                type="text"
+                value={this.state.description} />
             </p>
-            <button type="submit" className="btn btn-success"
-                    disabled={!this._isFormReady()}>
+            <button className="btn btn-success"
+              disabled={!this._isFormReady()}
+              type="submit">
               {l10n.get("feedback_submit_button")}
             </button>
           </form>
         </FeedbackLayout>
       );
     }
   });
 
@@ -224,20 +232,20 @@ loop.shared.views.FeedbackView = (functi
    */
   var FeedbackView = React.createClass({
     mixins: [
       Backbone.Events,
       loop.store.StoreMixin("feedbackStore")
     ],
 
     propTypes: {
-      onAfterFeedbackReceived: React.PropTypes.func,
       // Used by the UI showcase.
       feedbackState: React.PropTypes.string,
-      noCloseText: React.PropTypes.bool
+      noCloseText: React.PropTypes.bool,
+      onAfterFeedbackReceived: React.PropTypes.func
     },
 
     getInitialState: function() {
       var storeState = this.getStoreState();
       return _.extend({}, storeState, {
         feedbackState: this.props.feedbackState || storeState.feedbackState
       });
     },
@@ -284,18 +292,18 @@ loop.shared.views.FeedbackView = (functi
               </div>
             </FeedbackLayout>
           );
         }
         case FEEDBACK_STATES.DETAILS: {
           return (
             <FeedbackForm
               feedbackStore={this.getStore()}
-              reset={this.reset}
-              pending={this.state.feedbackState === FEEDBACK_STATES.PENDING} />
+              pending={this.state.feedbackState === FEEDBACK_STATES.PENDING}
+              reset={this.reset} />
             );
         }
         case FEEDBACK_STATES.PENDING:
         case FEEDBACK_STATES.SENT:
         case FEEDBACK_STATES.FAILED: {
           if (this.state.error) {
             // XXX better end user error reporting, see bug 1046738
             console.error("Error encountered while submitting feedback",
--- a/browser/components/loop/content/shared/js/textChatView.js
+++ b/browser/components/loop/content/shared/js/textChatView.js
@@ -102,17 +102,17 @@ loop.shared.views.TextChatView = (functi
             
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
                       return React.createElement(TextChatRoomName, {key: i, message: entry.message});
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
-                        React.createElement("div", {key: i, className: "context-url-view-wrapper"}, 
+                        React.createElement("div", {className: "context-url-view-wrapper", key: i}, 
                           React.createElement(sharedViews.ContextUrlView, {
                             allowClick: true, 
                             description: entry.message, 
                             dispatcher: this.props.dispatcher, 
                             showContextTitle: true, 
                             thumbnail: entry.extraData.thumbnail, 
                             url: entry.extraData.location, 
                             useDesktopPaths: false})
@@ -120,20 +120,21 @@ loop.shared.views.TextChatView = (functi
                       );
                     default:
                       console.error("Unsupported contentType", entry.contentType);
                       return null;
                   }
                 }
 
                 return (
-                  React.createElement(TextChatEntry, {key: i, 
-                                 contentType: entry.contentType, 
-                                 message: entry.message, 
-                                 type: entry.type})
+                  React.createElement(TextChatEntry, {
+                    contentType: entry.contentType, 
+                    key: i, 
+                    message: entry.message, 
+                    type: entry.type})
                 );
               }, this)
             
           )
         )
       );
     }
   });
@@ -201,20 +202,21 @@ loop.shared.views.TextChatView = (functi
     render: function() {
       if (!this.props.textChatEnabled) {
         return null;
       }
 
       return (
         React.createElement("div", {className: "text-chat-box"}, 
           React.createElement("form", {onSubmit: this.handleFormSubmit}, 
-            React.createElement("input", {type: "text", 
-                   placeholder: this.props.showPlaceholder ? mozL10n.get("chat_textbox_placeholder") : "", 
-                   onKeyDown: this.handleKeyDown, 
-                   valueLink: this.linkState("messageDetail")})
+            React.createElement("input", {
+              onKeyDown: this.handleKeyDown, 
+              placeholder: this.props.showPlaceholder ? mozL10n.get("chat_textbox_placeholder") : "", 
+              type: "text", 
+              valueLink: this.linkState("messageDetail")})
           )
         )
       );
     }
   });
 
   /**
    * Displays the text chat view. This includes the text chat messages as well
--- a/browser/components/loop/content/shared/js/textChatView.jsx
+++ b/browser/components/loop/content/shared/js/textChatView.jsx
@@ -102,17 +102,17 @@ loop.shared.views.TextChatView = (functi
             {
               this.props.messageList.map(function(entry, i) {
                 if (entry.type === CHAT_MESSAGE_TYPES.SPECIAL) {
                   switch (entry.contentType) {
                     case CHAT_CONTENT_TYPES.ROOM_NAME:
                       return <TextChatRoomName key={i} message={entry.message}/>;
                     case CHAT_CONTENT_TYPES.CONTEXT:
                       return (
-                        <div key={i} className="context-url-view-wrapper">
+                        <div className="context-url-view-wrapper" key={i}>
                           <sharedViews.ContextUrlView
                             allowClick={true}
                             description={entry.message}
                             dispatcher={this.props.dispatcher}
                             showContextTitle={true}
                             thumbnail={entry.extraData.thumbnail}
                             url={entry.extraData.location}
                             useDesktopPaths={false} />
@@ -120,20 +120,21 @@ loop.shared.views.TextChatView = (functi
                       );
                     default:
                       console.error("Unsupported contentType", entry.contentType);
                       return null;
                   }
                 }
 
                 return (
-                  <TextChatEntry key={i}
-                                 contentType={entry.contentType}
-                                 message={entry.message}
-                                 type={entry.type} />
+                  <TextChatEntry
+                    contentType={entry.contentType}
+                    key={i}
+                    message={entry.message}
+                    type={entry.type} />
                 );
               }, this)
             }
           </div>
         </div>
       );
     }
   });
@@ -201,20 +202,21 @@ loop.shared.views.TextChatView = (functi
     render: function() {
       if (!this.props.textChatEnabled) {
         return null;
       }
 
       return (
         <div className="text-chat-box">
           <form onSubmit={this.handleFormSubmit}>
-            <input type="text"
-                   placeholder={this.props.showPlaceholder ? mozL10n.get("chat_textbox_placeholder") : ""}
-                   onKeyDown={this.handleKeyDown}
-                   valueLink={this.linkState("messageDetail")} />
+            <input
+              onKeyDown={this.handleKeyDown}
+              placeholder={this.props.showPlaceholder ? mozL10n.get("chat_textbox_placeholder") : ""}
+              type="text"
+              valueLink={this.linkState("messageDetail")} />
           </form>
         </div>
       );
     }
   });
 
   /**
    * Displays the text chat view. This includes the text chat messages as well
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -18,20 +18,20 @@ loop.shared.views = (function(_, l10n) {
    * Required props:
    * - {String}   scope   Media scope, can be "local" or "remote".
    * - {String}   type    Media type, can be "audio" or "video".
    * - {Function} action  Function to be executed on click.
    * - {Enabled}  enabled Stream activation status (default: true).
    */
   var MediaControlButton = React.createClass({displayName: "MediaControlButton",
     propTypes: {
+      action: React.PropTypes.func.isRequired,
+      enabled: React.PropTypes.bool.isRequired,
       scope: React.PropTypes.string.isRequired,
       type: React.PropTypes.string.isRequired,
-      action: React.PropTypes.func.isRequired,
-      enabled: React.PropTypes.bool.isRequired,
       visible: React.PropTypes.bool.isRequired
     },
 
     getDefaultProps: function() {
       return {enabled: true, visible: true};
     },
 
     handleClick: function() {
@@ -58,18 +58,18 @@ loop.shared.views = (function(_, l10n) {
       var suffix = "button_title";
       var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
       return l10n.get(msgId);
     },
 
     render: function() {
       return (
         React.createElement("button", {className: this._getClasses(), 
-                title: this._getTitle(), 
-                onClick: this.handleClick})
+                onClick: this.handleClick, 
+                title: this._getTitle()})
       );
     }
   });
 
   /**
    * Screen sharing control button.
    *
    * Required props:
@@ -78,18 +78,18 @@ loop.shared.views = (function(_, l10n) {
    * - {String}          state       One of the screen sharing states, see
    *                                 loop.shared.utils.SCREEN_SHARE_STATES
    */
   var ScreenShareControlButton = React.createClass({displayName: "ScreenShareControlButton",
     mixins: [sharedMixins.DropdownMenuMixin()],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      visible: React.PropTypes.bool.isRequired,
-      state: React.PropTypes.string.isRequired
+      state: React.PropTypes.string.isRequired,
+      visible: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       var os = loop.shared.utils.getOS();
       var osVersion = loop.shared.utils.getOSVersion();
       // Disable screensharing on older OSX and Windows versions.
       if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
           (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
@@ -157,21 +157,21 @@ loop.shared.views = (function(_, l10n) {
       return (
         React.createElement("div", null, 
           React.createElement("button", {className: screenShareClasses, 
                   onClick: this.handleClick, 
                   ref: "menu-button", 
                   title: this._getTitle()}, 
             isActive ? null : React.createElement("span", {className: "chevron"})
           ), 
-          React.createElement("ul", {ref: "menu", className: dropdownMenuClasses}, 
+          React.createElement("ul", {className: dropdownMenuClasses, ref: "menu"}, 
             React.createElement("li", {onClick: this._handleShareTabs}, 
               l10n.get("share_tabs_button_title2")
             ), 
-            React.createElement("li", {onClick: this._handleShareWindows, className: windowSharingClasses}, 
+            React.createElement("li", {className: windowSharingClasses, onClick: this._handleShareWindows}, 
               l10n.get("share_windows_button_title")
             )
           )
         )
       );
     }
   });
 
@@ -184,24 +184,24 @@ loop.shared.views = (function(_, l10n) {
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true},
         screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
         enableHangup: true
       };
     },
 
     propTypes: {
+      audio: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      video: React.PropTypes.object.isRequired,
-      audio: React.PropTypes.object.isRequired,
+      enableHangup: React.PropTypes.bool,
+      hangup: React.PropTypes.func.isRequired,
+      hangupButtonLabel: React.PropTypes.string,
+      publishStream: React.PropTypes.func.isRequired,
       screenShare: React.PropTypes.object,
-      hangup: React.PropTypes.func.isRequired,
-      publishStream: React.PropTypes.func.isRequired,
-      hangupButtonLabel: React.PropTypes.string,
-      enableHangup: React.PropTypes.bool
+      video: React.PropTypes.object.isRequired
     },
 
     handleClickHangup: function() {
       this.props.hangup();
     },
 
     handleToggleVideo: function() {
       this.props.publishStream("video", !this.props.video.enabled);
@@ -214,38 +214,39 @@ loop.shared.views = (function(_, l10n) {
     _getHangupButtonLabel: function() {
       return this.props.hangupButtonLabel || l10n.get("hangup_button_caption2");
     },
 
     render: function() {
       return (
         React.createElement("ul", {className: "conversation-toolbar"}, 
           React.createElement("li", {className: "conversation-toolbar-btn-box btn-hangup-entry"}, 
-            React.createElement("button", {className: "btn btn-hangup", onClick: this.handleClickHangup, 
-                    title: l10n.get("hangup_button_title"), 
-                    disabled: !this.props.enableHangup}, 
+            React.createElement("button", {className: "btn btn-hangup", 
+                    disabled: !this.props.enableHangup, 
+                    onClick: this.handleClickHangup, 
+                    title: l10n.get("hangup_button_title")}, 
               this._getHangupButtonLabel()
             )
           ), 
           React.createElement("li", {className: "conversation-toolbar-btn-box"}, 
             React.createElement(MediaControlButton, {action: this.handleToggleVideo, 
                                 enabled: this.props.video.enabled, 
-                                visible: this.props.video.visible, 
-                                scope: "local", type: "video"})
+                                scope: "local", type: "video", 
+                                visible: this.props.video.visible})
           ), 
           React.createElement("li", {className: "conversation-toolbar-btn-box"}, 
             React.createElement(MediaControlButton, {action: this.handleToggleAudio, 
                                 enabled: this.props.audio.enabled, 
-                                visible: this.props.audio.visible, 
-                                scope: "local", type: "audio"})
+                                scope: "local", type: "audio", 
+                                visible: this.props.audio.visible})
           ), 
           React.createElement("li", {className: "conversation-toolbar-btn-box btn-screen-share-entry"}, 
             React.createElement(ScreenShareControlButton, {dispatcher: this.props.dispatcher, 
-                                      visible: this.props.screenShare.visible, 
-                                      state: this.props.screenShare.state})
+                                      state: this.props.screenShare.state, 
+                                      visible: this.props.screenShare.visible})
           )
         )
       );
     }
   });
 
   /**
    * Conversation view.
@@ -253,21 +254,21 @@ loop.shared.views = (function(_, l10n) {
   var ConversationView = React.createClass({displayName: "ConversationView",
     mixins: [
       Backbone.Events,
       sharedMixins.AudioMixin,
       sharedMixins.MediaSetupMixin
     ],
 
     propTypes: {
-      sdk: React.PropTypes.object.isRequired,
-      video: React.PropTypes.object,
       audio: React.PropTypes.object,
       initiate: React.PropTypes.bool,
-      isDesktop: React.PropTypes.bool
+      isDesktop: React.PropTypes.bool,
+      sdk: React.PropTypes.object.isRequired,
+      video: React.PropTypes.object
     },
 
     getDefaultProps: function() {
       return {
         initiate: true,
         isDesktop: false,
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true}
@@ -450,68 +451,69 @@ loop.shared.views = (function(_, l10n) {
         React.createElement("div", {className: "video-layout-wrapper"}, 
           React.createElement("div", {className: "conversation in-call"}, 
             React.createElement("div", {className: "media nested"}, 
               React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                 React.createElement("div", {className: "video_inner remote focus-stream"})
               ), 
               React.createElement("div", {className: localStreamClasses})
             ), 
-            React.createElement(ConversationToolbar, {video: this.state.video, 
-                                 audio: this.state.audio, 
-                                 publishStream: this.publishStream, 
-                                 hangup: this.hangup})
+            React.createElement(ConversationToolbar, {
+              audio: this.state.audio, 
+              hangup: this.hangup, 
+              publishStream: this.publishStream, 
+              video: this.state.video})
           )
         )
       );
     }
   });
 
   /**
    * Notification view.
    */
   var NotificationView = React.createClass({displayName: "NotificationView",
     mixins: [Backbone.Events],
 
     propTypes: {
-      notification: React.PropTypes.object.isRequired,
-      key: React.PropTypes.number.isRequired
+      key: React.PropTypes.number.isRequired,
+      notification: React.PropTypes.object.isRequired
     },
 
     render: function() {
       var notification = this.props.notification;
       return (
         React.createElement("div", {className: "notificationContainer"}, 
-          React.createElement("div", {key: this.props.key, 
-               className: "alert alert-" + notification.get("level")}, 
+          React.createElement("div", {className: "alert alert-" + notification.get("level"), 
+            key: this.props.key}, 
             React.createElement("span", {className: "message"}, notification.get("message"))
           ), 
           React.createElement("div", {className: "detailsBar details-" + notification.get("level"), 
                hidden: !notification.get("details")}, 
             React.createElement("button", {className: "detailsButton btn-info", 
-                    onClick: notification.get("detailsButtonCallback"), 
-                    hidden: !notification.get("detailsButtonLabel") || !notification.get("detailsButtonCallback")}, 
+                    hidden: !notification.get("detailsButtonLabel") || !notification.get("detailsButtonCallback"), 
+                    onClick: notification.get("detailsButtonCallback")}, 
               notification.get("detailsButtonLabel")
             ), 
             React.createElement("span", {className: "details"}, notification.get("details"))
           )
         )
       );
     }
   });
 
   /**
    * Notification list view.
    */
   var NotificationListView = React.createClass({displayName: "NotificationListView",
     mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
 
     propTypes: {
-      notifications: React.PropTypes.object.isRequired,
-      clearOnDocumentHidden: React.PropTypes.bool
+      clearOnDocumentHidden: React.PropTypes.bool,
+      notifications: React.PropTypes.object.isRequired
     },
 
     getDefaultProps: function() {
       return {clearOnDocumentHidden: false};
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
@@ -548,21 +550,21 @@ loop.shared.views = (function(_, l10n) {
         
         )
       );
     }
   });
 
   var Button = React.createClass({displayName: "Button",
     propTypes: {
+      additionalClass: React.PropTypes.string,
       caption: React.PropTypes.string.isRequired,
-      onClick: React.PropTypes.func.isRequired,
       disabled: React.PropTypes.bool,
-      additionalClass: React.PropTypes.string,
-      htmlId: React.PropTypes.string
+      htmlId: React.PropTypes.string,
+      onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {
         disabled: false,
         additionalClass: "",
         htmlId: ""
       };
@@ -570,20 +572,20 @@ loop.shared.views = (function(_, l10n) {
 
     render: function() {
       var cx = React.addons.classSet;
       var classObject = { button: true, disabled: this.props.disabled };
       if (this.props.additionalClass) {
         classObject[this.props.additionalClass] = true;
       }
       return (
-        React.createElement("button", {onClick: this.props.onClick, 
+        React.createElement("button", {className: cx(classObject), 
                 disabled: this.props.disabled, 
                 id: this.props.htmlId, 
-                className: cx(classObject)}, 
+                onClick: this.props.onClick}, 
           React.createElement("span", {className: "button-caption"}, this.props.caption), 
           this.props.children
         )
       );
     }
   });
 
   var ButtonGroup = React.createClass({displayName: "ButtonGroup",
@@ -786,18 +788,18 @@ loop.shared.views = (function(_, l10n) {
       return (
         React.createElement("div", {className: "context-content"}, 
           this.renderContextTitle(), 
           React.createElement("div", {className: "context-wrapper"}, 
             React.createElement("img", {className: "context-preview", src: thumbnail}), 
             React.createElement("span", {className: "context-description"}, 
               this.props.description, 
               React.createElement("a", {className: "context-url", 
+                 href: this.props.allowClick ? this.props.url : null, 
                  onClick: this.handleLinkClick, 
-                 href: this.props.allowClick ? this.props.url : null, 
                  rel: "noreferrer", 
                  target: "_blank"}, hostname)
             )
           )
         )
       );
     }
   });
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -18,20 +18,20 @@ loop.shared.views = (function(_, l10n) {
    * Required props:
    * - {String}   scope   Media scope, can be "local" or "remote".
    * - {String}   type    Media type, can be "audio" or "video".
    * - {Function} action  Function to be executed on click.
    * - {Enabled}  enabled Stream activation status (default: true).
    */
   var MediaControlButton = React.createClass({
     propTypes: {
+      action: React.PropTypes.func.isRequired,
+      enabled: React.PropTypes.bool.isRequired,
       scope: React.PropTypes.string.isRequired,
       type: React.PropTypes.string.isRequired,
-      action: React.PropTypes.func.isRequired,
-      enabled: React.PropTypes.bool.isRequired,
       visible: React.PropTypes.bool.isRequired
     },
 
     getDefaultProps: function() {
       return {enabled: true, visible: true};
     },
 
     handleClick: function() {
@@ -58,18 +58,18 @@ loop.shared.views = (function(_, l10n) {
       var suffix = "button_title";
       var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
       return l10n.get(msgId);
     },
 
     render: function() {
       return (
         <button className={this._getClasses()}
-                title={this._getTitle()}
-                onClick={this.handleClick}></button>
+                onClick={this.handleClick}
+                title={this._getTitle()}></button>
       );
     }
   });
 
   /**
    * Screen sharing control button.
    *
    * Required props:
@@ -78,18 +78,18 @@ loop.shared.views = (function(_, l10n) {
    * - {String}          state       One of the screen sharing states, see
    *                                 loop.shared.utils.SCREEN_SHARE_STATES
    */
   var ScreenShareControlButton = React.createClass({
     mixins: [sharedMixins.DropdownMenuMixin()],
 
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      visible: React.PropTypes.bool.isRequired,
-      state: React.PropTypes.string.isRequired
+      state: React.PropTypes.string.isRequired,
+      visible: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       var os = loop.shared.utils.getOS();
       var osVersion = loop.shared.utils.getOSVersion();
       // Disable screensharing on older OSX and Windows versions.
       if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
           (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
@@ -157,21 +157,21 @@ loop.shared.views = (function(_, l10n) {
       return (
         <div>
           <button className={screenShareClasses}
                   onClick={this.handleClick}
                   ref="menu-button"
                   title={this._getTitle()}>
             {isActive ? null : <span className="chevron"/>}
           </button>
-          <ul ref="menu" className={dropdownMenuClasses}>
+          <ul className={dropdownMenuClasses} ref="menu">
             <li onClick={this._handleShareTabs}>
               {l10n.get("share_tabs_button_title2")}
             </li>
-            <li onClick={this._handleShareWindows} className={windowSharingClasses}>
+            <li className={windowSharingClasses} onClick={this._handleShareWindows}>
               {l10n.get("share_windows_button_title")}
             </li>
           </ul>
         </div>
       );
     }
   });
 
@@ -184,24 +184,24 @@ loop.shared.views = (function(_, l10n) {
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true},
         screenShare: {state: SCREEN_SHARE_STATES.INACTIVE, visible: false},
         enableHangup: true
       };
     },
 
     propTypes: {
+      audio: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      video: React.PropTypes.object.isRequired,
-      audio: React.PropTypes.object.isRequired,
+      enableHangup: React.PropTypes.bool,
+      hangup: React.PropTypes.func.isRequired,
+      hangupButtonLabel: React.PropTypes.string,
+      publishStream: React.PropTypes.func.isRequired,
       screenShare: React.PropTypes.object,
-      hangup: React.PropTypes.func.isRequired,
-      publishStream: React.PropTypes.func.isRequired,
-      hangupButtonLabel: React.PropTypes.string,
-      enableHangup: React.PropTypes.bool
+      video: React.PropTypes.object.isRequired
     },
 
     handleClickHangup: function() {
       this.props.hangup();
     },
 
     handleToggleVideo: function() {
       this.props.publishStream("video", !this.props.video.enabled);
@@ -214,38 +214,39 @@ loop.shared.views = (function(_, l10n) {
     _getHangupButtonLabel: function() {
       return this.props.hangupButtonLabel || l10n.get("hangup_button_caption2");
     },
 
     render: function() {
       return (
         <ul className="conversation-toolbar">
           <li className="conversation-toolbar-btn-box btn-hangup-entry">
-            <button className="btn btn-hangup" onClick={this.handleClickHangup}
-                    title={l10n.get("hangup_button_title")}
-                    disabled={!this.props.enableHangup}>
+            <button className="btn btn-hangup"
+                    disabled={!this.props.enableHangup}
+                    onClick={this.handleClickHangup}
+                    title={l10n.get("hangup_button_title")}>
               {this._getHangupButtonLabel()}
             </button>
           </li>
           <li className="conversation-toolbar-btn-box">
             <MediaControlButton action={this.handleToggleVideo}
                                 enabled={this.props.video.enabled}
-                                visible={this.props.video.visible}
-                                scope="local" type="video" />
+                                scope="local" type="video"
+                                visible={this.props.video.visible} />
           </li>
           <li className="conversation-toolbar-btn-box">
             <MediaControlButton action={this.handleToggleAudio}
                                 enabled={this.props.audio.enabled}
-                                visible={this.props.audio.visible}
-                                scope="local" type="audio" />
+                                scope="local" type="audio"
+                                visible={this.props.audio.visible} />
           </li>
           <li className="conversation-toolbar-btn-box btn-screen-share-entry">
             <ScreenShareControlButton dispatcher={this.props.dispatcher}
-                                      visible={this.props.screenShare.visible}
-                                      state={this.props.screenShare.state} />
+                                      state={this.props.screenShare.state}
+                                      visible={this.props.screenShare.visible} />
           </li>
         </ul>
       );
     }
   });
 
   /**
    * Conversation view.
@@ -253,21 +254,21 @@ loop.shared.views = (function(_, l10n) {
   var ConversationView = React.createClass({
     mixins: [
       Backbone.Events,
       sharedMixins.AudioMixin,
       sharedMixins.MediaSetupMixin
     ],
 
     propTypes: {
-      sdk: React.PropTypes.object.isRequired,
-      video: React.PropTypes.object,
       audio: React.PropTypes.object,
       initiate: React.PropTypes.bool,
-      isDesktop: React.PropTypes.bool
+      isDesktop: React.PropTypes.bool,
+      sdk: React.PropTypes.object.isRequired,
+      video: React.PropTypes.object
     },
 
     getDefaultProps: function() {
       return {
         initiate: true,
         isDesktop: false,
         video: {enabled: true, visible: true},
         audio: {enabled: true, visible: true}
@@ -450,68 +451,69 @@ loop.shared.views = (function(_, l10n) {
         <div className="video-layout-wrapper">
           <div className="conversation in-call">
             <div className="media nested">
               <div className="video_wrapper remote_wrapper">
                 <div className="video_inner remote focus-stream"></div>
               </div>
               <div className={localStreamClasses}></div>
             </div>
-            <ConversationToolbar video={this.state.video}
-                                 audio={this.state.audio}
-                                 publishStream={this.publishStream}
-                                 hangup={this.hangup} />
+            <ConversationToolbar
+              audio={this.state.audio}
+              hangup={this.hangup}
+              publishStream={this.publishStream}
+              video={this.state.video} />
           </div>
         </div>
       );
     }
   });
 
   /**
    * Notification view.
    */
   var NotificationView = React.createClass({
     mixins: [Backbone.Events],
 
     propTypes: {
-      notification: React.PropTypes.object.isRequired,
-      key: React.PropTypes.number.isRequired
+      key: React.PropTypes.number.isRequired,
+      notification: React.PropTypes.object.isRequired
     },
 
     render: function() {
       var notification = this.props.notification;
       return (
         <div className="notificationContainer">
-          <div key={this.props.key}
-               className={"alert alert-" + notification.get("level")}>
+          <div className={"alert alert-" + notification.get("level")}
+            key={this.props.key}>
             <span className="message">{notification.get("message")}</span>
           </div>
           <div className={"detailsBar details-" + notification.get("level")}
                hidden={!notification.get("details")}>
             <button className="detailsButton btn-info"
-                    onClick={notification.get("detailsButtonCallback")}
-                    hidden={!notification.get("detailsButtonLabel") || !notification.get("detailsButtonCallback")}>
+                    hidden={!notification.get("detailsButtonLabel") || !notification.get("detailsButtonCallback")}
+                    onClick={notification.get("detailsButtonCallback")}>
               {notification.get("detailsButtonLabel")}
             </button>
             <span className="details">{notification.get("details")}</span>
           </div>
         </div>
       );
     }
   });
 
   /**
    * Notification list view.
    */
   var NotificationListView = React.createClass({
     mixins: [Backbone.Events, sharedMixins.DocumentVisibilityMixin],
 
     propTypes: {
-      notifications: React.PropTypes.object.isRequired,
-      clearOnDocumentHidden: React.PropTypes.bool
+      clearOnDocumentHidden: React.PropTypes.bool,
+      notifications: React.PropTypes.object.isRequired
     },
 
     getDefaultProps: function() {
       return {clearOnDocumentHidden: false};
     },
 
     componentDidMount: function() {
       this.listenTo(this.props.notifications, "reset add remove", function() {
@@ -548,21 +550,21 @@ loop.shared.views = (function(_, l10n) {
         }
         </div>
       );
     }
   });
 
   var Button = React.createClass({
     propTypes: {
+      additionalClass: React.PropTypes.string,
       caption: React.PropTypes.string.isRequired,
-      onClick: React.PropTypes.func.isRequired,
       disabled: React.PropTypes.bool,
-      additionalClass: React.PropTypes.string,
-      htmlId: React.PropTypes.string
+      htmlId: React.PropTypes.string,
+      onClick: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {
         disabled: false,
         additionalClass: "",
         htmlId: ""
       };
@@ -570,20 +572,20 @@ loop.shared.views = (function(_, l10n) {
 
     render: function() {
       var cx = React.addons.classSet;
       var classObject = { button: true, disabled: this.props.disabled };
       if (this.props.additionalClass) {
         classObject[this.props.additionalClass] = true;
       }
       return (
-        <button onClick={this.props.onClick}
+        <button className={cx(classObject)}
                 disabled={this.props.disabled}
                 id={this.props.htmlId}
-                className={cx(classObject)}>
+                onClick={this.props.onClick}>
           <span className="button-caption">{this.props.caption}</span>
           {this.props.children}
         </button>
       );
     }
   });
 
   var ButtonGroup = React.createClass({
@@ -786,18 +788,18 @@ loop.shared.views = (function(_, l10n) {
       return (
         <div className="context-content">
           {this.renderContextTitle()}
           <div className="context-wrapper">
             <img className="context-preview" src={thumbnail} />
             <span className="context-description">
               {this.props.description}
               <a className="context-url"
+                 href={this.props.allowClick ? this.props.url : null}
                  onClick={this.handleLinkClick}
-                 href={this.props.allowClick ? this.props.url : null}
                  rel="noreferrer"
                  target="_blank">{hostname}</a>
             </span>
           </div>
         </div>
       );
     }
   });
--- a/browser/components/loop/standalone/content/js/fxOSMarketplace.js
+++ b/browser/components/loop/standalone/content/js/fxOSMarketplace.js
@@ -11,17 +11,17 @@ loop.fxOSMarketplaceViews = (function() 
    * based API that wraps a small set of functionality from the WebApps API
    * that allow us to request the installation of apps given their manifest
    * URL. We will be embedding the content of this web page within an hidden
    * iframe in case that we need to request the installation of the FxOS Loop
    * client.
    */
   var FxOSHiddenMarketplaceView = React.createClass({displayName: "FxOSHiddenMarketplaceView",
     render: function() {
-      return React.createElement("iframe", {id: "marketplace", src: this.props.marketplaceSrc, hidden: true});
+      return React.createElement("iframe", {hidden: true, id: "marketplace", src: this.props.marketplaceSrc});
     },
 
     componentDidUpdate: function() {
       // This happens only once when we change the 'src' property of the iframe.
       if (this.props.onMarketplaceMessage) {
         // The reason for listening on the global window instead of on the
         // iframe content window is because the Marketplace is doing a
         // window.top.postMessage.
--- a/browser/components/loop/standalone/content/js/fxOSMarketplace.jsx
+++ b/browser/components/loop/standalone/content/js/fxOSMarketplace.jsx
@@ -11,17 +11,17 @@ loop.fxOSMarketplaceViews = (function() 
    * based API that wraps a small set of functionality from the WebApps API
    * that allow us to request the installation of apps given their manifest
    * URL. We will be embedding the content of this web page within an hidden
    * iframe in case that we need to request the installation of the FxOS Loop
    * client.
    */
   var FxOSHiddenMarketplaceView = React.createClass({
     render: function() {
-      return <iframe id="marketplace" src={this.props.marketplaceSrc} hidden/>;
+      return <iframe hidden id="marketplace" src={this.props.marketplaceSrc} />;
     },
 
     componentDidUpdate: function() {
       // This happens only once when we change the 'src' property of the iframe.
       if (this.props.onMarketplaceMessage) {
         // The reason for listening on the global window instead of on the
         // iframe content window is because the Marketplace is doing a
         // window.top.postMessage.
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -11,42 +11,42 @@ loop.standaloneRoomViews = (function(moz
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
   var StandaloneRoomInfoArea = React.createClass({displayName: "StandaloneRoomInfoArea",
     propTypes: {
-      isFirefox: React.PropTypes.bool.isRequired,
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
-      ]).isRequired
+      ]).isRequired,
+      isFirefox: React.PropTypes.bool.isRequired
     },
 
     onFeedbackSent: function() {
       // We pass a tick to prevent React warnings regarding nested updates.
       setTimeout(function() {
         this.props.activeRoomStore.dispatchAction(new sharedActions.FeedbackComplete());
       }.bind(this));
     },
 
     _renderCallToActionLink: function() {
       if (this.props.isFirefox) {
         return (
-          React.createElement("a", {href: loop.config.learnMoreUrl, className: "btn btn-info"}, 
+          React.createElement("a", {className: "btn btn-info", href: loop.config.learnMoreUrl}, 
             mozL10n.get("rooms_room_full_call_to_action_label", {
               clientShortname: mozL10n.get("clientShortname2")
             })
           )
         );
       }
       return (
-        React.createElement("a", {href: loop.config.downloadFirefoxUrl, className: "btn btn-info"}, 
+        React.createElement("a", {className: "btn btn-info", href: loop.config.downloadFirefoxUrl}, 
           mozL10n.get("rooms_room_full_call_to_action_nonFx_label", {
             brandShortname: mozL10n.get("brandShortname")
           })
         )
       );
     },
 
     /**
@@ -122,19 +122,18 @@ loop.standaloneRoomViews = (function(moz
             )
           );
         }
         case ROOM_STATES.ENDED: {
           if (this.props.roomUsed) {
             return (
               React.createElement("div", {className: "ended-conversation"}, 
                 React.createElement(sharedViews.FeedbackView, {
-                  onAfterFeedbackReceived: this.onFeedbackSent, 
-                  noCloseText: true}
-                )
+                  noCloseText: true, 
+                  onAfterFeedbackReceived: this.onFeedbackSent})
               )
             );
           }
 
           // In case the room was not used (no one was here), we
           // bypass the feedback form.
           this.onFeedbackSent();
           return null;
@@ -427,63 +426,63 @@ loop.standaloneRoomViews = (function(moz
         "showing-local-streams": this.state.localSrcVideoObject ||
           this.props.localPosterUrl
       });
 
       return (
         React.createElement("div", {className: "room-conversation-wrapper"}, 
           React.createElement("div", {className: "beta-logo"}), 
           React.createElement(StandaloneRoomHeader, {dispatcher: this.props.dispatcher}), 
-          React.createElement(StandaloneRoomInfoArea, {roomState: this.state.roomState, 
+          React.createElement(StandaloneRoomInfoArea, {activeRoomStore: this.props.activeRoomStore, 
                                   failureReason: this.state.failureReason, 
+                                  isFirefox: this.props.isFirefox, 
                                   joinRoom: this.joinRoom, 
-                                  isFirefox: this.props.isFirefox, 
-                                  activeRoomStore: this.props.activeRoomStore, 
+                                  roomState: this.state.roomState, 
                                   roomUsed: this.state.used}), 
           React.createElement("div", {className: "media-layout"}, 
             React.createElement("div", {className: mediaWrapperClasses}, 
               React.createElement("span", {className: "self-view-hidden-message"}, 
                 mozL10n.get("self_view_hidden_message")
               ), 
               React.createElement("div", {className: remoteStreamClasses}, 
                 React.createElement(sharedViews.MediaView, {displayAvatar: !this.shouldRenderRemoteVideo(), 
-                  posterUrl: this.props.remotePosterUrl, 
                   isLoading: this._shouldRenderRemoteLoading(), 
                   mediaType: "remote", 
+                  posterUrl: this.props.remotePosterUrl, 
                   srcVideoObject: this.state.remoteSrcVideoObject})
               ), 
               React.createElement("div", {className: screenShareStreamClasses}, 
                 React.createElement(sharedViews.MediaView, {displayAvatar: false, 
-                  posterUrl: this.props.screenSharePosterUrl, 
                   isLoading: this._shouldRenderScreenShareLoading(), 
                   mediaType: "screen-share", 
+                  posterUrl: this.props.screenSharePosterUrl, 
                   srcVideoObject: this.state.screenShareVideoObject})
               ), 
               React.createElement(sharedViews.TextChatView, {
                 dispatcher: this.props.dispatcher, 
                 showAlways: true, 
                 showRoomName: true}), 
               React.createElement("div", {className: "local"}, 
                 React.createElement(sharedViews.MediaView, {displayAvatar: this.state.videoMuted, 
-                  posterUrl: this.props.localPosterUrl, 
                   isLoading: this._shouldRenderLocalLoading(), 
                   mediaType: "local", 
+                  posterUrl: this.props.localPosterUrl, 
                   srcVideoObject: this.state.localSrcVideoObject})
               )
             ), 
             React.createElement(sharedViews.ConversationToolbar, {
-              dispatcher: this.props.dispatcher, 
-              video: {enabled: !this.state.videoMuted,
-                      visible: this._roomIsActive()}, 
               audio: {enabled: !this.state.audioMuted,
                       visible: this._roomIsActive()}, 
-              publishStream: this.publishStream, 
+              dispatcher: this.props.dispatcher, 
+              enableHangup: this._roomIsActive(), 
               hangup: this.leaveRoom, 
               hangupButtonLabel: mozL10n.get("rooms_leave_button_label"), 
-              enableHangup: this._roomIsActive()})
+              publishStream: this.publishStream, 
+              video: {enabled: !this.state.videoMuted,
+                      visible: this._roomIsActive()}})
           ), 
           React.createElement(loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView, {
             marketplaceSrc: this.state.marketplaceSrc, 
             onMarketplaceMessage: this.state.onMarketplaceMessage}), 
           React.createElement(StandaloneRoomFooter, {dispatcher: this.props.dispatcher})
         )
       );
     }
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -11,42 +11,42 @@ loop.standaloneRoomViews = (function(moz
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
   var StandaloneRoomInfoArea = React.createClass({
     propTypes: {
-      isFirefox: React.PropTypes.bool.isRequired,
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
-      ]).isRequired
+      ]).isRequired,
+      isFirefox: React.PropTypes.bool.isRequired
     },
 
     onFeedbackSent: function() {
       // We pass a tick to prevent React warnings regarding nested updates.
       setTimeout(function() {
         this.props.activeRoomStore.dispatchAction(new sharedActions.FeedbackComplete());
       }.bind(this));
     },
 
     _renderCallToActionLink: function() {
       if (this.props.isFirefox) {
         return (
-          <a href={loop.config.learnMoreUrl} className="btn btn-info">
+          <a className="btn btn-info" href={loop.config.learnMoreUrl}>
             {mozL10n.get("rooms_room_full_call_to_action_label", {
               clientShortname: mozL10n.get("clientShortname2")
             })}
           </a>
         );
       }
       return (
-        <a href={loop.config.downloadFirefoxUrl} className="btn btn-info">
+        <a className="btn btn-info" href={loop.config.downloadFirefoxUrl}>
           {mozL10n.get("rooms_room_full_call_to_action_nonFx_label", {
             brandShortname: mozL10n.get("brandShortname")
           })}
         </a>
       );
     },
 
     /**
@@ -122,19 +122,18 @@ loop.standaloneRoomViews = (function(moz
             </div>
           );
         }
         case ROOM_STATES.ENDED: {
           if (this.props.roomUsed) {
             return (
               <div className="ended-conversation">
                 <sharedViews.FeedbackView
-                  onAfterFeedbackReceived={this.onFeedbackSent}
                   noCloseText={true}
-                />
+                  onAfterFeedbackReceived={this.onFeedbackSent} />
               </div>
             );
           }
 
           // In case the room was not used (no one was here), we
           // bypass the feedback form.
           this.onFeedbackSent();
           return null;
@@ -427,63 +426,63 @@ loop.standaloneRoomViews = (function(moz
         "showing-local-streams": this.state.localSrcVideoObject ||
           this.props.localPosterUrl
       });
 
       return (
         <div className="room-conversation-wrapper">
           <div className="beta-logo" />
           <StandaloneRoomHeader dispatcher={this.props.dispatcher} />
-          <StandaloneRoomInfoArea roomState={this.state.roomState}
+          <StandaloneRoomInfoArea activeRoomStore={this.props.activeRoomStore}
                                   failureReason={this.state.failureReason}
+                                  isFirefox={this.props.isFirefox}
                                   joinRoom={this.joinRoom}
-                                  isFirefox={this.props.isFirefox}
-                                  activeRoomStore={this.props.activeRoomStore}
+                                  roomState={this.state.roomState}
                                   roomUsed={this.state.used} />
           <div className="media-layout">
             <div className={mediaWrapperClasses}>
               <span className="self-view-hidden-message">
                 {mozL10n.get("self_view_hidden_message")}
               </span>
               <div className={remoteStreamClasses}>
                 <sharedViews.MediaView displayAvatar={!this.shouldRenderRemoteVideo()}
-                  posterUrl={this.props.remotePosterUrl}
                   isLoading={this._shouldRenderRemoteLoading()}
                   mediaType="remote"
+                  posterUrl={this.props.remotePosterUrl}
                   srcVideoObject={this.state.remoteSrcVideoObject} />
               </div>
               <div className={screenShareStreamClasses}>
                 <sharedViews.MediaView displayAvatar={false}
-                  posterUrl={this.props.screenSharePosterUrl}
                   isLoading={this._shouldRenderScreenShareLoading()}
                   mediaType="screen-share"
+                  posterUrl={this.props.screenSharePosterUrl}
                   srcVideoObject={this.state.screenShareVideoObject} />
               </div>
               <sharedViews.TextChatView
                 dispatcher={this.props.dispatcher}
                 showAlways={true}
                 showRoomName={true} />
               <div className="local">
                 <sharedViews.MediaView displayAvatar={this.state.videoMuted}
-                  posterUrl={this.props.localPosterUrl}
                   isLoading={this._shouldRenderLocalLoading()}
                   mediaType="local"
+                  posterUrl={this.props.localPosterUrl}
                   srcVideoObject={this.state.localSrcVideoObject} />
               </div>
             </div>
             <sharedViews.ConversationToolbar
-              dispatcher={this.props.dispatcher}
-              video={{enabled: !this.state.videoMuted,
-                      visible: this._roomIsActive()}}
               audio={{enabled: !this.state.audioMuted,
                       visible: this._roomIsActive()}}
-              publishStream={this.publishStream}
+              dispatcher={this.props.dispatcher}
+              enableHangup={this._roomIsActive()}
               hangup={this.leaveRoom}
               hangupButtonLabel={mozL10n.get("rooms_leave_button_label")}
-              enableHangup={this._roomIsActive()} />
+              publishStream={this.publishStream}
+              video={{enabled: !this.state.videoMuted,
+                      visible: this._roomIsActive()}} />
           </div>
           <loop.fxOSMarketplaceViews.FxOSHiddenMarketplaceView
             marketplaceSrc={this.state.marketplaceSrc}
             onMarketplaceMessage={this.state.onMarketplaceMessage} />
           <StandaloneRoomFooter dispatcher={this.props.dispatcher} />
         </div>
       );
     }
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -243,21 +243,21 @@ loop.webapp = (function($, _, OT, mozL10
       );
     }
   });
 
   var ConversationFooter = React.createClass({displayName: "ConversationFooter",
     render: function() {
       return (
         React.createElement("div", {className: "standalone-footer container-box"}, 
-          React.createElement("div", {title: mozL10n.get("vendor_alttext",
-                                  {vendorShortname: mozL10n.get("vendorShortname")}), 
-               className: "footer-logo"}), 
+          React.createElement("div", {className: "footer-logo", 
+               title: mozL10n.get("vendor_alttext",
+                                  {vendorShortname: mozL10n.get("vendorShortname")})}), 
           React.createElement("div", {className: "footer-external-links"}, 
-            React.createElement("a", {target: "_blank", href: loop.config.generalSupportUrl}, 
+            React.createElement("a", {href: loop.config.generalSupportUrl, target: "_blank"}, 
               mozL10n.get("support_link")
             )
           )
         )
       );
     }
   });
 
@@ -369,29 +369,28 @@ loop.webapp = (function($, _, OT, mozL10
       var callState = mozL10n.get(callStateStringEntityName);
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get(callStateStringEntityName)});
 
       return (
         React.createElement(PendingConversationView, {
           callState: callState, 
-          cancelCallback: this._cancelOutgoingCall}
-        )
+          cancelCallback: this._cancelOutgoingCall})
       );
     }
   });
 
   var InitiateCallButton = React.createClass({displayName: "InitiateCallButton",
     mixins: [sharedMixins.DropdownMenuMixin()],
 
     propTypes: {
       caption: React.PropTypes.string.isRequired,
-      startCall: React.PropTypes.func.isRequired,
-      disabled: React.PropTypes.bool
+      disabled: React.PropTypes.bool,
+      startCall: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {disabled: false};
     },
 
     render: function() {
       var dropdownMenuClasses = React.addons.classSet({
@@ -403,33 +402,33 @@ loop.webapp = (function($, _, OT, mozL10
         "btn-chevron": true,
         "disabled": this.props.disabled
       });
       return (
         React.createElement("div", {className: "standalone-btn-chevron-menu-group"}, 
           React.createElement("div", {className: "btn-group-chevron"}, 
             React.createElement("div", {className: "btn-group"}, 
               React.createElement("button", {className: "btn btn-constrained btn-large btn-accept", 
+                      disabled: this.props.disabled, 
                       onClick: this.props.startCall("audio-video"), 
-                      disabled: this.props.disabled, 
                       title: mozL10n.get("initiate_audio_video_call_tooltip2")}, 
                 React.createElement("span", {className: "standalone-call-btn-text"}, 
                   this.props.caption
                 ), 
                 React.createElement("span", {className: "standalone-call-btn-video-icon"})
               ), 
               React.createElement("div", {className: chevronClasses, 
                    onClick: this.toggleDropdownMenu}
               )
             ), 
             React.createElement("ul", {className: dropdownMenuClasses}, 
               React.createElement("li", null, 
                 React.createElement("button", {className: "start-audio-only-call", 
-                        onClick: this.props.startCall("audio"), 
-                        disabled: this.props.disabled}, 
+                        disabled: this.props.disabled, 
+                        onClick: this.props.startCall("audio")}, 
                   mozL10n.get("initiate_audio_call_button2")
                 )
               )
             )
           )
         )
       );
     }
@@ -437,25 +436,25 @@ loop.webapp = (function($, _, OT, mozL10
 
   /**
    * Initiate conversation view.
    */
   var InitiateConversationView = React.createClass({displayName: "InitiateConversationView",
     mixins: [Backbone.Events],
 
     propTypes: {
+      callButtonLabel: React.PropTypes.string.isRequired,
+      client: React.PropTypes.object.isRequired,
       conversation: React.PropTypes.oneOfType([
                       React.PropTypes.instanceOf(sharedModels.ConversationModel),
                       React.PropTypes.instanceOf(FxOSConversationModel)
                     ]).isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifications: React.PropTypes.object.isRequired,
-      client: React.PropTypes.object.isRequired,
-      title: React.PropTypes.string.isRequired,
-      callButtonLabel: React.PropTypes.string.isRequired
+      title: React.PropTypes.string.isRequired
     },
 
     getInitialState: function() {
       return {
         urlCreationDateString: "",
         disableCallButton: false
       };
     },
@@ -572,49 +571,47 @@ loop.webapp = (function($, _, OT, mozL10
 
   /**
    * Ended conversation view.
    */
   var EndedConversationView = React.createClass({displayName: "EndedConversationView",
     propTypes: {
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      sdk: React.PropTypes.object.isRequired,
-      onAfterFeedbackReceived: React.PropTypes.func.isRequired
+      onAfterFeedbackReceived: React.PropTypes.func.isRequired,
+      sdk: React.PropTypes.object.isRequired
     },
 
     render: function() {
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get("status_conversation_ended")});
       return (
         React.createElement("div", {className: "ended-conversation"}, 
           React.createElement(sharedViews.FeedbackView, {
-            onAfterFeedbackReceived: this.props.onAfterFeedbackReceived}
-          ), 
+            onAfterFeedbackReceived: this.props.onAfterFeedbackReceived}), 
           React.createElement(sharedViews.ConversationView, {
+            audio: {enabled: false, visible: false}, 
             initiate: false, 
+            model: this.props.conversation, 
             sdk: this.props.sdk, 
-            model: this.props.conversation, 
-            audio: {enabled: false, visible: false}, 
-            video: {enabled: false, visible: false}}
-          )
+            video: {enabled: false, visible: false}})
         )
       );
     }
   });
 
   var StartConversationView = React.createClass({displayName: "StartConversationView",
     render: function() {
       document.title = mozL10n.get("clientShortname2");
       return (
         React.createElement(InitiateConversationView, React.__spread({}, 
           this.props, 
-          {title: mozL10n.get("initiate_call_button_label2"), 
-          callButtonLabel: mozL10n.get("initiate_audio_video_call_button2")}))
+          {callButtonLabel: mozL10n.get("initiate_audio_video_call_button2"), 
+          title: mozL10n.get("initiate_call_button_label2")}))
       );
     }
   });
 
   var FailedConversationView = React.createClass({displayName: "FailedConversationView",
     mixins: [sharedMixins.AudioMixin],
 
     componentDidMount: function() {
@@ -623,18 +620,18 @@ loop.webapp = (function($, _, OT, mozL10
 
     render: function() {
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get("status_error")});
       return (
         React.createElement(InitiateConversationView, React.__spread({}, 
           this.props, 
-          {title: mozL10n.get("call_failed_title"), 
-          callButtonLabel: mozL10n.get("retry_call_button")}))
+          {callButtonLabel: mozL10n.get("retry_call_button"), 
+          title: mozL10n.get("call_failed_title")}))
       );
     }
   });
 
   /**
    * This view manages the outgoing conversation views - from
    * call initiation through to the actual conversation and call end.
    *
@@ -690,57 +687,53 @@ loop.webapp = (function($, _, OT, mozL10
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
         case "start": {
           return (
             React.createElement(StartConversationView, {
+              client: this.props.client, 
               conversation: this.props.conversation, 
-              notifications: this.props.notifications, 
-              client: this.props.client}
-            )
+              notifications: this.props.notifications})
           );
         }
         case "failure": {
           return (
             React.createElement(FailedConversationView, {
+              client: this.props.client, 
               conversation: this.props.conversation, 
-              notifications: this.props.notifications, 
-              client: this.props.client}
-            )
+              notifications: this.props.notifications})
           );
         }
         case "gumPrompt": {
           return React.createElement(GumPromptConversationView, null);
         }
         case "pending": {
           return React.createElement(WaitingConversationView, {websocket: this._websocket});
         }
         case "connected": {
           document.title = mozL10n.get("standalone_title_with_status",
                                        {clientShortname: mozL10n.get("clientShortname2"),
                                         currentStatus: mozL10n.get("status_in_conversation")});
           return (
             React.createElement(sharedViews.ConversationView, {
               initiate: true, 
+              model: this.props.conversation, 
               sdk: this.props.sdk, 
-              model: this.props.conversation, 
-              video: {enabled: this.props.conversation.hasVideoStream("outgoing")}}
-            )
+              video: {enabled: this.props.conversation.hasVideoStream("outgoing")}})
           );
         }
         case "end": {
           return (
             React.createElement(EndedConversationView, {
-              sdk: this.props.sdk, 
               conversation: this.props.conversation, 
-              onAfterFeedbackReceived: this.resetCallStatus()}
-            )
+              onAfterFeedbackReceived: this.resetCallStatus(), 
+              sdk: this.props.sdk})
           );
         }
         case "expired": {
           return (
             React.createElement(CallUrlExpiredView, null)
           );
         }
         default: {
@@ -934,33 +927,31 @@ loop.webapp = (function($, _, OT, mozL10
    */
   var WebappRootView = React.createClass({displayName: "WebappRootView",
 
     mixins: [sharedMixins.UrlHashChangeMixin,
              sharedMixins.DocumentLocationMixin,
              Backbone.Events],
 
     propTypes: {
+      activeRoomStore: React.PropTypes.oneOfType([
+        React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
+        React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
+      ]).isRequired,
       client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
       conversation: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(sharedModels.ConversationModel),
         React.PropTypes.instanceOf(FxOSConversationModel)
       ]).isRequired,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
                           .isRequired,
       sdk: React.PropTypes.object.isRequired,
-
-      // XXX New types for flux style
       standaloneAppStore: React.PropTypes.instanceOf(
-        loop.store.StandaloneAppStore).isRequired,
-      activeRoomStore: React.PropTypes.oneOfType([
-        React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
-        React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
-      ]).isRequired,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+        loop.store.StandaloneAppStore).isRequired
     },
 
     getInitialState: function() {
       return this.props.standaloneAppStore.getStoreState();
     },
 
     componentWillMount: function() {
       this.listenTo(this.props.standaloneAppStore, "change", function() {
@@ -983,30 +974,28 @@ loop.webapp = (function($, _, OT, mozL10
         }
         case "unsupportedBrowser": {
           return React.createElement(UnsupportedBrowserView, {isFirefox: this.state.isFirefox});
         }
         case "outgoing": {
           return (
             React.createElement(OutgoingConversationView, {
                client: this.props.client, 
+               conversation: this.props.conversation, 
                dispatcher: this.props.dispatcher, 
-               conversation: this.props.conversation, 
                notifications: this.props.notifications, 
-               sdk: this.props.sdk}
-            )
+               sdk: this.props.sdk})
           );
         }
         case "room": {
           return (
             React.createElement(loop.standaloneRoomViews.StandaloneRoomView, {
               activeRoomStore: this.props.activeRoomStore, 
               dispatcher: this.props.dispatcher, 
-              isFirefox: this.state.isFirefox}
-            )
+              isFirefox: this.state.isFirefox})
           );
         }
         case "home": {
           return React.createElement(HomeView, null);
         }
         default: {
           // The state hasn't been initialised yet, so don't display
           // anything to avoid flicker.
@@ -1102,24 +1091,23 @@ loop.webapp = (function($, _, OT, mozL10
       textChatStore: textChatStore
     });
 
     window.addEventListener("unload", function() {
       dispatcher.dispatch(new sharedActions.WindowUnload());
     });
 
     React.render(React.createElement(WebappRootView, {
+      activeRoomStore: activeRoomStore, 
       client: client, 
       conversation: conversation, 
+      dispatcher: dispatcher, 
       notifications: notifications, 
       sdk: OT, 
-      standaloneAppStore: standaloneAppStore, 
-      activeRoomStore: activeRoomStore, 
-      dispatcher: dispatcher}
-    ), document.querySelector("#main"));
+      standaloneAppStore: standaloneAppStore}), document.querySelector("#main"));
 
     // Set the 'lang' and 'dir' attributes to <html> when the page is translated
     document.documentElement.lang = mozL10n.language.code;
     document.documentElement.dir = mozL10n.language.direction;
     document.title = mozL10n.get("clientShortname2");
 
     var locationData = sharedUtils.locationData();
 
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -243,21 +243,21 @@ loop.webapp = (function($, _, OT, mozL10
       );
     }
   });
 
   var ConversationFooter = React.createClass({
     render: function() {
       return (
         <div className="standalone-footer container-box">
-          <div title={mozL10n.get("vendor_alttext",
-                                  {vendorShortname: mozL10n.get("vendorShortname")})}
-               className="footer-logo"></div>
+          <div className="footer-logo"
+               title={mozL10n.get("vendor_alttext",
+                                  {vendorShortname: mozL10n.get("vendorShortname")})} />
           <div className="footer-external-links">
-            <a target="_blank" href={loop.config.generalSupportUrl}>
+            <a href={loop.config.generalSupportUrl} target="_blank">
               {mozL10n.get("support_link")}
             </a>
           </div>
         </div>
       );
     }
   });
 
@@ -369,29 +369,28 @@ loop.webapp = (function($, _, OT, mozL10
       var callState = mozL10n.get(callStateStringEntityName);
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get(callStateStringEntityName)});
 
       return (
         <PendingConversationView
           callState={callState}
-          cancelCallback={this._cancelOutgoingCall}
-        />
+          cancelCallback={this._cancelOutgoingCall} />
       );
     }
   });
 
   var InitiateCallButton = React.createClass({
     mixins: [sharedMixins.DropdownMenuMixin()],
 
     propTypes: {
       caption: React.PropTypes.string.isRequired,
-      startCall: React.PropTypes.func.isRequired,
-      disabled: React.PropTypes.bool
+      disabled: React.PropTypes.bool,
+      startCall: React.PropTypes.func.isRequired
     },
 
     getDefaultProps: function() {
       return {disabled: false};
     },
 
     render: function() {
       var dropdownMenuClasses = React.addons.classSet({
@@ -403,33 +402,33 @@ loop.webapp = (function($, _, OT, mozL10
         "btn-chevron": true,
         "disabled": this.props.disabled
       });
       return (
         <div className="standalone-btn-chevron-menu-group">
           <div className="btn-group-chevron">
             <div className="btn-group">
               <button className="btn btn-constrained btn-large btn-accept"
+                      disabled={this.props.disabled}
                       onClick={this.props.startCall("audio-video")}
-                      disabled={this.props.disabled}
                       title={mozL10n.get("initiate_audio_video_call_tooltip2")}>
                 <span className="standalone-call-btn-text">
                   {this.props.caption}
                 </span>
                 <span className="standalone-call-btn-video-icon" />
               </button>
               <div className={chevronClasses}
                    onClick={this.toggleDropdownMenu}>
               </div>
             </div>
             <ul className={dropdownMenuClasses}>
               <li>
                 <button className="start-audio-only-call"
-                        onClick={this.props.startCall("audio")}
-                        disabled={this.props.disabled}>
+                        disabled={this.props.disabled}
+                        onClick={this.props.startCall("audio")}>
                   {mozL10n.get("initiate_audio_call_button2")}
                 </button>
               </li>
             </ul>
           </div>
         </div>
       );
     }
@@ -437,25 +436,25 @@ loop.webapp = (function($, _, OT, mozL10
 
   /**
    * Initiate conversation view.
    */
   var InitiateConversationView = React.createClass({
     mixins: [Backbone.Events],
 
     propTypes: {
+      callButtonLabel: React.PropTypes.string.isRequired,
+      client: React.PropTypes.object.isRequired,
       conversation: React.PropTypes.oneOfType([
                       React.PropTypes.instanceOf(sharedModels.ConversationModel),
                       React.PropTypes.instanceOf(FxOSConversationModel)
                     ]).isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifications: React.PropTypes.object.isRequired,
-      client: React.PropTypes.object.isRequired,
-      title: React.PropTypes.string.isRequired,
-      callButtonLabel: React.PropTypes.string.isRequired
+      title: React.PropTypes.string.isRequired
     },
 
     getInitialState: function() {
       return {
         urlCreationDateString: "",
         disableCallButton: false
       };
     },
@@ -572,49 +571,47 @@ loop.webapp = (function($, _, OT, mozL10
 
   /**
    * Ended conversation view.
    */
   var EndedConversationView = React.createClass({
     propTypes: {
       conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
                          .isRequired,
-      sdk: React.PropTypes.object.isRequired,
-      onAfterFeedbackReceived: React.PropTypes.func.isRequired
+      onAfterFeedbackReceived: React.PropTypes.func.isRequired,
+      sdk: React.PropTypes.object.isRequired
     },
 
     render: function() {
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get("status_conversation_ended")});
       return (
         <div className="ended-conversation">
           <sharedViews.FeedbackView
-            onAfterFeedbackReceived={this.props.onAfterFeedbackReceived}
-          />
+            onAfterFeedbackReceived={this.props.onAfterFeedbackReceived} />
           <sharedViews.ConversationView
+            audio={{enabled: false, visible: false}}
             initiate={false}
+            model={this.props.conversation}
             sdk={this.props.sdk}
-            model={this.props.conversation}
-            audio={{enabled: false, visible: false}}
-            video={{enabled: false, visible: false}}
-          />
+            video={{enabled: false, visible: false}} />
         </div>
       );
     }
   });
 
   var StartConversationView = React.createClass({
     render: function() {
       document.title = mozL10n.get("clientShortname2");
       return (
         <InitiateConversationView
           {...this.props}
-          title={mozL10n.get("initiate_call_button_label2")}
-          callButtonLabel={mozL10n.get("initiate_audio_video_call_button2")}/>
+          callButtonLabel={mozL10n.get("initiate_audio_video_call_button2")}
+          title={mozL10n.get("initiate_call_button_label2")} />
       );
     }
   });
 
   var FailedConversationView = React.createClass({
     mixins: [sharedMixins.AudioMixin],
 
     componentDidMount: function() {
@@ -623,18 +620,18 @@ loop.webapp = (function($, _, OT, mozL10
 
     render: function() {
       document.title = mozL10n.get("standalone_title_with_status",
                                    {clientShortname: mozL10n.get("clientShortname2"),
                                     currentStatus: mozL10n.get("status_error")});
       return (
         <InitiateConversationView
           {...this.props}
-          title={mozL10n.get("call_failed_title")}
-          callButtonLabel={mozL10n.get("retry_call_button")} />
+          callButtonLabel={mozL10n.get("retry_call_button")}
+          title={mozL10n.get("call_failed_title")} />
       );
     }
   });
 
   /**
    * This view manages the outgoing conversation views - from
    * call initiation through to the actual conversation and call end.
    *
@@ -690,57 +687,53 @@ loop.webapp = (function($, _, OT, mozL10
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
         case "start": {
           return (
             <StartConversationView
+              client={this.props.client}
               conversation={this.props.conversation}
-              notifications={this.props.notifications}
-              client={this.props.client}
-            />
+              notifications={this.props.notifications} />
           );
         }
         case "failure": {
           return (
             <FailedConversationView
+              client={this.props.client}
               conversation={this.props.conversation}
-              notifications={this.props.notifications}
-              client={this.props.client}
-            />
+              notifications={this.props.notifications} />
           );
         }
         case "gumPrompt": {
           return <GumPromptConversationView />;
         }
         case "pending": {
           return <WaitingConversationView websocket={this._websocket} />;
         }
         case "connected": {
           document.title = mozL10n.get("standalone_title_with_status",
                                        {clientShortname: mozL10n.get("clientShortname2"),
                                         currentStatus: mozL10n.get("status_in_conversation")});
           return (
             <sharedViews.ConversationView
               initiate={true}
+              model={this.props.conversation}
               sdk={this.props.sdk}
-              model={this.props.conversation}
-              video={{enabled: this.props.conversation.hasVideoStream("outgoing")}}
-            />
+              video={{enabled: this.props.conversation.hasVideoStream("outgoing")}} />
           );
         }
         case "end": {
           return (
             <EndedConversationView
-              sdk={this.props.sdk}
               conversation={this.props.conversation}
               onAfterFeedbackReceived={this.resetCallStatus()}
-            />
+              sdk={this.props.sdk} />
           );
         }
         case "expired": {
           return (
             <CallUrlExpiredView />
           );
         }
         default: {
@@ -934,33 +927,31 @@ loop.webapp = (function($, _, OT, mozL10
    */
   var WebappRootView = React.createClass({
 
     mixins: [sharedMixins.UrlHashChangeMixin,
              sharedMixins.DocumentLocationMixin,
              Backbone.Events],
 
     propTypes: {
+      activeRoomStore: React.PropTypes.oneOfType([
+        React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
+        React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
+      ]).isRequired,
       client: React.PropTypes.instanceOf(loop.StandaloneClient).isRequired,
       conversation: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(sharedModels.ConversationModel),
         React.PropTypes.instanceOf(FxOSConversationModel)
       ]).isRequired,
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
                           .isRequired,
       sdk: React.PropTypes.object.isRequired,
-
-      // XXX New types for flux style
       standaloneAppStore: React.PropTypes.instanceOf(
-        loop.store.StandaloneAppStore).isRequired,
-      activeRoomStore: React.PropTypes.oneOfType([
-        React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
-        React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
-      ]).isRequired,
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+        loop.store.StandaloneAppStore).isRequired
     },
 
     getInitialState: function() {
       return this.props.standaloneAppStore.getStoreState();
     },
 
     componentWillMount: function() {
       this.listenTo(this.props.standaloneAppStore, "change", function() {
@@ -983,30 +974,28 @@ loop.webapp = (function($, _, OT, mozL10
         }
         case "unsupportedBrowser": {
           return <UnsupportedBrowserView isFirefox={this.state.isFirefox}/>;
         }
         case "outgoing": {
           return (
             <OutgoingConversationView
                client={this.props.client}
+               conversation={this.props.conversation}
                dispatcher={this.props.dispatcher}
-               conversation={this.props.conversation}
                notifications={this.props.notifications}
-               sdk={this.props.sdk}
-            />
+               sdk={this.props.sdk} />
           );
         }
         case "room": {
           return (
             <loop.standaloneRoomViews.StandaloneRoomView
               activeRoomStore={this.props.activeRoomStore}
               dispatcher={this.props.dispatcher}
-              isFirefox={this.state.isFirefox}
-            />
+              isFirefox={this.state.isFirefox} />
           );
         }
         case "home": {
           return <HomeView />;
         }
         default: {
           // The state hasn't been initialised yet, so don't display
           // anything to avoid flicker.
@@ -1102,24 +1091,23 @@ loop.webapp = (function($, _, OT, mozL10
       textChatStore: textChatStore
     });
 
     window.addEventListener("unload", function() {
       dispatcher.dispatch(new sharedActions.WindowUnload());
     });
 
     React.render(<WebappRootView
+      activeRoomStore={activeRoomStore}
       client={client}
       conversation={conversation}
+      dispatcher={dispatcher}
       notifications={notifications}
       sdk={OT}
-      standaloneAppStore={standaloneAppStore}
-      activeRoomStore={activeRoomStore}
-      dispatcher={dispatcher}
-    />, document.querySelector("#main"));
+      standaloneAppStore={standaloneAppStore} />, document.querySelector("#main"));
 
     // Set the 'lang' and 'dir' attributes to <html> when the page is translated
     document.documentElement.lang = mozL10n.language.code;
     document.documentElement.dir = mozL10n.language.direction;
     document.title = mozL10n.get("clientShortname2");
 
     var locationData = sharedUtils.locationData();
 
--- a/browser/components/loop/ui/react-frame-component.js
+++ b/browser/components/loop/ui/react-frame-component.js
@@ -20,28 +20,28 @@ window.queuedFrames = [];
  * contents will have been rendered, and then fires a callback indicating that.
  *
  * @see onContentsRendered for the gory details about this.
  *
  * @type {ReactComponentFactory<P>}
  */
 window.Frame = React.createClass({
   propTypes: {
-    style: React.PropTypes.object,
-    head: React.PropTypes.node,
-    width: React.PropTypes.number,
-    height: React.PropTypes.number,
-    onContentsRendered: React.PropTypes.func,
     className: React.PropTypes.string,
     /* By default, <link rel="stylesheet> nodes from the containing frame's
        head will be cloned into this iframe.  However, if the link also has
        a "class" attribute, we only clone it if that class attribute is the
        same as cssClass.  This allows us to avoid injecting stylesheets that
        aren't intended for this rendering of this component. */
-    cssClass: React.PropTypes.string
+    cssClass: React.PropTypes.string,
+    head: React.PropTypes.node,
+    height: React.PropTypes.number,
+    onContentsRendered: React.PropTypes.func,
+    style: React.PropTypes.object,
+    width: React.PropTypes.number
   },
   render: function() {
     return React.createElement("iframe", {
       style: this.props.style,
       head: this.props.head,
       width: this.props.width,
       height: this.props.height,
       className: this.props.className
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -380,19 +380,19 @@
     detailsButtonLabel: "Retry"
   });
 
   var SVGIcon = React.createClass({displayName: "SVGIcon",
     render: function() {
       var sizeUnit = this.props.size.split("x");
       return (
         React.createElement("img", {className: "svg-icon", 
+             height: sizeUnit[1], 
              src: "../content/shared/img/icons-" + this.props.size + ".svg#" + this.props.shapeId, 
-             width: sizeUnit[0], 
-             height: sizeUnit[1]})
+             width: sizeUnit[0]})
       );
     }
   });
 
   var SVGIcons = React.createClass({displayName: "SVGIcons",
     shapes: {
       "10x10": ["close", "close-active", "close-disabled", "dropdown",
         "dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
@@ -416,35 +416,35 @@
         "tag-hover", "tag-active", "trash", "unblock", "unblock-hover", "unblock-active",
         "video", "video-hover", "video-active", "tour"
       ]
     },
 
     render: function() {
       var icons = this.shapes[this.props.size].map(function(shapeId, i) {
         return (
-          React.createElement("li", {key: this.props.size + "-" + i, className: "svg-icon-entry"}, 
+          React.createElement("li", {className: "svg-icon-entry", key: this.props.size + "-" + i}, 
             React.createElement("p", null, React.createElement(SVGIcon, {shapeId: shapeId, size: this.props.size})), 
             React.createElement("p", null, shapeId)
           )
         );
       }, this);
       return (
         React.createElement("ul", {className: "svg-icon-list"}, icons)
       );
     }
   });
 
   var FramedExample = React.createClass({displayName: "FramedExample",
     propTypes: {
-      width: React.PropTypes.number,
+      cssClass: React.PropTypes.string,
+      dashed: React.PropTypes.bool,
       height: React.PropTypes.number,
       onContentsRendered: React.PropTypes.func,
-      dashed: React.PropTypes.bool,
-      cssClass: React.PropTypes.string
+      width: React.PropTypes.number
     },
 
     makeId: function(prefix) {
       return (prefix || "") + this.props.summary.toLowerCase().replace(/\s/g, "-");
     },
 
     render: function() {
       var height = this.props.height;
@@ -459,20 +459,21 @@
       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, {width: width, height: height, 
+            React.createElement(Frame, {className: cx({dashed: this.props.dashed}), 
+                   cssClass: this.props.cssClass, 
+                   height: height, 
                    onContentsRendered: this.props.onContentsRendered, 
-                   className: cx({dashed: this.props.dashed}), 
-                   cssClass: this.props.cssClass}, 
+                   width: width}, 
               this.props.children
             )
           )
         )
       );
     }
   });
 
@@ -496,17 +497,17 @@
         )
       );
     }
   });
 
   var Section = React.createClass({displayName: "Section",
     render: function() {
       return (
-        React.createElement("section", {id: this.props.name, className: this.props.className}, 
+        React.createElement("section", {className: this.props.className, id: this.props.name}, 
           React.createElement("h1", null, this.props.name), 
           this.props.children
         )
       );
     }
   });
 
   var ShowCase = React.createClass({displayName: "ShowCase",
@@ -539,17 +540,17 @@
         document.documentElement.setAttribute("lang", "ar");
         document.documentElement.setAttribute("dir", "rtl");
       }
 
       return (
         React.createElement("div", {className: "showcase"}, 
           React.createElement("header", null, 
             React.createElement("h1", null, "Loop UI Components Showcase"), 
-            React.createElement(Checkbox, {label: "RTL mode?", checked: this.state.rtlMode, 
+            React.createElement(Checkbox, {checked: this.state.rtlMode, label: "RTL mode?", 
               onChange: this._handleCheckboxChange}), 
             React.createElement("nav", {className: "showcase-menu"}, 
               React.Children.map(this.props.children, function(section) {
                 return (
                   React.createElement("a", {className: "btn btn-info", href: "#" + section.props.name}, 
                     section.props.name
                   )
                 );
@@ -566,252 +567,273 @@
 
     render: function() {
       return (
         React.createElement(ShowCase, null, 
           React.createElement(Section, {name: "PanelView"}, 
             React.createElement("p", {className: "note"}, 
               React.createElement("strong", null, "Note:"), " 332px wide."
             ), 
-            React.createElement(Example, {summary: "Re-sign-in view", dashed: "true", style: {width: "332px"}}, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Re-sign-in view"}, 
               React.createElement(SignInRequestView, {mozLoop: mockMozLoopRooms})
             ), 
-            React.createElement(Example, {summary: "Room list tab", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {client: mockClient, notifications: notifications, 
-                         userProfile: {email: "test@example.com"}, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Room list tab"}, 
+              React.createElement(PanelView, {client: mockClient, 
+                         dispatcher: dispatcher, 
                          mozLoop: mockMozLoopRooms, 
-                         dispatcher: dispatcher, 
+                         notifications: notifications, 
                          roomStore: roomStore, 
-                         selectedTab: "rooms"})
+                         selectedTab: "rooms", 
+                         userProfile: {email: "test@example.com"}})
             ), 
-            React.createElement(Example, {summary: "Contact list tab", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {client: mockClient, notifications: notifications, 
-                         userProfile: {email: "test@example.com"}, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Contact list tab"}, 
+              React.createElement(PanelView, {client: mockClient, 
+                         dispatcher: dispatcher, 
                          mozLoop: mockMozLoopRooms, 
-                         dispatcher: dispatcher, 
+                         notifications: notifications, 
                          roomStore: roomStore, 
-                         selectedTab: "contacts"})
+                         selectedTab: "contacts", 
+                         userProfile: {email: "test@example.com"}})
             ), 
-            React.createElement(Example, {summary: "Error Notification", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {client: mockClient, notifications: errNotifications, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Error Notification"}, 
+              React.createElement(PanelView, {client: mockClient, 
+                         dispatcher: dispatcher, 
                          mozLoop: navigator.mozLoop, 
-                         dispatcher: dispatcher, 
+                         notifications: errNotifications, 
                          roomStore: roomStore})
             ), 
-            React.createElement(Example, {summary: "Error Notification - authenticated", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {client: mockClient, notifications: errNotifications, 
-                         userProfile: {email: "test@example.com"}, 
-                         mozLoop: navigator.mozLoop, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Error Notification - authenticated"}, 
+              React.createElement(PanelView, {client: mockClient, 
                          dispatcher: dispatcher, 
-                         roomStore: roomStore})
+                         mozLoop: navigator.mozLoop, 
+                         notifications: errNotifications, 
+                         roomStore: roomStore, 
+                         userProfile: {email: "test@example.com"}})
             ), 
-            React.createElement(Example, {summary: "Contact import success", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {notifications: new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}]), 
-                         userProfile: {email: "test@example.com"}, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Contact import success"}, 
+              React.createElement(PanelView, {dispatcher: dispatcher, 
                          mozLoop: mockMozLoopRooms, 
-                         dispatcher: dispatcher, 
+                         notifications: new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}]), 
                          roomStore: roomStore, 
-                         selectedTab: "contacts"})
+                         selectedTab: "contacts", 
+                         userProfile: {email: "test@example.com"}})
             ), 
-            React.createElement(Example, {summary: "Contact import error", dashed: "true", style: {width: "332px"}}, 
-              React.createElement(PanelView, {notifications: new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}]), 
-                         userProfile: {email: "test@example.com"}, 
+            React.createElement(Example, {dashed: "true", style: {width: "332px"}, summary: "Contact import error"}, 
+              React.createElement(PanelView, {dispatcher: dispatcher, 
                          mozLoop: mockMozLoopRooms, 
-                         dispatcher: dispatcher, 
+                         notifications: new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}]), 
                          roomStore: roomStore, 
-                         selectedTab: "contacts"})
+                         selectedTab: "contacts", 
+                         userProfile: {email: "test@example.com"}})
             )
           ), 
 
           React.createElement(Section, {name: "AcceptCallView"}, 
-            React.createElement(Example, {summary: "Default / incoming video call", dashed: "true", style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", style: {width: "300px", height: "272px"}, 
+                     summary: "Default / incoming video call"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(AcceptCallView, {callType: CALL_TYPES.AUDIO_VIDEO, 
                                 callerId: "Mr Smith", 
                                 dispatcher: dispatcher, 
                                 mozLoop: mockMozLoopRooms})
               )
             ), 
 
-            React.createElement(Example, {summary: "Default / incoming audio only call", dashed: "true", style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", style: {width: "300px", height: "272px"}, 
+                     summary: "Default / incoming audio only call"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(AcceptCallView, {callType: CALL_TYPES.AUDIO_ONLY, 
                                 callerId: "Mr Smith", 
                                 dispatcher: dispatcher, 
                                 mozLoop: mockMozLoopRooms})
               )
             )
           ), 
 
           React.createElement(Section, {name: "AcceptCallView-ActiveState"}, 
-            React.createElement(Example, {summary: "Default", dashed: "true", style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", style: {width: "300px", height: "272px"}, 
+                     summary: "Default"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(AcceptCallView, {callType: CALL_TYPES.AUDIO_VIDEO, 
                                 callerId: "Mr Smith", 
                                 dispatcher: dispatcher, 
                                 mozLoop: mockMozLoopRooms, 
                                 showMenu: true})
               )
             )
           ), 
 
           React.createElement(Section, {name: "ConversationToolbar"}, 
             React.createElement("h2", null, "Desktop Conversation Window"), 
             React.createElement("div", {className: "fx-embedded override-position"}, 
-              React.createElement(Example, {summary: "Default", style: {width: "300px", height: "26px"}}, 
-                React.createElement(ConversationToolbar, {video: {enabled: true}, 
-                                     audio: {enabled: true}, 
+              React.createElement(Example, {style: {width: "300px", height: "26px"}, summary: "Default"}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: true}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: true}})
               ), 
-              React.createElement(Example, {summary: "Video muted", style: {width: "300px", height: "26px"}}, 
-                React.createElement(ConversationToolbar, {video: {enabled: false}, 
-                                     audio: {enabled: true}, 
+              React.createElement(Example, {style: {width: "300px", height: "26px"}, summary: "Video muted"}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: true}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: false}})
               ), 
-              React.createElement(Example, {summary: "Audio muted", style: {width: "300px", height: "26px"}}, 
-                React.createElement(ConversationToolbar, {video: {enabled: true}, 
-                                     audio: {enabled: false}, 
+              React.createElement(Example, {style: {width: "300px", height: "26px"}, summary: "Audio muted"}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: false}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: true}})
               )
             ), 
 
             React.createElement("h2", null, "Standalone"), 
             React.createElement("div", {className: "standalone override-position"}, 
               React.createElement(Example, {summary: "Default"}, 
-                React.createElement(ConversationToolbar, {video: {enabled: true}, 
-                                     audio: {enabled: true}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: true}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: true}})
               ), 
               React.createElement(Example, {summary: "Video muted"}, 
-                React.createElement(ConversationToolbar, {video: {enabled: false}, 
-                                     audio: {enabled: true}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: true}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: false}})
               ), 
               React.createElement(Example, {summary: "Audio muted"}, 
-                React.createElement(ConversationToolbar, {video: {enabled: true}, 
-                                     audio: {enabled: false}, 
+                React.createElement(ConversationToolbar, {audio: {enabled: false}, 
                                      hangup: noop, 
-                                     publishStream: noop})
+                                     publishStream: noop, 
+                                     video: {enabled: true}})
               )
             )
           ), 
 
           React.createElement(Section, {name: "PendingConversationView (Desktop)"}, 
-            React.createElement(Example, {summary: "Connecting", dashed: "true", 
-                     style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Connecting"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopPendingConversationView, {callState: "gather", 
                                                 contact: mockContact, 
                                                 dispatcher: dispatcher})
               )
             )
           ), 
 
           React.createElement(Section, {name: "CallFailedView"}, 
-            React.createElement(Example, {summary: "Call Failed - Incoming", dashed: "true", 
-                     style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Call Failed - Incoming"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(CallFailedView, {dispatcher: dispatcher, 
                                 outgoing: false, 
                                 store: conversationStore})
               )
             ), 
-            React.createElement(Example, {summary: "Call Failed - Outgoing", dashed: "true", 
-                     style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Call Failed - Outgoing"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(CallFailedView, {dispatcher: dispatcher, 
                                 outgoing: true, 
                                 store: conversationStore})
               )
             ), 
-            React.createElement(Example, {summary: "Call Failed — with call URL error", dashed: "true", 
-                     style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Call Failed — with call URL error"}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true, 
                                 outgoing: true, 
                                 store: conversationStore})
               )
             )
           ), 
 
           React.createElement(Section, {name: "OngoingConversationView"}, 
-            React.createElement(FramedExample, {width: 298, height: 254, 
-                           summary: "Desktop ongoing conversation window"}, 
+            React.createElement(FramedExample, {height: 254, 
+                           summary: "Desktop ongoing conversation window", 
+                           width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(OngoingConversationView, {
+                  audio: {enabled: true}, 
                   dispatcher: dispatcher, 
-                  video: {enabled: true}, 
-                  audio: {enabled: true}, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
+                  mediaConnected: true, 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
                   remoteVideoEnabled: true, 
-                  mediaConnected: true})
+                  video: {enabled: true}})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 800, height: 600, 
-                           summary: "Desktop ongoing conversation window large"}, 
+            React.createElement(FramedExample, {height: 600, 
+                           summary: "Desktop ongoing conversation window large", 
+                           width: 800}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(OngoingConversationView, {
+                    audio: {enabled: true}, 
                     dispatcher: dispatcher, 
-                    video: {enabled: true}, 
-                    audio: {enabled: true}, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
+                    mediaConnected: true, 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
                     remoteVideoEnabled: true, 
-                    mediaConnected: true})
+                    video: {enabled: true}})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
-              summary: "Desktop ongoing conversation window - local face mute"}, 
+            React.createElement(FramedExample, {height: 254, 
+              summary: "Desktop ongoing conversation window - local face mute", 
+              width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(OngoingConversationView, {
+                  audio: {enabled: true}, 
                   dispatcher: dispatcher, 
-                  video: {enabled: false}, 
-                  audio: {enabled: true}, 
+                  mediaConnected: true, 
+                  remotePosterUrl: "sample-img/video-screen-remote.png", 
                   remoteVideoEnabled: true, 
-                  remotePosterUrl: "sample-img/video-screen-remote.png", 
-                  mediaConnected: true})
+                  video: {enabled: false}})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
-              summary: "Desktop ongoing conversation window - remote face mute"}, 
+            React.createElement(FramedExample, {height: 254, 
+              summary: "Desktop ongoing conversation window - remote face mute", 
+              width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(OngoingConversationView, {
+                  audio: {enabled: true}, 
                   dispatcher: dispatcher, 
-                  video: {enabled: true}, 
-                  audio: {enabled: true}, 
+                  localPosterUrl: "sample-img/video-screen-local.png", 
+                  mediaConnected: true, 
                   remoteVideoEnabled: false, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
-                  mediaConnected: true})
+                  video: {enabled: true}})
               )
             )
 
           ), 
 
           React.createElement(Section, {name: "FeedbackView"}, 
             React.createElement("p", {className: "note"}, 
               React.createElement("strong", null, "Note:"), " For the useable demo, you can access submitted data at ", 
               React.createElement("a", {href: "https://input.allizom.org/"}, "input.allizom.org"), "."
             ), 
-            React.createElement(Example, {summary: "Default (useable demo)", dashed: "true", style: {width: "300px", height: "272px"}}, 
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Default (useable demo)"}, 
               React.createElement(FeedbackView, {feedbackStore: feedbackStore})
             ), 
-            React.createElement(Example, {summary: "Detailed form", dashed: "true", style: {width: "300px", height: "272px"}}, 
-              React.createElement(FeedbackView, {feedbackStore: feedbackStore, feedbackState: FEEDBACK_STATES.DETAILS})
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Detailed form"}, 
+              React.createElement(FeedbackView, {feedbackState: FEEDBACK_STATES.DETAILS, feedbackStore: feedbackStore})
             ), 
-            React.createElement(Example, {summary: "Thank you!", dashed: "true", style: {width: "300px", height: "272px"}}, 
-              React.createElement(FeedbackView, {feedbackStore: feedbackStore, feedbackState: FEEDBACK_STATES.SENT})
+            React.createElement(Example, {dashed: "true", 
+                     style: {width: "300px", height: "272px"}, 
+                     summary: "Thank you!"}, 
+              React.createElement(FeedbackView, {feedbackState: FEEDBACK_STATES.SENT, feedbackStore: feedbackStore})
             )
           ), 
 
           React.createElement(Section, {name: "AlertMessages"}, 
             React.createElement(Example, {summary: "Various alerts"}, 
               React.createElement("div", {className: "alert alert-warning"}, 
                 React.createElement("button", {className: "close"}), 
                 React.createElement("p", {className: "message"}, 
@@ -840,319 +862,364 @@
             React.createElement(Example, {summary: "Standalone Unsupported Device"}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(UnsupportedDeviceView, {platform: "ios"})
               )
             )
           ), 
 
           React.createElement(Section, {name: "DesktopRoomConversationView"}, 
-            React.createElement(FramedExample, {width: 298, height: 254, 
-              summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"}, 
+            React.createElement(FramedExample, {
+              height: 254, 
+              summary: "Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)", 
+              width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
-                  roomStore: invitationRoomStore, 
                   dispatcher: dispatcher, 
+                  localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
-                  roomState: ROOM_STATES.INIT})
+                  roomState: ROOM_STATES.INIT, 
+                  roomStore: invitationRoomStore})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
-              summary: "Desktop room conversation (loading)"}, 
+            React.createElement(FramedExample, {
+              dashed: true, 
+              height: 394, 
+              summary: "Desktop room conversation (loading)", 
+              width: 298}, 
               /* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */
               React.createElement("div", {className: "fx-embedded overflow-hidden"}, 
                 React.createElement(DesktopRoomConversationView, {
-                  roomStore: desktopRoomStoreLoading, 
                   dispatcher: dispatcher, 
+                  localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
-                  roomState: ROOM_STATES.HAS_PARTICIPANTS})
+                  roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                  roomStore: desktopRoomStoreLoading})
+              )
+            ), 
+
+            React.createElement(FramedExample, {height: 254, 
+                           summary: "Desktop room conversation"}, 
+              React.createElement("div", {className: "fx-embedded"}, 
+                React.createElement(DesktopRoomConversationView, {
+                  dispatcher: dispatcher, 
+                  localPosterUrl: "sample-img/video-screen-local.png", 
+                  mozLoop: navigator.mozLoop, 
+                  remotePosterUrl: "sample-img/video-screen-remote.png", 
+                  roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                  roomStore: roomStore})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 254, 
-              summary: "Desktop room conversation"}, 
+            React.createElement(FramedExample, {dashed: true, 
+                           height: 394, 
+                           summary: "Desktop room conversation local face-mute", 
+                           width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
-                  roomStore: roomStore, 
                   dispatcher: dispatcher, 
                   mozLoop: navigator.mozLoop, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
                   remotePosterUrl: "sample-img/video-screen-remote.png", 
-                  roomState: ROOM_STATES.HAS_PARTICIPANTS})
+                  roomStore: desktopLocalFaceMuteRoomStore})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
-                           summary: "Desktop room conversation local face-mute"}, 
+            React.createElement(FramedExample, {dashed: true, height: 394, 
+                           summary: "Desktop room conversation remote face-mute", 
+                           width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(DesktopRoomConversationView, {
-                  roomStore: desktopLocalFaceMuteRoomStore, 
                   dispatcher: dispatcher, 
+                  localPosterUrl: "sample-img/video-screen-local.png", 
                   mozLoop: navigator.mozLoop, 
-                  remotePosterUrl: "sample-img/video-screen-remote.png"})
-              )
-            ), 
-
-            React.createElement(FramedExample, {width: 298, height: 394, dashed: true, 
-                           summary: "Desktop room conversation remote face-mute"}, 
-              React.createElement("div", {className: "fx-embedded"}, 
-                React.createElement(DesktopRoomConversationView, {
-                  roomStore: desktopRemoteFaceMuteRoomStore, 
-                  dispatcher: dispatcher, 
-                  mozLoop: navigator.mozLoop, 
-                  localPosterUrl: "sample-img/video-screen-local.png"})
+                  roomStore: desktopRemoteFaceMuteRoomStore})
               )
             )
           ), 
 
           React.createElement(Section, {name: "StandaloneRoomView"}, 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone room conversation (ready)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           summary: "Standalone room conversation (ready)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: readyRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: readyRoomStore, 
-                  roomState: ROOM_STATES.READY, 
-                  isFirefox: true})
+                  isFirefox: true, 
+                  roomState: ROOM_STATES.READY})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-              summary: "Standalone room conversation (joined)", 
-              cssClass: "standalone", 
-              onContentsRendered: joinedRoomStore.forcedUpdate}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           onContentsRendered: joinedRoomStore.forcedUpdate, 
+                           summary: "Standalone room conversation (joined)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: joinedRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: joinedRoomStore, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
-                  isFirefox: true})
+                  isFirefox: true, 
+                  localPosterUrl: "sample-img/video-screen-local.png"})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-              summary: "Standalone room conversation (loading remote)", 
-              cssClass: "standalone", 
-              onContentsRendered: loadingRemoteVideoRoomStore.forcedUpdate}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           onContentsRendered: loadingRemoteVideoRoomStore.forcedUpdate, 
+                           summary: "Standalone room conversation (loading remote)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: loadingRemoteVideoRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: loadingRemoteVideoRoomStore, 
-                  localPosterUrl: "sample-img/video-screen-local.png", 
-                  isFirefox: true})
+                  isFirefox: true, 
+                  localPosterUrl: "sample-img/video-screen-local.png"})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
                            onContentsRendered: updatingActiveRoomStore.forcedUpdate, 
-                           summary: "Standalone room conversation (has-participants, 644x483)"}, 
+                           summary: "Standalone room conversation (has-participants, 644x483)", 
+                           width: 644}, 
                 React.createElement("div", {className: "standalone"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: updatingActiveRoomStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: updatingActiveRoomStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
-                    remotePosterUrl: "sample-img/video-screen-remote.png"})
+                    remotePosterUrl: "sample-img/video-screen-remote.png", 
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           onContentsRendered: localFaceMuteRoomStore.forcedUpdate, 
-                           summary: "Standalone room conversation (local face mute, has-participants, 644x483)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 483, 
+              onContentsRendered: localFaceMuteRoomStore.forcedUpdate, 
+              summary: "Standalone room conversation (local face mute, has-participants, 644x483)", 
+              width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: localFaceMuteRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: localFaceMuteRoomStore, 
                   isFirefox: true, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   remotePosterUrl: "sample-img/video-screen-remote.png"})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           onContentsRendered: remoteFaceMuteRoomStore.forcedUpdate, 
-                           summary: "Standalone room conversation (remote face mute, has-participants, 644x483)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 483, 
+              onContentsRendered: remoteFaceMuteRoomStore.forcedUpdate, 
+              summary: "Standalone room conversation (remote face mute, has-participants, 644x483)", 
+              width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: remoteFaceMuteRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: remoteFaceMuteRoomStore, 
                   isFirefox: true, 
                   localPosterUrl: "sample-img/video-screen-local.png", 
                   remotePosterUrl: "sample-img/video-screen-remote.png"})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 800, height: 660, dashed: true, 
-                           cssClass: "standalone", 
-                           onContentsRendered: loadingRemoteLoadingScreenStore.forcedUpdate, 
-              summary: "Standalone room convo (has-participants, loading screen share, loading remote video, 800x660)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 660, 
+              onContentsRendered: loadingRemoteLoadingScreenStore.forcedUpdate, 
+              summary: "Standalone room convo (has-participants, loading screen share, loading remote video, 800x660)", 
+              width: 800}, 
               /* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */
                React.createElement("div", {className: "standalone overflow-hidden"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: loadingRemoteLoadingScreenStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: loadingRemoteLoadingScreenStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
-                    screenSharePosterUrl: "sample-img/video-screen-baz.png"}
-                  )
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                    screenSharePosterUrl: "sample-img/video-screen-baz.png"})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 800, height: 660, dashed: true, 
-                           cssClass: "standalone", 
-                           onContentsRendered: loadingScreenSharingRoomStore.forcedUpdate, 
-              summary: "Standalone room convo (has-participants, loading screen share, 800x660)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 660, 
+              onContentsRendered: loadingScreenSharingRoomStore.forcedUpdate, 
+              summary: "Standalone room convo (has-participants, loading screen share, 800x660)", 
+              width: 800}, 
               /* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */
                React.createElement("div", {className: "standalone overflow-hidden"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: loadingScreenSharingRoomStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: loadingScreenSharingRoomStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
-                    screenSharePosterUrl: "sample-img/video-screen-baz.png"}
-                  )
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                    screenSharePosterUrl: "sample-img/video-screen-baz.png"})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 800, height: 660, dashed: true, 
-                           cssClass: "standalone", 
-                           onContentsRendered: updatingSharingRoomStore.forcedUpdate, 
-              summary: "Standalone room convo (has-participants, receivingScreenShare, 800x660)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 660, 
+              onContentsRendered: updatingSharingRoomStore.forcedUpdate, 
+              summary: "Standalone room convo (has-participants, receivingScreenShare, 800x660)", 
+              width: 800}, 
                 React.createElement("div", {className: "standalone"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: updatingSharingRoomStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: updatingSharingRoomStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
-                    screenSharePosterUrl: "sample-img/video-screen-terminal.png"}
-                  )
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
+                    screenSharePosterUrl: "sample-img/video-screen-terminal.png"})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone room conversation (full - FFx user)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           summary: "Standalone room conversation (full - FFx user)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: fullActiveRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: fullActiveRoomStore, 
                   isFirefox: true})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone room conversation (full - non FFx user)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           summary: "Standalone room conversation (full - non FFx user)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: fullActiveRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: fullActiveRoomStore, 
                   isFirefox: false})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone room conversation (feedback)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           summary: "Standalone room conversation (feedback)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: endedRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: endedRoomStore, 
                   feedbackStore: feedbackStore, 
                   isFirefox: false})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 644, height: 483, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone room conversation (failed)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 483, 
+                           summary: "Standalone room conversation (failed)", 
+                           width: 644}, 
               React.createElement("div", {className: "standalone"}, 
                 React.createElement(StandaloneRoomView, {
+                  activeRoomStore: failedRoomStore, 
                   dispatcher: dispatcher, 
-                  activeRoomStore: failedRoomStore, 
                   isFirefox: false})
               )
             )
           ), 
 
           React.createElement(Section, {name: "StandaloneRoomView (Mobile)"}, 
-            React.createElement(FramedExample, {width: 600, height: 480, cssClass: "standalone", 
-                           dashed: true, 
-                           onContentsRendered: updatingActiveRoomStore.forcedUpdate, 
-                           summary: "Standalone room conversation (has-participants, 600x480)"}, 
+            React.createElement(FramedExample, {
+              cssClass: "standalone", 
+              dashed: true, 
+              height: 480, 
+              onContentsRendered: updatingActiveRoomStore.forcedUpdate, 
+              summary: "Standalone room conversation (has-participants, 600x480)", 
+              width: 600}, 
                 React.createElement("div", {className: "standalone"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: updatingActiveRoomStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: updatingActiveRoomStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
-                    remotePosterUrl: "sample-img/video-screen-remote.png"})
+                    remotePosterUrl: "sample-img/video-screen-remote.png", 
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS})
                 )
             ), 
 
-            React.createElement(FramedExample, {width: 600, height: 480, 
-                           onContentsRendered: updatingSharingRoomStore.forcedUpdate, 
-              summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)"}, 
+            React.createElement(FramedExample, {
+              height: 480, 
+              onContentsRendered: updatingSharingRoomStore.forcedUpdate, 
+              summary: "Standalone room convo (has-participants, receivingScreenShare, 600x480)", 
+              width: 600}, 
                 React.createElement("div", {className: "standalone", cssClass: "standalone"}, 
                   React.createElement(StandaloneRoomView, {
+                    activeRoomStore: updatingSharingRoomStore, 
                     dispatcher: dispatcher, 
-                    activeRoomStore: updatingSharingRoomStore, 
-                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     isFirefox: true, 
                     localPosterUrl: "sample-img/video-screen-local.png", 
                     remotePosterUrl: "sample-img/video-screen-remote.png", 
+                    roomState: ROOM_STATES.HAS_PARTICIPANTS, 
                     screenSharePosterUrl: "sample-img/video-screen-terminal.png"})
                 )
             )
           ), 
 
           React.createElement(Section, {name: "TextChatView"}, 
-            React.createElement(FramedExample, {width: 298, height: 160, dashed: true, 
-                           summary: "TextChatView: desktop embedded"}, 
+            React.createElement(FramedExample, {dashed: true, 
+                           height: 160, 
+                           summary: "TextChatView: desktop embedded", 
+                           width: 298}, 
               React.createElement("div", {className: "fx-embedded"}, 
                 React.createElement(TextChatView, {dispatcher: dispatcher, 
                               showAlways: false, 
                               showRoomName: false})
               )
             ), 
 
-            React.createElement(FramedExample, {width: 200, height: 400, dashed: true, 
-                           cssClass: "standalone", 
-                           summary: "Standalone Text Chat conversation (200x400)"}, 
+            React.createElement(FramedExample, {cssClass: "standalone", 
+                           dashed: true, 
+                           height: 400, 
+                           summary: "Standalone Text Chat conversation (200x400)", 
+                           width: 200}, 
               React.createElement("div", {className: "standalone text-chat-example"}, 
                 React.createElement("div", {className: "media-wrapper"}, 
                   React.createElement(TextChatView, {
                     dispatcher: dispatcher, 
                     showAlways: true, 
                     showRoomName: true})
                 )
               )
             )
           ), 
 
-          React.createElement(Section, {name: "SVG icons preview", className: "svg-icons"}, 
+          React.createElement(Section, {className: "svg-icons", name: "SVG icons preview"}, 
             React.createElement(Example, {summary: "10x10"}, 
               React.createElement(SVGIcons, {size: "10x10"})
             ), 
             React.createElement(Example, {summary: "14x14"}, 
               React.createElement(SVGIcons, {size: "14x14"})
             ), 
             React.createElement(Example, {summary: "16x16"}, 
               React.createElement(SVGIcons, {size: "16x16"})
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -380,19 +380,19 @@
     detailsButtonLabel: "Retry"
   });
 
   var SVGIcon = React.createClass({
     render: function() {
       var sizeUnit = this.props.size.split("x");
       return (
         <img className="svg-icon"
+             height={sizeUnit[1]}
              src={"../content/shared/img/icons-" + this.props.size + ".svg#" + this.props.shapeId}
-             width={sizeUnit[0]}
-             height={sizeUnit[1]} />
+             width={sizeUnit[0]} />
       );
     }
   });
 
   var SVGIcons = React.createClass({
     shapes: {
       "10x10": ["close", "close-active", "close-disabled", "dropdown",
         "dropdown-white", "dropdown-active", "dropdown-disabled", "edit",
@@ -416,35 +416,35 @@
         "tag-hover", "tag-active", "trash", "unblock", "unblock-hover", "unblock-active",
         "video", "video-hover", "video-active", "tour"
       ]
     },
 
     render: function() {
       var icons = this.shapes[this.props.size].map(function(shapeId, i) {
         return (
-          <li key={this.props.size + "-" + i} className="svg-icon-entry">
+          <li className="svg-icon-entry" key={this.props.size + "-" + i}>
             <p><SVGIcon shapeId={shapeId} size={this.props.size} /></p>
             <p>{shapeId}</p>
           </li>
         );
       }, this);
       return (
         <ul className="svg-icon-list">{icons}</ul>
       );
     }
   });
 
   var FramedExample = React.createClass({
     propTypes: {
-      width: React.PropTypes.number,
+      cssClass: React.PropTypes.string,
+      dashed: React.PropTypes.bool,
       height: React.PropTypes.number,
       onContentsRendered: React.PropTypes.func,
-      dashed: React.PropTypes.bool,
-      cssClass: React.PropTypes.string
+      width: React.PropTypes.number
     },
 
     makeId: function(prefix) {
       return (prefix || "") + this.props.summary.toLowerCase().replace(/\s/g, "-");
     },
 
     render: function() {
       var height = this.props.height;
@@ -459,20 +459,21 @@
       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 width={width} height={height}
+            <Frame className={cx({dashed: this.props.dashed})}
+                   cssClass={this.props.cssClass}
+                   height={height}
                    onContentsRendered={this.props.onContentsRendered}
-                   className={cx({dashed: this.props.dashed})}
-                   cssClass={this.props.cssClass}>
+                   width={width}>
               {this.props.children}
             </Frame>
           </div>
         </div>
       );
     }
   });
 
@@ -496,17 +497,17 @@
         </div>
       );
     }
   });
 
   var Section = React.createClass({
     render: function() {
       return (
-        <section id={this.props.name} className={this.props.className}>
+        <section className={this.props.className} id={this.props.name}>
           <h1>{this.props.name}</h1>
           {this.props.children}
         </section>
       );
     }
   });
 
   var ShowCase = React.createClass({
@@ -539,17 +540,17 @@
         document.documentElement.setAttribute("lang", "ar");
         document.documentElement.setAttribute("dir", "rtl");
       }
 
       return (
         <div className="showcase">
           <header>
             <h1>Loop UI Components Showcase</h1>
-            <Checkbox label="RTL mode?" checked={this.state.rtlMode}
+            <Checkbox checked={this.state.rtlMode} label="RTL mode?"
               onChange={this._handleCheckboxChange} />
             <nav className="showcase-menu">{
               React.Children.map(this.props.children, function(section) {
                 return (
                   <a className="btn btn-info" href={"#" + section.props.name}>
                     {section.props.name}
                   </a>
                 );
@@ -566,252 +567,273 @@
 
     render: function() {
       return (
         <ShowCase>
           <Section name="PanelView">
             <p className="note">
               <strong>Note:</strong> 332px wide.
             </p>
-            <Example summary="Re-sign-in view" dashed="true" style={{width: "332px"}}>
+            <Example dashed="true" style={{width: "332px"}} summary="Re-sign-in view">
               <SignInRequestView mozLoop={mockMozLoopRooms} />
             </Example>
-            <Example summary="Room list tab" dashed="true" style={{width: "332px"}}>
-              <PanelView client={mockClient} notifications={notifications}
-                         userProfile={{email: "test@example.com"}}
+            <Example dashed="true" style={{width: "332px"}} summary="Room list tab">
+              <PanelView client={mockClient}
+                         dispatcher={dispatcher}
                          mozLoop={mockMozLoopRooms}
-                         dispatcher={dispatcher}
+                         notifications={notifications}
                          roomStore={roomStore}
-                         selectedTab="rooms" />
+                         selectedTab="rooms"
+                         userProfile={{email: "test@example.com"}} />
             </Example>
-            <Example summary="Contact list tab" dashed="true" style={{width: "332px"}}>
-              <PanelView client={mockClient} notifications={notifications}
-                         userProfile={{email: "test@example.com"}}
+            <Example dashed="true" style={{width: "332px"}} summary="Contact list tab">
+              <PanelView client={mockClient}
+                         dispatcher={dispatcher}
                          mozLoop={mockMozLoopRooms}
-                         dispatcher={dispatcher}
+                         notifications={notifications}
                          roomStore={roomStore}
-                         selectedTab="contacts" />
+                         selectedTab="contacts"
+                         userProfile={{email: "test@example.com"}} />
             </Example>
-            <Example summary="Error Notification" dashed="true" style={{width: "332px"}}>
-              <PanelView client={mockClient} notifications={errNotifications}
+            <Example dashed="true" style={{width: "332px"}} summary="Error Notification">
+              <PanelView client={mockClient}
+                         dispatcher={dispatcher}
                          mozLoop={navigator.mozLoop}
-                         dispatcher={dispatcher}
+                         notifications={errNotifications}
                          roomStore={roomStore} />
             </Example>
-            <Example summary="Error Notification - authenticated" dashed="true" style={{width: "332px"}}>
-              <PanelView client={mockClient} notifications={errNotifications}
-                         userProfile={{email: "test@example.com"}}
-                         mozLoop={navigator.mozLoop}
+            <Example dashed="true" style={{width: "332px"}} summary="Error Notification - authenticated">
+              <PanelView client={mockClient}
                          dispatcher={dispatcher}
-                         roomStore={roomStore} />
+                         mozLoop={navigator.mozLoop}
+                         notifications={errNotifications}
+                         roomStore={roomStore}
+                         userProfile={{email: "test@example.com"}} />
             </Example>
-            <Example summary="Contact import success" dashed="true" style={{width: "332px"}}>
-              <PanelView notifications={new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}])}
-                         userProfile={{email: "test@example.com"}}
+            <Example dashed="true" style={{width: "332px"}} summary="Contact import success">
+              <PanelView dispatcher={dispatcher}
                          mozLoop={mockMozLoopRooms}
-                         dispatcher={dispatcher}
+                         notifications={new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}])}
                          roomStore={roomStore}
-                         selectedTab="contacts" />
+                         selectedTab="contacts"
+                         userProfile={{email: "test@example.com"}} />
             </Example>
-            <Example summary="Contact import error" dashed="true" style={{width: "332px"}}>
-              <PanelView notifications={new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}])}
-                         userProfile={{email: "test@example.com"}}
+            <Example dashed="true" style={{width: "332px"}} summary="Contact import error">
+              <PanelView dispatcher={dispatcher}
                          mozLoop={mockMozLoopRooms}
-                         dispatcher={dispatcher}
+                         notifications={new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}])}
                          roomStore={roomStore}
-                         selectedTab="contacts" />
+                         selectedTab="contacts"
+                         userProfile={{email: "test@example.com"}} />
             </Example>
           </Section>
 
           <Section name="AcceptCallView">
-            <Example summary="Default / incoming video call" dashed="true" style={{width: "300px", height: "272px"}}>
+            <Example dashed="true" style={{width: "300px", height: "272px"}}
+                     summary="Default / incoming video call">
               <div className="fx-embedded">
                 <AcceptCallView callType={CALL_TYPES.AUDIO_VIDEO}
                                 callerId="Mr Smith"
                                 dispatcher={dispatcher}
                                 mozLoop={mockMozLoopRooms} />
               </div>
             </Example>
 
-            <Example summary="Default / incoming audio only call" dashed="true" style={{width: "300px", height: "272px"}}>
+            <Example dashed="true" style={{width: "300px", height: "272px"}}
+                     summary="Default / incoming audio only call">
               <div className="fx-embedded">
                 <AcceptCallView callType={CALL_TYPES.AUDIO_ONLY}
                                 callerId="Mr Smith"
                                 dispatcher={dispatcher}
                                 mozLoop={mockMozLoopRooms} />
               </div>
             </Example>
           </Section>
 
           <Section name="AcceptCallView-ActiveState">
-            <Example summary="Default" dashed="true" style={{width: "300px", height: "272px"}}>
+            <Example dashed="true" style={{width: "300px", height: "272px"}}
+                     summary="Default">
               <div className="fx-embedded" >
                 <AcceptCallView callType={CALL_TYPES.AUDIO_VIDEO}
                                 callerId="Mr Smith"
                                 dispatcher={dispatcher}
                                 mozLoop={mockMozLoopRooms}
                                 showMenu={true} />
               </div>
             </Example>
           </Section>
 
           <Section name="ConversationToolbar">
             <h2>Desktop Conversation Window</h2>
             <div className="fx-embedded override-position">
-              <Example summary="Default" style={{width: "300px", height: "26px"}}>
-                <ConversationToolbar video={{enabled: true}}
-                                     audio={{enabled: true}}
+              <Example style={{width: "300px", height: "26px"}} summary="Default">
+                <ConversationToolbar audio={{enabled: true}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: true}} />
               </Example>
-              <Example summary="Video muted" style={{width: "300px", height: "26px"}}>
-                <ConversationToolbar video={{enabled: false}}
-                                     audio={{enabled: true}}
+              <Example style={{width: "300px", height: "26px"}} summary="Video muted">
+                <ConversationToolbar audio={{enabled: true}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: false}} />
               </Example>
-              <Example summary="Audio muted" style={{width: "300px", height: "26px"}}>
-                <ConversationToolbar video={{enabled: true}}
-                                     audio={{enabled: false}}
+              <Example style={{width: "300px", height: "26px"}} summary="Audio muted">
+                <ConversationToolbar audio={{enabled: false}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: true}} />
               </Example>
             </div>
 
             <h2>Standalone</h2>
             <div className="standalone override-position">
               <Example summary="Default">
-                <ConversationToolbar video={{enabled: true}}
-                                     audio={{enabled: true}}
+                <ConversationToolbar audio={{enabled: true}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: true}} />
               </Example>
               <Example summary="Video muted">
-                <ConversationToolbar video={{enabled: false}}
-                                     audio={{enabled: true}}
+                <ConversationToolbar audio={{enabled: true}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: false}} />
               </Example>
               <Example summary="Audio muted">
-                <ConversationToolbar video={{enabled: true}}
-                                     audio={{enabled: false}}
+                <ConversationToolbar audio={{enabled: false}}
                                      hangup={noop}
-                                     publishStream={noop} />
+                                     publishStream={noop}
+                                     video={{enabled: true}} />
               </Example>
             </div>
           </Section>
 
           <Section name="PendingConversationView (Desktop)">
-            <Example summary="Connecting" dashed="true"
-                     style={{width: "300px", height: "272px"}}>
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Connecting">
               <div className="fx-embedded">
                 <DesktopPendingConversationView callState={"gather"}
                                                 contact={mockContact}
                                                 dispatcher={dispatcher} />
               </div>
             </Example>
           </Section>
 
           <Section name="CallFailedView">
-            <Example summary="Call Failed - Incoming" dashed="true"
-                     style={{width: "300px", height: "272px"}}>
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Call Failed - Incoming">
               <div className="fx-embedded">
                 <CallFailedView dispatcher={dispatcher}
                                 outgoing={false}
                                 store={conversationStore} />
               </div>
             </Example>
-            <Example summary="Call Failed - Outgoing" dashed="true"
-                     style={{width: "300px", height: "272px"}}>
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Call Failed - Outgoing">
               <div className="fx-embedded">
                 <CallFailedView dispatcher={dispatcher}
                                 outgoing={true}
                                 store={conversationStore} />
               </div>
             </Example>
-            <Example summary="Call Failed — with call URL error" dashed="true"
-                     style={{width: "300px", height: "272px"}}>
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Call Failed — with call URL error">
               <div className="fx-embedded">
                 <CallFailedView dispatcher={dispatcher} emailLinkError={true}
                                 outgoing={true}
                                 store={conversationStore} />
               </div>
             </Example>
           </Section>
 
           <Section name="OngoingConversationView">
-            <FramedExample width={298} height={254}
-                           summary="Desktop ongoing conversation window">
+            <FramedExample height={254}
+                           summary="Desktop ongoing conversation window"
+                           width={298}>
               <div className="fx-embedded">
                 <OngoingConversationView
+                  audio={{enabled: true}}
                   dispatcher={dispatcher}
-                  video={{enabled: true}}
-                  audio={{enabled: true}}
                   localPosterUrl="sample-img/video-screen-local.png"
+                  mediaConnected={true}
                   remotePosterUrl="sample-img/video-screen-remote.png"
                   remoteVideoEnabled={true}
-                  mediaConnected={true} />
+                  video={{enabled: true}} />
               </div>
             </FramedExample>
 
-            <FramedExample width={800} height={600}
-                           summary="Desktop ongoing conversation window large">
+            <FramedExample height={600}
+                           summary="Desktop ongoing conversation window large"
+                           width={800}>
                 <div className="fx-embedded">
                   <OngoingConversationView
+                    audio={{enabled: true}}
                     dispatcher={dispatcher}
-                    video={{enabled: true}}
-                    audio={{enabled: true}}
                     localPosterUrl="sample-img/video-screen-local.png"
+                    mediaConnected={true}
                     remotePosterUrl="sample-img/video-screen-remote.png"
                     remoteVideoEnabled={true}
-                    mediaConnected={true} />
+                    video={{enabled: true}} />
                 </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
-              summary="Desktop ongoing conversation window - local face mute">
+            <FramedExample height={254}
+              summary="Desktop ongoing conversation window - local face mute"
+              width={298} >
               <div className="fx-embedded">
                 <OngoingConversationView
+                  audio={{enabled: true}}
                   dispatcher={dispatcher}
-                  video={{enabled: false}}
-                  audio={{enabled: true}}
+                  mediaConnected={true}
+                  remotePosterUrl="sample-img/video-screen-remote.png"
                   remoteVideoEnabled={true}
-                  remotePosterUrl="sample-img/video-screen-remote.png"
-                  mediaConnected={true} />
+                  video={{enabled: false}} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
-              summary="Desktop ongoing conversation window - remote face mute">
+            <FramedExample height={254}
+              summary="Desktop ongoing conversation window - remote face mute"
+              width={298} >
               <div className="fx-embedded">
                 <OngoingConversationView
+                  audio={{enabled: true}}
                   dispatcher={dispatcher}
-                  video={{enabled: true}}
-                  audio={{enabled: true}}
+                  localPosterUrl="sample-img/video-screen-local.png"
+                  mediaConnected={true}
                   remoteVideoEnabled={false}
-                  localPosterUrl="sample-img/video-screen-local.png"
-                  mediaConnected={true} />
+                  video={{enabled: true}} />
               </div>
             </FramedExample>
 
           </Section>
 
           <Section name="FeedbackView">
             <p className="note">
               <strong>Note:</strong> For the useable demo, you can access submitted data at&nbsp;
               <a href="https://input.allizom.org/">input.allizom.org</a>.
             </p>
-            <Example summary="Default (useable demo)" dashed="true" style={{width: "300px", height: "272px"}}>
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Default (useable demo)">
               <FeedbackView feedbackStore={feedbackStore} />
             </Example>
-            <Example summary="Detailed form" dashed="true" style={{width: "300px", height: "272px"}}>
-              <FeedbackView feedbackStore={feedbackStore} feedbackState={FEEDBACK_STATES.DETAILS} />
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Detailed form">
+              <FeedbackView feedbackState={FEEDBACK_STATES.DETAILS} feedbackStore={feedbackStore} />
             </Example>
-            <Example summary="Thank you!" dashed="true" style={{width: "300px", height: "272px"}}>
-              <FeedbackView feedbackStore={feedbackStore} feedbackState={FEEDBACK_STATES.SENT} />
+            <Example dashed="true"
+                     style={{width: "300px", height: "272px"}}
+                     summary="Thank you!">
+              <FeedbackView feedbackState={FEEDBACK_STATES.SENT} feedbackStore={feedbackStore}/>
             </Example>
           </Section>
 
           <Section name="AlertMessages">
             <Example summary="Various alerts">
               <div className="alert alert-warning">
                 <button className="close"></button>
                 <p className="message">
@@ -840,319 +862,364 @@
             <Example summary="Standalone Unsupported Device">
               <div className="standalone">
                 <UnsupportedDeviceView platform="ios"/>
               </div>
             </Example>
           </Section>
 
           <Section name="DesktopRoomConversationView">
-            <FramedExample width={298} height={254}
-              summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)">
+            <FramedExample
+              height={254}
+              summary="Desktop room conversation (invitation, text-chat inclusion/scrollbars don't happen in real client)"
+              width={298}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
-                  roomStore={invitationRoomStore}
                   dispatcher={dispatcher}
+                  localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  localPosterUrl="sample-img/video-screen-local.png"
-                  roomState={ROOM_STATES.INIT} />
+                  roomState={ROOM_STATES.INIT}
+                  roomStore={invitationRoomStore} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={394} dashed={true}
-              summary="Desktop room conversation (loading)">
+            <FramedExample
+              dashed={true}
+              height={394}
+              summary="Desktop room conversation (loading)"
+              width={298}>
               {/* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */}
               <div className="fx-embedded overflow-hidden">
                 <DesktopRoomConversationView
-                  roomStore={desktopRoomStoreLoading}
                   dispatcher={dispatcher}
+                  localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  localPosterUrl="sample-img/video-screen-local.png"
                   remotePosterUrl="sample-img/video-screen-remote.png"
-                  roomState={ROOM_STATES.HAS_PARTICIPANTS} />
+                  roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                  roomStore={desktopRoomStoreLoading} />
+              </div>
+            </FramedExample>
+
+            <FramedExample height={254}
+                           summary="Desktop room conversation">
+              <div className="fx-embedded">
+                <DesktopRoomConversationView
+                  dispatcher={dispatcher}
+                  localPosterUrl="sample-img/video-screen-local.png"
+                  mozLoop={navigator.mozLoop}
+                  remotePosterUrl="sample-img/video-screen-remote.png"
+                  roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                  roomStore={roomStore} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={254}
-              summary="Desktop room conversation">
+            <FramedExample dashed={true}
+                           height={394}
+                           summary="Desktop room conversation local face-mute"
+                           width={298}>
               <div className="fx-embedded">
                 <DesktopRoomConversationView
-                  roomStore={roomStore}
                   dispatcher={dispatcher}
                   mozLoop={navigator.mozLoop}
-                  localPosterUrl="sample-img/video-screen-local.png"
                   remotePosterUrl="sample-img/video-screen-remote.png"
-                  roomState={ROOM_STATES.HAS_PARTICIPANTS} />
+                  roomStore={desktopLocalFaceMuteRoomStore} />
               </div>
             </FramedExample>
 
-            <FramedExample width={298} height={394} dashed={true}
-                           summary="Desktop room conversation local face-mute">
+            <FramedExample dashed={true} height={394}
+                           summary="Desktop room conversation remote face-mute"
+                           width={298} >
               <div className="fx-embedded">
                 <DesktopRoomConversationView
-                  roomStore={desktopLocalFaceMuteRoomStore}
                   dispatcher={dispatcher}
+                  localPosterUrl="sample-img/video-screen-local.png"
                   mozLoop={navigator.mozLoop}
-                  remotePosterUrl="sample-img/video-screen-remote.png" />
-              </div>
-            </FramedExample>
-
-            <FramedExample width={298} height={394} dashed={true}
-                           summary="Desktop room conversation remote face-mute">
-              <div className="fx-embedded">
-                <DesktopRoomConversationView
-                  roomStore={desktopRemoteFaceMuteRoomStore}
-                  dispatcher={dispatcher}
-                  mozLoop={navigator.mozLoop}
-                  localPosterUrl="sample-img/video-screen-local.png" />
+                  roomStore={desktopRemoteFaceMuteRoomStore} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="StandaloneRoomView">
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone room conversation (ready)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           summary="Standalone room conversation (ready)"
+                           width={644} >
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={readyRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={readyRoomStore}
-                  roomState={ROOM_STATES.READY}
-                  isFirefox={true} />
+                  isFirefox={true}
+                  roomState={ROOM_STATES.READY} />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-              summary="Standalone room conversation (joined)"
-              cssClass="standalone"
-              onContentsRendered={joinedRoomStore.forcedUpdate}>
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           onContentsRendered={joinedRoomStore.forcedUpdate}
+                           summary="Standalone room conversation (joined)"
+                           width={644}>
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={joinedRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={joinedRoomStore}
-                  localPosterUrl="sample-img/video-screen-local.png"
-                  isFirefox={true} />
+                  isFirefox={true}
+                  localPosterUrl="sample-img/video-screen-local.png" />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-              summary="Standalone room conversation (loading remote)"
-              cssClass="standalone"
-              onContentsRendered={loadingRemoteVideoRoomStore.forcedUpdate}>
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           onContentsRendered={loadingRemoteVideoRoomStore.forcedUpdate}
+                           summary="Standalone room conversation (loading remote)"
+                           width={644}>
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={loadingRemoteVideoRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={loadingRemoteVideoRoomStore}
-                  localPosterUrl="sample-img/video-screen-local.png"
-                  isFirefox={true} />
+                  isFirefox={true}
+                  localPosterUrl="sample-img/video-screen-local.png" />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
                            onContentsRendered={updatingActiveRoomStore.forcedUpdate}
-                           summary="Standalone room conversation (has-participants, 644x483)">
+                           summary="Standalone room conversation (has-participants, 644x483)"
+                           width={644} >
                 <div className="standalone">
                   <StandaloneRoomView
+                    activeRoomStore={updatingActiveRoomStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={updatingActiveRoomStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
-                    remotePosterUrl="sample-img/video-screen-remote.png" />
+                    remotePosterUrl="sample-img/video-screen-remote.png"
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS} />
                 </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           onContentsRendered={localFaceMuteRoomStore.forcedUpdate}
-                           summary="Standalone room conversation (local face mute, has-participants, 644x483)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={483}
+              onContentsRendered={localFaceMuteRoomStore.forcedUpdate}
+              summary="Standalone room conversation (local face mute, has-participants, 644x483)"
+              width={644}>
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={localFaceMuteRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={localFaceMuteRoomStore}
                   isFirefox={true}
                   localPosterUrl="sample-img/video-screen-local.png"
                   remotePosterUrl="sample-img/video-screen-remote.png" />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           onContentsRendered={remoteFaceMuteRoomStore.forcedUpdate}
-                           summary="Standalone room conversation (remote face mute, has-participants, 644x483)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={483}
+              onContentsRendered={remoteFaceMuteRoomStore.forcedUpdate}
+              summary="Standalone room conversation (remote face mute, has-participants, 644x483)"
+              width={644}>
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={remoteFaceMuteRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={remoteFaceMuteRoomStore}
                   isFirefox={true}
                   localPosterUrl="sample-img/video-screen-local.png"
                   remotePosterUrl="sample-img/video-screen-remote.png" />
               </div>
             </FramedExample>
 
-            <FramedExample width={800} height={660} dashed={true}
-                           cssClass="standalone"
-                           onContentsRendered={loadingRemoteLoadingScreenStore.forcedUpdate}
-              summary="Standalone room convo (has-participants, loading screen share, loading remote video, 800x660)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={660}
+              onContentsRendered={loadingRemoteLoadingScreenStore.forcedUpdate}
+              summary="Standalone room convo (has-participants, loading screen share, loading remote video, 800x660)"
+              width={800}>
               {/* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */}
                <div className="standalone overflow-hidden">
                   <StandaloneRoomView
+                    activeRoomStore={loadingRemoteLoadingScreenStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={loadingRemoteLoadingScreenStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png"
-                    screenSharePosterUrl="sample-img/video-screen-baz.png"
-                  />
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                    screenSharePosterUrl="sample-img/video-screen-baz.png" />
                 </div>
             </FramedExample>
 
-            <FramedExample width={800} height={660} dashed={true}
-                           cssClass="standalone"
-                           onContentsRendered={loadingScreenSharingRoomStore.forcedUpdate}
-              summary="Standalone room convo (has-participants, loading screen share, 800x660)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={660}
+              onContentsRendered={loadingScreenSharingRoomStore.forcedUpdate}
+              summary="Standalone room convo (has-participants, loading screen share, 800x660)"
+              width={800}>
               {/* Hide scrollbars here. Rotating loading div overflows and causes
                scrollbars to appear */}
                <div className="standalone overflow-hidden">
                   <StandaloneRoomView
+                    activeRoomStore={loadingScreenSharingRoomStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={loadingScreenSharingRoomStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png"
-                    screenSharePosterUrl="sample-img/video-screen-baz.png"
-                  />
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                    screenSharePosterUrl="sample-img/video-screen-baz.png" />
                 </div>
             </FramedExample>
 
-            <FramedExample width={800} height={660} dashed={true}
-                           cssClass="standalone"
-                           onContentsRendered={updatingSharingRoomStore.forcedUpdate}
-              summary="Standalone room convo (has-participants, receivingScreenShare, 800x660)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={660}
+              onContentsRendered={updatingSharingRoomStore.forcedUpdate}
+              summary="Standalone room convo (has-participants, receivingScreenShare, 800x660)"
+              width={800}>
                 <div className="standalone">
                   <StandaloneRoomView
+                    activeRoomStore={updatingSharingRoomStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={updatingSharingRoomStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png"
-                    screenSharePosterUrl="sample-img/video-screen-terminal.png"
-                  />
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
+                    screenSharePosterUrl="sample-img/video-screen-terminal.png" />
                 </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone room conversation (full - FFx user)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           summary="Standalone room conversation (full - FFx user)"
+                           width={644} >
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={fullActiveRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={fullActiveRoomStore}
                   isFirefox={true} />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone room conversation (full - non FFx user)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           summary="Standalone room conversation (full - non FFx user)"
+                           width={644} >
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={fullActiveRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={fullActiveRoomStore}
                   isFirefox={false} />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone room conversation (feedback)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           summary="Standalone room conversation (feedback)"
+                           width={644}>
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={endedRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={endedRoomStore}
                   feedbackStore={feedbackStore}
                   isFirefox={false} />
               </div>
             </FramedExample>
 
-            <FramedExample width={644} height={483} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone room conversation (failed)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={483}
+                           summary="Standalone room conversation (failed)"
+                           width={644} >
               <div className="standalone">
                 <StandaloneRoomView
+                  activeRoomStore={failedRoomStore}
                   dispatcher={dispatcher}
-                  activeRoomStore={failedRoomStore}
                   isFirefox={false} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="StandaloneRoomView (Mobile)">
-            <FramedExample width={600} height={480} cssClass="standalone"
-                           dashed={true}
-                           onContentsRendered={updatingActiveRoomStore.forcedUpdate}
-                           summary="Standalone room conversation (has-participants, 600x480)">
+            <FramedExample
+              cssClass="standalone"
+              dashed={true}
+              height={480}
+              onContentsRendered={updatingActiveRoomStore.forcedUpdate}
+              summary="Standalone room conversation (has-participants, 600x480)"
+              width={600}>
                 <div className="standalone">
                   <StandaloneRoomView
+                    activeRoomStore={updatingActiveRoomStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={updatingActiveRoomStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
-                    remotePosterUrl="sample-img/video-screen-remote.png" />
+                    remotePosterUrl="sample-img/video-screen-remote.png"
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS} />
                 </div>
             </FramedExample>
 
-            <FramedExample width={600} height={480}
-                           onContentsRendered={updatingSharingRoomStore.forcedUpdate}
-              summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)">
+            <FramedExample
+              height={480}
+              onContentsRendered={updatingSharingRoomStore.forcedUpdate}
+              summary="Standalone room convo (has-participants, receivingScreenShare, 600x480)"
+              width={600} >
                 <div className="standalone" cssClass="standalone">
                   <StandaloneRoomView
+                    activeRoomStore={updatingSharingRoomStore}
                     dispatcher={dispatcher}
-                    activeRoomStore={updatingSharingRoomStore}
-                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     isFirefox={true}
                     localPosterUrl="sample-img/video-screen-local.png"
                     remotePosterUrl="sample-img/video-screen-remote.png"
+                    roomState={ROOM_STATES.HAS_PARTICIPANTS}
                     screenSharePosterUrl="sample-img/video-screen-terminal.png" />
                 </div>
             </FramedExample>
           </Section>
 
           <Section name="TextChatView">
-            <FramedExample width={298} height={160} dashed={true}
-                           summary="TextChatView: desktop embedded">
+            <FramedExample dashed={true}
+                           height={160}
+                           summary="TextChatView: desktop embedded"
+                           width={298}>
               <div className="fx-embedded">
                 <TextChatView dispatcher={dispatcher}
                               showAlways={false}
                               showRoomName={false} />
               </div>
             </FramedExample>
 
-            <FramedExample width={200} height={400} dashed={true}
-                           cssClass="standalone"
-                           summary="Standalone Text Chat conversation (200x400)">
+            <FramedExample cssClass="standalone"
+                           dashed={true}
+                           height={400}
+                           summary="Standalone Text Chat conversation (200x400)"
+                           width={200}>
               <div className="standalone text-chat-example">
                 <div className="media-wrapper">
                   <TextChatView
                     dispatcher={dispatcher}
                     showAlways={true}
                     showRoomName={true} />
                 </div>
               </div>
             </FramedExample>
           </Section>
 
-          <Section name="SVG icons preview" className="svg-icons">
+          <Section className="svg-icons" name="SVG icons preview">
             <Example summary="10x10">
               <SVGIcons size="10x10"/>
             </Example>
             <Example summary="14x14">
               <SVGIcons size="14x14" />
             </Example>
             <Example summary="16x16">
               <SVGIcons size="16x16"/>
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -1203,32 +1203,32 @@ SourceScripts.prototype = {
   _cache: new Map(),
 
   /**
    * Connect to the current thread client.
    */
   connect: function() {
     dumpn("SourceScripts is connecting...");
     this.debuggerClient.addListener("newGlobal", this._onNewGlobal);
-    this.debuggerClient.addListener("newSource", this._onNewSource);
+    this.activeThread.addListener("newSource", this._onNewSource);
     this.activeThread.addListener("blackboxchange", this._onBlackBoxChange);
     this.activeThread.addListener("prettyprintchange", this._onPrettyPrintChange);
     this.handleTabNavigation();
   },
 
   /**
    * Disconnect from the client.
    */
   disconnect: function() {
     if (!this.activeThread) {
       return;
     }
     dumpn("SourceScripts is disconnecting...");
     this.debuggerClient.removeListener("newGlobal", this._onNewGlobal);
-    this.debuggerClient.removeListener("newSource", this._onNewSource);
+    this.activeThread.removeListener("newSource", this._onNewSource);
     this.activeThread.removeListener("blackboxchange", this._onBlackBoxChange);
     this.activeThread.addListener("prettyprintchange", this._onPrettyPrintChange);
   },
 
   /**
    * Clears all the cached source contents.
    */
   clearCache: function() {
--- a/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
+++ b/browser/devtools/debugger/test/browser_dbg_chrome-debugging.js
@@ -40,22 +40,22 @@ function test() {
 
     testChromeActor();
   });
 }
 
 function testChromeActor() {
   gClient.getProcess().then(aResponse => {
     gClient.addListener("newGlobal", onNewGlobal);
-    gClient.addListener("newSource", onNewSource);
 
     let actor = aResponse.form.actor;
     gClient.attachTab(actor, (response, tabClient) => {
       tabClient.attachThread(null, (aResponse, aThreadClient) => {
         gThreadClient = aThreadClient;
+        gThreadClient.addListener("newSource", onNewSource);
 
         if (aResponse.error) {
           ok(false, "Couldn't attach to the chrome debugger.");
           gAttached.reject();
         } else {
           ok(true, "Attached to the chrome debugger.");
           gAttached.resolve();
 
@@ -73,17 +73,17 @@ function onNewGlobal() {
   gClient.removeListener("newGlobal", onNewGlobal);
   gNewGlobal.resolve();
 }
 
 function onNewSource(aEvent, aPacket) {
   if (aPacket.source.url.startsWith("chrome:")) {
     ok(true, "Received a new chrome source: " + aPacket.source.url);
 
-    gClient.removeListener("newSource", onNewSource);
+    gThreadClient.removeListener("newSource", onNewSource);
     gNewChromeSource.resolve();
   }
 }
 
 function resumeAndCloseConnection() {
   let deferred = promise.defer();
   gThreadClient.resume(() => gClient.close(deferred.resolve));
   return deferred.promise;
--- a/browser/devtools/inspector/test/browser_inspector_highlighter-options.js
+++ b/browser/devtools/inspector/test/browser_inspector_highlighter-options.js
@@ -138,16 +138,56 @@ const TEST_DATA = [
       let {points} = yield getHighlighterRegionPath("margin", toolbox.highlighter);
       points = points[0];
 
       is(Math.ceil(topY1), points[0][1], "Top guide's y1 is correct");
       is(Math.floor(rightX1), points[1][0], "Right guide's x1 is correct");
       is(Math.floor(bottomY1), points[2][1], "Bottom guide's y1 is correct");
       is(Math.ceil(leftX1), points[3][0], "Left guide's x1 is correct");
     }
+  },
+  {
+    desc: "When showOnly is used, other regions can be faded",
+    options: {showOnly: "margin", onlyRegionArea: true},
+    checkHighlighter: function*(toolbox) {
+      let h = toolbox.highlighter;
+
+      for (let region of ["margin", "border", "padding", "content"]) {
+        let {d} = yield getHighlighterRegionPath(region, h);
+        ok(d, "Region " + region + " is shown (it has a d attribute)");
+
+        let faded = yield getHighlighterNodeAttribute(h,
+                          "box-model-" + region, "faded");
+        if (region === "margin") {
+          ok(!faded, "The margin region is not faded");
+        } else {
+          is(faded, "true", "Region " + region + " is faded");
+        }
+      }
+    }
+  },
+  {
+    desc: "When showOnly is used, other regions can be faded (2)",
+    options: {showOnly: "padding", onlyRegionArea: true},
+    checkHighlighter: function*(toolbox) {
+      let h = toolbox.highlighter;
+
+      for (let region of ["margin", "border", "padding", "content"]) {
+        let {d} = yield getHighlighterRegionPath(region, h);
+        ok(d, "Region " + region + " is shown (it has a d attribute)");
+
+        let faded = yield getHighlighterNodeAttribute(h,
+                          "box-model-" + region, "faded");
+        if (region === "padding") {
+          ok(!faded, "The padding region is not faded");
+        } else {
+          is(faded, "true", "Region " + region + " is faded");
+        }
+      }
+    }
   }
 ];
 
 add_task(function*() {
   let {inspector, toolbox} = yield openInspectorForURL(TEST_URL);
 
   let divFront = yield getNodeFront("div", inspector);
 
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -548,17 +548,21 @@ LayoutView.prototype = {
 let elts;
 
 let onmouseover = function(e) {
   let region = e.target.getAttribute("data-box");
   if (!region) {
     return false;
   }
 
-  this.layoutview.showBoxModel({region});
+  this.layoutview.showBoxModel({
+    region,
+    showOnly: region,
+    onlyRegionArea: true
+  });
 
   return false;
 }.bind(window);
 
 let onmouseout = function() {
   this.layoutview.hideBoxModel();
   return false;
 }.bind(window);
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -1538,17 +1538,17 @@ Scope.prototype = {
 
   /**
    * Sort in ascending order
    * This only needs to compare non-numbers since it is dealing with an array
    * which numeric-based indices are placed in order.
    *
    * @param string a
    * @param string b
-   * @return number 
+   * @return number
    *         -1 if a is less than b, 0 if no change in order, +1 if a is greater than 0
    */
   _naturalSort: function(a,b) {
     if (isNaN(parseFloat(a)) && isNaN(parseFloat(b))) {
       return a < b ? -1 : 1;
     }
    },
 
--- a/browser/devtools/webconsole/console-output.js
+++ b/browser/devtools/webconsole/console-output.js
@@ -337,16 +337,20 @@ ConsoleOutput.prototype = {
    * Open an URL in a new tab.
    * @see WebConsole.openLink() in hudservice.js
    */
   openLink: function()
   {
     this.owner.owner.openLink.apply(this.owner.owner, arguments);
   },
 
+  openLocationInDebugger: function ({url, line}) {
+    return this.owner.owner.viewSourceInDebugger(url, line);
+  },
+
   /**
    * Open the variables view to inspect an object actor.
    * @see JSTerm.openVariablesView() in webconsole.js
    */
   openVariablesView: function()
   {
     this.owner.jsterm.openVariablesView.apply(this.owner.jsterm, arguments);
   },
@@ -2491,43 +2495,69 @@ Widgets.JSObject.prototype = Heritage.ex
   _anchor: function(text, options = {})
   {
     if (!options.onClick) {
       // If the anchor has an URL, open it in a new tab. If not, show the
       // current object actor.
       options.onClick = options.href ? this._onClickAnchor : this._onClick;
     }
 
+    options.onContextMenu = options.onContextMenu || this._onContextMenu;
+
     let anchor = this.el("a", {
       class: options.className,
       draggable: false,
       href: options.href || "#",
     }, text);
 
     this.message._addLinkCallback(anchor, options.onClick);
 
+    anchor.addEventListener("contextmenu", options.onContextMenu.bind(this));
+
     if (options.appendTo) {
       options.appendTo.appendChild(anchor);
     } else if (!("appendTo" in options) && this.element) {
       this.element.appendChild(anchor);
     }
 
     return anchor;
   },
 
+  openObjectInVariablesView: function()
+  {
+    this.output.openVariablesView({
+      label: VariablesView.getString(this.objectActor, { concise: true }),
+      objectActor: this.objectActor,
+      autofocus: true,
+    });
+  },
+
   /**
    * The click event handler for objects shown inline.
    * @private
    */
   _onClick: function()
   {
-    this.output.openVariablesView({
-      label: VariablesView.getString(this.objectActor, { concise: true }),
-      objectActor: this.objectActor,
-      autofocus: true,
+    this.openObjectInVariablesView();
+  },
+
+  _onContextMenu: function(ev) {
+    // TODO offer a nice API for the context menu.
+    // Probably worth to take a look at Firebug's way
+    // https://github.com/firebug/firebug/blob/master/extension/content/firebug/chrome/menu.js
+    let doc = ev.target.ownerDocument;
+    let cmPopup = doc.getElementById("output-contextmenu");
+    let openInVarViewCmd = doc.getElementById("menu_openInVarView");
+    let openVarView = this.openObjectInVariablesView.bind(this);
+    openInVarViewCmd.addEventListener("command", openVarView);
+    openInVarViewCmd.removeAttribute("disabled");
+    cmPopup.addEventListener("popuphiding", function onPopupHiding() {
+      cmPopup.removeEventListener("popuphiding", onPopupHiding);
+      openInVarViewCmd.removeEventListener("command", openVarView);
+      openInVarViewCmd.setAttribute("disabled", "true");
     });
   },
 
   /**
    * Add a string to the message.
    *
    * @private
    * @param string str
@@ -2685,16 +2715,26 @@ Widgets.ObjectRenderers.add({
         this._text(", ");
       }
       this.element.appendChild(this.el("span.cm-def", param));
       shown++;
     }
 
     this._text(")");
   },
+
+  _onClick: function () {
+    let location = this.objectActor.location;
+    if (location) {
+      this.output.openLocationInDebugger(location);
+    }
+    else {
+      this.openObjectInVariablesView();
+    }
+  }
 }); // Widgets.ObjectRenderers.byClass.Function
 
 /**
  * The widget used for displaying ArrayLike objects.
  */
 Widgets.ObjectRenderers.add({
   byKind: "ArrayLike",
 
--- a/browser/devtools/webconsole/test/browser.ini
+++ b/browser/devtools/webconsole/test/browser.ini
@@ -119,16 +119,18 @@ support-files =
   test-bug_923281_console_log_filter.html
   test-bug_923281_test1.js
   test-bug_923281_test2.js
   test-bug_939783_console_trace_duplicates.html
   test-bug-952277-highlight-nodes-in-vview.html
   test-bug-609872-cd-iframe-parent.html
   test-bug-609872-cd-iframe-child.html
   test-bug-989025-iframe-parent.html
+  test-bug_1050691_click_function_to_source.html
+  test-bug_1050691_click_function_to_source.js
   test-console-api-stackframe.html
   test_bug_1010953_cspro.html^headers^
   test_bug_1010953_cspro.html
   test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
   test_bug1045902_console_csp_ignore_reflected_xss_message.html
   test_bug1092055_shouldwarn.js^headers^
   test_bug1092055_shouldwarn.js
   test_bug1092055_shouldwarn.html
@@ -382,8 +384,10 @@ skip-if = e10s # Bug 1042253 - webconsol
 [browser_webconsole_autocomplete_crossdomain_iframe.js]
 [browser_webconsole_console_custom_styles.js]
 [browser_webconsole_console_api_stackframe.js]
 [browser_webconsole_column_numbers.js]
 [browser_console_open_or_focus.js]
 [browser_webconsole_bug_922212_console_dirxml.js]
 [browser_webconsole_shows_reqs_in_netmonitor.js]
 [browser_netmonitor_shows_reqs_in_webconsole.js]
+[browser_webconsole_bug_1050691_click_function_to_source.js]
+[browser_webconsole_context_menu_open_in_var_view.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_1050691_click_function_to_source.js
@@ -0,0 +1,60 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Tests that clicking on a function displays its source in the debugger.
+
+"use strict";
+
+const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-bug_1050691_click_function_to_source.html";
+
+let test = asyncTest(function*() {
+  yield loadTab(TEST_URI);
+  let hud = yield openConsole();
+
+  yield testWithoutDebuggerOpen(hud);
+
+  // Open the Debugger panel.
+  let debuggerPanel = yield openDebugger();
+  // And right after come back to the Console panel.
+  yield openConsole();
+  yield testWithDebuggerOpen(hud, debuggerPanel);
+});
+
+function* testWithoutDebuggerOpen(hud) {
+  let clickable = yield printFunction(hud);
+  let onVariablesViewOpen = hud.jsterm.once("variablesview-fetched");
+  synthesizeClick(clickable, hud);
+  return onVariablesViewOpen;
+}
+
+function* testWithDebuggerOpen(hud, debuggerPanel) {
+  let clickable = yield printFunction(hud);
+  let panelWin = debuggerPanel.panelWin;
+  let onEditorLocationSet = panelWin.once(panelWin.EVENTS.EDITOR_LOCATION_SET);
+  synthesizeClick(clickable, hud);
+  yield onEditorLocationSet;
+  ok(isDebuggerCaretPos(debuggerPanel, 7),
+    "Clicking on a function should go to its source in the debugger view");
+}
+
+function synthesizeClick(clickable, hud) {
+  EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
+}
+
+let printFunction = Task.async(function* (hud) {
+  hud.jsterm.clearOutput();
+  content.wrappedJSObject.foo();
+  let [result] = yield waitForMessages({
+    webconsole: hud,
+    messages: [{
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+    }],
+  });
+  let msg = [...result.matched][0];
+  let clickable = msg.querySelector("a");
+  ok(clickable, "clickable item for object should exist");
+  return clickable;
+});
--- a/browser/devtools/webconsole/test/browser_webconsole_closure_inspection.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_closure_inspection.js
@@ -34,17 +34,17 @@ function test()
         });
 
         let button = content.document.querySelector("button");
         ok(button, "button element found");
         EventUtils.synthesizeMouseAtCenter(button, {}, content);
 
         return deferred.promise;
       });
-    })
+    });
   });
 }
 
 function consoleOpened(hud)
 {
   gWebConsole = hud;
   gJSTerm = hud.jsterm;
   gJSTerm.execute("window.george.getName");
@@ -60,17 +60,25 @@ function consoleOpened(hud)
 }
 
 function onExecuteGetName(aResults)
 {
   let clickable = aResults[0].clickableElements[0];
   ok(clickable, "clickable object found");
 
   gJSTerm.once("variablesview-fetched", onGetNameFetch);
-  EventUtils.synthesizeMouse(clickable, 2, 2, {}, gWebConsole.iframeWindow);
+  let contextMenu =
+      gWebConsole.iframeWindow.document.getElementById("output-contextmenu");
+  waitForContextMenu(contextMenu, clickable, () => {
+    let openInVarView = contextMenu.querySelector("#menu_openInVarView");
+    ok(openInVarView.disabled === false,
+       "the \"Open In Variables View\" context menu item should be clickable");
+    // EventUtils.synthesizeMouseAtCenter seems to fail here in Mac OSX
+    openInVarView.click();
+  });
 }
 
 function onGetNameFetch(aEvent, aVar)
 {
   gVariablesView = aVar._variablesView;
   ok(gVariablesView, "variables view object");
 
   findVariableViewProperties(aVar, [
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/browser_webconsole_context_menu_open_in_var_view.js
@@ -0,0 +1,51 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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/. */
+
+// Tests that the "Open in Variables View" context menu item is enabled
+// only for objects.
+
+"use strict";
+
+const TEST_URI = `data:text/html,<script>
+  console.log("foo");
+  console.log("foo", window);
+</script>`;
+
+let test = asyncTest(function*() {
+  yield loadTab(TEST_URI);
+  let hud = yield openConsole();
+
+  let [result] = yield waitForMessages({
+    webconsole: hud,
+    messages: [{
+      category: CATEGORY_WEBDEV,
+      severity: SEVERITY_LOG,
+      count: 2,
+      text: /foo/
+    }],
+  });
+
+  let [msgWithText, msgWithObj] = [...result.matched];
+  ok(msgWithText && msgWithObj, "Two messages should have appeared");
+
+  let contextMenu = hud.iframeWindow.
+    document.getElementById("output-contextmenu");
+  let openInVarViewItem = contextMenu.querySelector("#menu_openInVarView");
+  let obj = msgWithObj.querySelector(".cm-variable");
+  let text = msgWithText.querySelector(".console-string");
+
+  yield waitForContextMenu(contextMenu, obj, () => {
+    ok(openInVarViewItem.disabled === false, "The \"Open In Variables View\" " +
+      "context menu item should be available for objects");
+  }, () => {
+    ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
+      "context menu item should be disabled on popup hiding");
+  });
+
+  yield waitForContextMenu(contextMenu, text, () => {
+    ok(openInVarViewItem.disabled === true, "The \"Open In Variables View\" " +
+      "context menu item should be disabled for texts");
+  });
+});
--- a/browser/devtools/webconsole/test/head.js
+++ b/browser/devtools/webconsole/test/head.js
@@ -221,16 +221,18 @@ let closeConsole = Task.async(function* 
  * @param function aOnHidden
  *        Function to invoke on popuphidden event.
  * @return object
  *         A Promise object that is resolved after the popuphidden event
  *         callback is invoked.
  */
 function waitForContextMenu(aPopup, aButton, aOnShown, aOnHidden)
 {
+  let deferred = promise.defer();
+
   function onPopupShown() {
     info("onPopupShown");
     aPopup.removeEventListener("popupshown", onPopupShown);
 
     aOnShown && aOnShown();
 
     // Use executeSoon() to get out of the popupshown event.
     aPopup.addEventListener("popuphidden", onPopupHidden);
@@ -240,21 +242,20 @@ function waitForContextMenu(aPopup, aBut
     info("onPopupHidden");
     aPopup.removeEventListener("popuphidden", onPopupHidden);
 
     aOnHidden && aOnHidden();
 
     deferred.resolve(aPopup);
   }
 
-  let deferred = promise.defer();
   aPopup.addEventListener("popupshown", onPopupShown);
 
   info("wait for the context menu to open");
-  let eventDetails = { type: "contextmenu", button: 2};
+  let eventDetails = {type: "contextmenu", button: 2};
   EventUtils.synthesizeMouse(aButton, 2, 2, eventDetails,
                              aButton.ownerDocument.defaultView);
   return deferred.promise;
 }
 
 /**
  * Listen for a new tab to open and return a promise that resolves when one
  * does and completes the load event.
@@ -820,17 +821,17 @@ function openDebugger(aOptions = {})
   if (!aOptions.tab) {
     aOptions.tab = gBrowser.selectedTab;
   }
 
   let deferred = promise.defer();
 
   let target = TargetFactory.forTab(aOptions.tab);
   let toolbox = gDevTools.getToolbox(target);
-  let dbgPanelAlreadyOpen = toolbox.getPanel("jsdebugger");
+  let dbgPanelAlreadyOpen = toolbox && toolbox.getPanel("jsdebugger");
 
   gDevTools.showToolbox(target, "jsdebugger").then(function onSuccess(aToolbox) {
     let panel = aToolbox.getCurrentPanel();
     let panelWin = panel.panelWin;
 
     panel._view.Variables.lazyEmpty = false;
 
     let resolveObject = {
@@ -852,16 +853,34 @@ function openDebugger(aOptions = {})
     console.debug("failed to open the toolbox for 'jsdebugger'", aReason);
     deferred.reject(aReason);
   });
 
   return deferred.promise;
 }
 
 /**
+ * Returns true if the caret in the debugger editor is placed at the specified
+ * position.
+ * @param  aPanel The debugger panel.
+ * @param {number} aLine The line number.
+ * @param {number} [aCol] The column number.
+ * @returns {boolean}
+ */
+function isDebuggerCaretPos(aPanel, aLine, aCol = 1) {
+  let editor = aPanel.panelWin.DebuggerView.editor;
+  let cursor = editor.getCursor();
+
+  // Source editor starts counting line and column numbers from 0.
+  info("Current editor caret position: " + (cursor.line + 1) + ", " +
+    (cursor.ch + 1));
+  return cursor.line == (aLine - 1) && cursor.ch == (aCol - 1);
+}
+
+/**
  * Wait for messages in the Web Console output.
  *
  * @param object aOptions
  *        Options for what you want to wait for:
  *        - webconsole: the webconsole instance you work with.
  *        - matchCondition: "any" or "all". Default: "all". The promise
  *        returned by this function resolves when all of the messages are
  *        matched, if the |matchCondition| is "all". If you set the condition to
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-bug_1050691_click_function_to_source.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML>
+<html dir="ltr" xml:lang="en-US" lang="en-US">
+  <head>
+    <meta charset="utf-8">
+    <title>Click on function should point to source</title>
+    <!-- Any copyright is dedicated to the Public Domain.
+       - http://creativecommons.org/publicdomain/zero/1.0/ -->
+    <script type="text/javascript" src="test-bug_1050691_click_function_to_source.js"></script>
+  </head>
+  <body></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webconsole/test/test-bug_1050691_click_function_to_source.js
@@ -0,0 +1,10 @@
+/**
+ * this
+ * is
+ * a
+ * function
+ */
+function foo() {
+  console.log(foo);
+}
+
--- a/browser/devtools/webconsole/webconsole.xul
+++ b/browser/devtools/webconsole/webconsole.xul
@@ -71,16 +71,18 @@ function goUpdateConsoleCommands() {
       <menuitem id="saveBodiesContextMenu" type="checkbox" label="&saveBodies.label;"
                 accesskey="&saveBodies.accesskey;"/>
       <menuitem id="menu_openURL" label="&openURL.label;"
                 accesskey="&openURL.accesskey;" command="consoleCmd_openURL"
                 selection="network" selectionType="single"/>
       <menuitem id="menu_copyURL" label="&copyURLCmd.label;"
                 accesskey="&copyURLCmd.accesskey;" command="consoleCmd_copyURL"
                 selection="network" selectionType="single"/>
+      <menuitem id="menu_openInVarView" label="&openInVarViewCmd.label;"
+        accesskey="&openInVarViewCmd.accesskey;" disabled="true"/>
       <menuitem id="cMenu_copy"/>
       <menuitem id="cMenu_selectAll"/>
     </menupopup>
   </popupset>
 
   <tooltip id="aHTMLTooltip" page="true"/>
 
   <box class="hud-outer-wrapper devtools-responsive-container theme-body" flex="1">
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -301,17 +301,16 @@
 @RESPATH@/components/parentalcontrols.xpt
 #ifdef MOZ_WEBRTC
 @RESPATH@/components/peerconnection.xpt
 #endif
 @RESPATH@/components/places.xpt
 @RESPATH@/components/plugin.xpt
 @RESPATH@/components/pref.xpt
 @RESPATH@/components/prefetch.xpt
-@RESPATH@/components/profile.xpt
 #ifdef MOZ_ENABLE_PROFILER_SPS
 @RESPATH@/components/profiler.xpt
 #endif
 @RESPATH@/components/rdf.xpt
 @RESPATH@/components/satchel.xpt
 @RESPATH@/components/saxparser.xpt
 @RESPATH@/browser/components/sessionstore.xpt
 @RESPATH@/components/services-crypto-component.xpt
--- a/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/webConsole.dtd
@@ -102,8 +102,10 @@
 <!ENTITY fullZoomResetCmd.commandkey2   "">
 
 <!ENTITY copyURLCmd.label     "Copy Link Location">
 <!ENTITY copyURLCmd.accesskey "a">
 
 <!ENTITY closeCmd.key         "W">
 <!ENTITY findCmd.key          "F">
 <!ENTITY clearOutputCtrl.key  "L">
+<!ENTITY openInVarViewCmd.label "Open in Variables View">
+<!ENTITY openInVarViewCmd.accesskey "V">
--- a/dom/base/Attr.h
+++ b/dom/base/Attr.h
@@ -93,16 +93,18 @@ public:
   bool Specified() const;
 
   // XPCOM GetNamespaceURI() is OK
   // XPCOM GetPrefix() is OK
   // XPCOM GetLocalName() is OK
 
   Element* GetOwnerElement(ErrorResult& aRv);
 
+  bool IsNSAware() const { return mNsAware; }
+
 protected:
   virtual Element* GetNameSpaceElement() override
   {
     return GetElement();
   }
 
   static bool sInitialized;
 
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -10,16 +10,17 @@
 
 #include "nsDOMAttributeMap.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/NamedNodeMapBinding.h"
 #include "mozilla/dom/NodeInfoInlines.h"
+#include "mozilla/Telemetry.h"
 #include "nsAttrName.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsNameSpaceManager.h"
 #include "nsNodeInfoManager.h"
 #include "nsUnicharUtils.h"
@@ -266,16 +267,31 @@ nsDOMAttributeMap::SetNamedItemNS(nsIDOM
 
 already_AddRefed<Attr>
 nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
                                         bool aWithNS,
                                         ErrorResult& aError)
 {
   NS_ENSURE_TRUE(mContent, nullptr);
 
+  if (!aAttr.IsNSAware() &&
+      !mContent->IsHTMLElement() &&
+      aAttr.OwnerDoc()->IsHTMLDocument()) {
+    // Check whether we have a non-lowercase name, and if so log some telemetry.
+    // We check whether the attr's document is HTML _before_ the adopt we do
+    // below, because we're trying to figure out whether we could lowercase the
+    // attr name at creation time.  We restrict this to the !IsNSAware() case,
+    // because we only care about Attr nodes created via createAttribute.
+    nsIAtom* nameAtom = aAttr.NodeInfo()->NameAtom();
+    if (nsContentUtils::StringContainsASCIIUpper(nsDependentAtomString(nameAtom))) {
+        Telemetry::Accumulate(Telemetry::NONLOWERCASE_NONHTML_ATTR_NODE_SET,
+                              true);
+      }
+  }
+
   // XXX should check same-origin between mContent and aAttr however
   // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
 
   // Check that attribute is not owned by somebody else
   nsDOMAttributeMap* owner = aAttr.GetMap();
   if (owner) {
     if (owner != this) {
       aError.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -7586,28 +7586,16 @@ nsGlobalWindow::ClearInterval(int32_t aH
 {
   ErrorResult rv;
   ClearInterval(aHandle, rv);
 
   return rv.StealNSResult();
 }
 
 NS_IMETHODIMP
-nsGlobalWindow::SetTimeout(int32_t *_retval)
-{
-  return SetTimeoutOrInterval(false, _retval);
-}
-
-NS_IMETHODIMP
-nsGlobalWindow::SetInterval(int32_t *_retval)
-{
-  return SetTimeoutOrInterval(true, _retval);
-}
-
-NS_IMETHODIMP
 nsGlobalWindow::SetResizable(bool aResizable)
 {
   // nop
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -11940,60 +11928,16 @@ nsGlobalWindow::SetTimeoutOrInterval(nsI
 
   timeout->mPublicId = ++mTimeoutPublicIdCounter;
   *aReturn = timeout->mPublicId;
 
   return NS_OK;
 
 }
 
-nsresult
-nsGlobalWindow::SetTimeoutOrInterval(bool aIsInterval, int32_t *aReturn)
-{
-  // This needs to forward to the inner window, but since the current
-  // inner may not be the inner in the calling scope, we need to treat
-  // this specially here as we don't want timeouts registered in a
-  // dying inner window to get registered and run on the current inner
-  // window. To get this right, we need to forward this call to the
-  // inner window that's calling window.setTimeout().
-
-  if (IsOuterWindow()) {
-    nsGlobalWindow* callerInner = CallerInnerWindow();
-    NS_ENSURE_TRUE(callerInner || nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
-
-    // If the caller and the callee share the same outer window,
-    // forward to the callee inner. Else, we forward to the current
-    // inner (e.g. someone is calling setTimeout() on a reference to
-    // some other window).
-
-    if (callerInner &&
-        callerInner->GetOuterWindow() == this &&
-        callerInner->IsInnerWindow()) {
-      return callerInner->SetTimeoutOrInterval(aIsInterval, aReturn);
-    }
-
-    FORWARD_TO_INNER(SetTimeoutOrInterval, (aIsInterval, aReturn),
-                     NS_ERROR_NOT_INITIALIZED);
-  }
-
-  int32_t interval = 0;
-  bool isInterval = aIsInterval;
-  nsCOMPtr<nsIScriptTimeoutHandler> handler;
-  nsresult rv = NS_CreateJSTimeoutHandler(this,
-                                          &isInterval,
-                                          &interval,
-                                          getter_AddRefs(handler));
-  if (!handler) {
-    *aReturn = 0;
-    return rv;
-  }
-
-  return SetTimeoutOrInterval(handler, interval, isInterval, aReturn);
-}
-
 int32_t
 nsGlobalWindow::SetTimeoutOrInterval(Function& aFunction, int32_t aTimeout,
                                      const Sequence<JS::Value>& aArguments,
                                      bool aIsInterval, ErrorResult& aError)
 {
   nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
   if (!inner) {
     return -1;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -124,22 +124,16 @@ namespace indexedDB {
 class IDBFactory;
 } // namespace indexedDB
 } // namespace dom
 namespace gfx {
 class VRHMDInfo;
 } // namespace gfx
 } // namespace mozilla
 
-extern nsresult
-NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
-                          bool *aIsInterval,
-                          int32_t *aInterval,
-                          nsIScriptTimeoutHandler **aRet);
-
 extern already_AddRefed<nsIScriptTimeoutHandler>
 NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                           mozilla::dom::Function& aFunction,
                           const mozilla::dom::Sequence<JS::Value>& aArguments,
                           mozilla::ErrorResult& aError);
 
 extern already_AddRefed<nsIScriptTimeoutHandler>
 NS_CreateJSTimeoutHandler(JSContext* aCx, nsGlobalWindow *aWindow,
@@ -1330,17 +1324,16 @@ public:
   nsresult ClearTimeoutOrInterval(int32_t aTimerID) override
   {
     mozilla::ErrorResult rv;
     ClearTimeoutOrInterval(aTimerID, rv);
     return rv.StealNSResult();
   }
 
   // JS specific timeout functions (JS args grabbed from context).
-  nsresult SetTimeoutOrInterval(bool aIsInterval, int32_t* aReturn);
   nsresult ResetTimersForNonBackgroundWindow();
 
   // The timeout implementation functions.
   void RunTimeout(nsTimeout *aTimeout);
   void RunTimeout() { RunTimeout(nullptr); }
   // Return true if |aTimeout| was cleared while its handler ran.
   bool RunTimeoutHandler(nsTimeout* aTimeout, nsIScriptContext* aScx);
   // Return true if |aTimeout| needs to be reinserted into the timeout list.
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -53,19 +53,16 @@ public:
     *aLineNo = mLineNo;
   }
 
   virtual const nsTArray<JS::Value>& GetArgs() override
   {
     return mArgs;
   }
 
-  nsresult Init(nsGlobalWindow *aWindow, bool *aIsInterval,
-                int32_t *aInterval, bool* aAllowEval);
-
   void ReleaseJSObjects();
 
 private:
   ~nsJSScriptTimeoutHandler();
 
   // filename, line number and JS language version string of the
   // caller of setTimeout()
   nsCString mFileName;
@@ -243,171 +240,23 @@ nsJSScriptTimeoutHandler::ReleaseJSObjec
 {
   if (mFunction) {
     mFunction = nullptr;
     mArgs.Clear();
     mozilla::DropJSObjects(this);
   }
 }
 
-nsresult
-nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
-                               int32_t *aInterval, bool *aAllowEval)
-{
-  if (!aWindow->GetContextInternal() || !aWindow->FastGetGlobalJSObject()) {
-    // This window was already closed, or never properly initialized,
-    // don't let a timer be scheduled on such a window.
-
-    return NS_ERROR_NOT_INITIALIZED;
-  }
-
-  nsAXPCNativeCallContext *ncc = nullptr;
-  nsresult rv = nsContentUtils::XPConnect()->
-    GetCurrentNativeCallContext(&ncc);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!ncc)
-    return NS_ERROR_NOT_AVAILABLE;
-
-  JSContext *cx = nullptr;
-
-  rv = ncc->GetJSContext(&cx);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t argc;
-  JS::Value *argv = nullptr;
-
-  ncc->GetArgc(&argc);
-  ncc->GetArgvPtr(&argv);
-
-  JS::Rooted<JSFlatString*> expr(cx);
-  JS::Rooted<JSObject*> funobj(cx);
-
-  if (argc < 1) {
-    ::JS_ReportError(cx, "Function %s requires at least 2 parameter",
-                     *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-    return NS_ERROR_DOM_TYPE_ERR;
-  }
-
-  int32_t interval = 0;
-  if (argc > 1) {
-    JS::Rooted<JS::Value> arg(cx, argv[1]);
-
-    if (!JS::ToInt32(cx, arg, &interval)) {
-      ::JS_ReportError(cx,
-                       "Second argument to %s must be a millisecond interval",
-                       aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-      return NS_ERROR_DOM_TYPE_ERR;
-    }
-  }
-
-  if (argc == 1) {
-    // If no interval was specified, treat this like a timeout, to avoid
-    // setting an interval of 0 milliseconds.
-    *aIsInterval = false;
-  }
-
-  JS::Rooted<JS::Value> arg(cx, argv[0]);
-  switch (::JS_TypeOfValue(cx, arg)) {
-  case JSTYPE_FUNCTION:
-    funobj = &arg.toObject();
-    break;
-
-  case JSTYPE_STRING:
-  case JSTYPE_OBJECT:
-    {
-      JSString *str = JS::ToString(cx, arg);
-      if (!str)
-        return NS_ERROR_OUT_OF_MEMORY;
-
-      expr = ::JS_FlattenString(cx, str);
-      if (!expr)
-          return NS_ERROR_OUT_OF_MEMORY;
-
-      argv[0] = JS::StringValue(str);
-    }
-    break;
-
-  default:
-    ::JS_ReportError(cx, "useless %s call (missing quotes around argument?)",
-                     *aIsInterval ? kSetIntervalStr : kSetTimeoutStr);
-
-    // Return an error that nsGlobalWindow can recognize and turn into NS_OK.
-    return NS_ERROR_DOM_TYPE_ERR;
-  }
-
-  if (expr) {
-    // if CSP is enabled, and setTimeout/setInterval was called with a string,
-    // disable the registration and log an error
-    ErrorResult error;
-    *aAllowEval = CheckCSPForEval(cx, aWindow, error);
-    if (error.Failed() || !*aAllowEval) {
-      return error.StealNSResult();
-    }
-
-    MOZ_ASSERT(mExpr.IsEmpty());
-    AssignJSFlatString(mExpr, expr);
-
-    // Get the calling location.
-    nsJSUtils::GetCallingLocation(cx, mFileName, &mLineNo);
-  } else if (funobj) {
-    *aAllowEval = true;
-
-    mozilla::HoldJSObjects(this);
-
-    mFunction = new Function(funobj, GetIncumbentGlobal());
-
-    // Create our arg array.  argc is the number of arguments passed
-    // to setTimeout or setInterval; the first two are our callback
-    // and the delay, so only arguments after that need to go in our
-    // array.
-    // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
-    uint32_t argCount = std::max(argc, 2u) - 2;
-
-    FallibleTArray<JS::Heap<JS::Value> > args;
-    if (!args.SetCapacity(argCount, fallible)) {
-      // No need to drop here, since we already have a non-null mFunction
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    for (uint32_t idx = 0; idx < argCount; ++idx) {
-      *args.AppendElement(fallible) = argv[idx + 2];
-    }
-    args.SwapElements(mArgs);
-  } else {
-    NS_WARNING("No func and no expr - why are we here?");
-  }
-  *aInterval = interval;
-  return NS_OK;
-}
-
 const char16_t *
 nsJSScriptTimeoutHandler::GetHandlerText()
 {
   NS_ASSERTION(!mFunction, "No expression, so no handler text!");
   return mExpr.get();
 }
 
-nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
-                                   bool *aIsInterval,
-                                   int32_t *aInterval,
-                                   nsIScriptTimeoutHandler **aRet)
-{
-  *aRet = nullptr;
-  nsRefPtr<nsJSScriptTimeoutHandler> handler = new nsJSScriptTimeoutHandler();
-  bool allowEval;
-  nsresult rv = handler->Init(aWindow, aIsInterval, aInterval, &allowEval);
-  if (NS_FAILED(rv) || !allowEval) {
-    return rv;
-  }
-
-  handler.forget(aRet);
-
-  return NS_OK;
-}
-
 already_AddRefed<nsIScriptTimeoutHandler>
 NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow, Function& aFunction,
                           const Sequence<JS::Value>& aArguments,
                           ErrorResult& aError)
 {
   FallibleTArray<JS::Heap<JS::Value> > args;
   if (!args.AppendElements(aArguments, fallible)) {
     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -673,38 +673,45 @@ nsXMLHttpRequest::AppendToResponseText(c
 {
   NS_ENSURE_STATE(mDecoder);
 
   int32_t destBufferLen;
   nsresult rv = mDecoder->GetMaxLength(aSrcBuffer, aSrcBufferLen,
                                        &destBufferLen);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!mResponseText.SetCapacity(mResponseText.Length() + destBufferLen, fallible)) {
+  uint32_t size = mResponseText.Length() + destBufferLen;
+  if (size < (uint32_t)destBufferLen) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  if (!mResponseText.SetCapacity(size, fallible)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   char16_t* destBuffer = mResponseText.BeginWriting() + mResponseText.Length();
 
-  int32_t totalChars = mResponseText.Length();
+  CheckedInt32 totalChars = mResponseText.Length();
 
   // This code here is basically a copy of a similar thing in
   // nsScanner::Append(const char* aBuffer, uint32_t aLen).
   int32_t srclen = (int32_t)aSrcBufferLen;
   int32_t destlen = (int32_t)destBufferLen;
   rv = mDecoder->Convert(aSrcBuffer,
                          &srclen,
                          destBuffer,
                          &destlen);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   totalChars += destlen;
-
-  mResponseText.SetLength(totalChars);
-
+  if (!totalChars.isValid()) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  mResponseText.SetLength(totalChars.value());
   return NS_OK;
 }
 
 /* readonly attribute AString responseText; */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetResponseText(nsAString& aResponseText)
 {
   ErrorResult rv;
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -751,16 +751,21 @@ IDBFactory::OpenInternal(nsIPrincipal* a
     MOZ_ASSERT(scriptOwner);
 
     request = IDBOpenDBRequest::CreateForWindow(this, mWindow, scriptOwner);
   } else {
     autoJS.Init(mOwningObject.get());
     JS::Rooted<JSObject*> scriptOwner(autoJS.cx(), mOwningObject);
 
     request = IDBOpenDBRequest::CreateForJS(this, scriptOwner);
+    if (!request) {
+      MOZ_ASSERT(!NS_IsMainThread());
+      aRv.ThrowUncatchableException();
+      return nullptr;
+    }
   }
 
   MOZ_ASSERT(request);
 
   if (aDeleting) {
     IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: "
                    "indexedDB.deleteDatabase(\"%s\")",
                  "IndexedDB %s: C R[%llu]: IDBFactory.deleteDatabase()",
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -437,35 +437,56 @@ IDBRequest::PreHandleEvent(EventChainPre
   aVisitor.mParentTarget = mTransaction;
   return NS_OK;
 }
 
 class IDBOpenDBRequest::WorkerFeature final
   : public mozilla::dom::workers::WorkerFeature
 {
   WorkerPrivate* mWorkerPrivate;
+#ifdef DEBUG
+  // This is only here so that assertions work in the destructor even if
+  // NoteAddFeatureFailed was called.
+  WorkerPrivate* mWorkerPrivateDEBUG;
+#endif
 
 public:
   explicit
   WorkerFeature(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate)
+#ifdef DEBUG
+    , mWorkerPrivateDEBUG(aWorkerPrivate)
+#endif
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     MOZ_COUNT_CTOR(IDBOpenDBRequest::WorkerFeature);
   }
 
   ~WorkerFeature()
   {
-    mWorkerPrivate->AssertIsOnWorkerThread();
+#ifdef DEBUG
+    mWorkerPrivateDEBUG->AssertIsOnWorkerThread();
+#endif
 
     MOZ_COUNT_DTOR(IDBOpenDBRequest::WorkerFeature);
 
-    mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+    if (mWorkerPrivate) {
+      mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
+    }
+  }
+
+  void
+  NoteAddFeatureFailed()
+  {
+    MOZ_ASSERT(mWorkerPrivate);
+    mWorkerPrivate->AssertIsOnWorkerThread();
+
+    mWorkerPrivate = nullptr;
   }
 
 private:
   virtual bool
   Notify(JSContext* aCx, Status aStatus) override;
 };
 
 IDBOpenDBRequest::IDBOpenDBRequest(IDBFactory* aFactory, nsPIDOMWindow* aOwner)
@@ -522,16 +543,17 @@ IDBOpenDBRequest::CreateForJS(IDBFactory
 
     workerPrivate->AssertIsOnWorkerThread();
 
     JSContext* cx = workerPrivate->GetJSContext();
     MOZ_ASSERT(cx);
 
     nsAutoPtr<WorkerFeature> feature(new WorkerFeature(workerPrivate));
     if (NS_WARN_IF(!workerPrivate->AddFeature(cx, feature))) {
+      feature->NoteAddFeatureFailed();
       return nullptr;
     }
 
     request->mWorkerFeature = Move(feature);
   }
 
   return request.forget();
 }
--- a/dom/interfaces/base/nsIDOMJSWindow.idl
+++ b/dom/interfaces/base/nsIDOMJSWindow.idl
@@ -1,30 +1,21 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(4237c376-d637-4b6e-9f8a-1da57e867834)]
+[scriptable, uuid(e0f739e3-47e2-4007-af30-181939e97a51)]
 interface nsIDOMJSWindow : nsISupports
 {
   void                      dump(in DOMString str);
 
   /**
-   * These methods take typeless arguments and optional arguments, the
-   * first argument is either a function or a string, the second
-   * argument must be a number (ms) and the rest of the arguments (2
-   * ... n) are passed to the callback function
-   */
-  long                      setTimeout();
-  long                      setInterval();
-
-  /**
    * These methods take one optional argument that's the timer ID to
    * clear. Often in existing code these methods are passed undefined,
    * which is a nop so we need to support that as well.
    */
   void                      clearTimeout([optional] in long handle);
   void                      clearInterval([optional] in long handle);
 
   /**
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -99,23 +99,22 @@ void InitPreferredSampleRate()
     // Query failed, use a sensible default.
     sPreferredSampleRate = 44100;
   }
 }
 
 cubeb* GetCubebContextUnlocked()
 {
   sMutex.AssertCurrentThreadOwns();
-  if (!sCubebContext) {
-    MOZ_ASSERT(NS_IsMainThread());
-    if (cubeb_init(&sCubebContext, "CubebUtils") != CUBEB_OK) {
-      NS_WARNING("cubeb_init failed");
-    }
+  if (sCubebContext ||
+      cubeb_init(&sCubebContext, "CubebUtils") == CUBEB_OK) {
+    return sCubebContext;
   }
-  return sCubebContext;
+  NS_WARNING("cubeb_init failed");
+  return nullptr;
 }
 
 uint32_t GetCubebLatency()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebLatency;
 }
 
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaDecoder.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/MathAlgorithms.h"
 #include <limits>
 #include "nsIObserver.h"
 #include "nsTArray.h"
-#include "CubebUtils.h"
 #include "VideoUtils.h"
 #include "MediaDecoderStateMachine.h"
 #include "ImageContainer.h"
 #include "MediaResource.h"
 #include "nsError.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
@@ -397,21 +396,16 @@ MediaDecoder::MediaDecoder() :
 }
 
 bool MediaDecoder::Init(MediaDecoderOwner* aOwner)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mOwner = aOwner;
   mVideoFrameContainer = aOwner->GetVideoFrameContainer();
   MediaShutdownManager::Instance().Register(this);
-  // We don't use the cubeb context yet, but need to ensure it is created on
-  // the main thread.
-  if (!CubebUtils::GetCubebContext()) {
-    NS_WARNING("Audio backend initialization failed.");
-  }
   return true;
 }
 
 void MediaDecoder::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mShuttingDown)
--- a/dom/media/MediaInfo.h
+++ b/dom/media/MediaInfo.h
@@ -281,16 +281,21 @@ public:
 
   nsRefPtr<MediaByteBuffer> mCodecSpecificConfig;
   nsRefPtr<MediaByteBuffer> mExtraData;
 
 };
 
 class EncryptionInfo {
 public:
+  EncryptionInfo()
+    : mEncrypted(false)
+  {
+  }
+
   struct InitData {
     template<typename AInitDatas>
     InitData(const nsAString& aType, AInitDatas&& aInitData)
       : mType(aType)
       , mInitData(Forward<AInitDatas>(aInitData))
     {
     }
 
@@ -300,32 +305,36 @@ public:
     // Encryption data.
     nsTArray<uint8_t> mInitData;
   };
   typedef nsTArray<InitData> InitDatas;
 
   // True if the stream has encryption metadata
   bool IsEncrypted() const
   {
-    return !mInitDatas.IsEmpty();
+    return mEncrypted;
   }
 
   template<typename AInitDatas>
   void AddInitData(const nsAString& aType, AInitDatas&& aInitData)
   {
     mInitDatas.AppendElement(InitData(aType, Forward<AInitDatas>(aInitData)));
+    mEncrypted = true;
   }
 
   void AddInitData(const EncryptionInfo& aInfo)
   {
     mInitDatas.AppendElements(aInfo.mInitDatas);
+    mEncrypted = !!mInitDatas.Length();
   }
 
   // One 'InitData' per encrypted buffer.
   InitDatas mInitDatas;
+private:
+  bool mEncrypted;
 };
 
 class MediaInfo {
 public:
   bool HasVideo() const
   {
     return mVideo.IsValid();
   }
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -26,16 +26,17 @@
 #endif
 #include "GMPContentParent.h"
 
 #include "mozilla/dom/CrashReporterParent.h"
 using mozilla::dom::CrashReporterParent;
 using mozilla::ipc::GeckoChildProcessHost;
 
 #ifdef MOZ_CRASHREPORTER
+#include "nsPrintfCString.h"
 using CrashReporter::AnnotationTable;
 using CrashReporter::GetIDFromMinidump;
 #endif
 
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 
@@ -191,16 +192,21 @@ GMPParent::LoadProcess()
   return NS_OK;
 }
 
 void
 AbortWaitingForGMPAsyncShutdown(nsITimer* aTimer, void* aClosure)
 {
   NS_WARNING("Timed out waiting for GMP async shutdown!");
   GMPParent* parent = reinterpret_cast<GMPParent*>(aClosure);
+#if defined(MOZ_CRASHREPORTER)
+  CrashReporter::AnnotateCrashReport(
+    nsPrintfCString("AsyncPluginShutdown-%s@%p", parent->GetDisplayName().get(), parent),
+    NS_LITERAL_CSTRING("Timed out waiting for async shutdown"));
+#endif
   nsRefPtr<GeckoMediaPluginServiceParent> service =
     GeckoMediaPluginServiceParent::GetSingleton();
   if (service) {
     service->AsyncShutdownComplete(parent);
   }
 }
 
 nsresult
@@ -234,18 +240,30 @@ GMPParent::EnsureAsyncShutdownTimeoutSet
     nsITimer::TYPE_ONE_SHOT);
 }
 
 bool
 GMPParent::RecvPGMPContentChildDestroyed()
 {
   --mGMPContentChildCount;
   if (!IsUsed()) {
+#if defined(MOZ_CRASHREPORTER)
+    CrashReporter::AnnotateCrashReport(
+      nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+      NS_LITERAL_CSTRING("Content children destroyed"));
+#endif
     CloseIfUnused();
   }
+#if defined(MOZ_CRASHREPORTER)
+  else {
+    CrashReporter::AnnotateCrashReport(
+      nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+      nsPrintfCString("Content child destroyed, remaining: %u", mGMPContentChildCount));
+  }
+#endif
   return true;
 }
 
 void
 GMPParent::CloseIfUnused()
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   LOGD("%s: mAsyncShutdownRequired=%d", __FUNCTION__, mAsyncShutdownRequired);
@@ -257,19 +275,29 @@ GMPParent::CloseIfUnused()
     // Ensure all timers are killed.
     for (uint32_t i = mTimers.Length(); i > 0; i--) {
       mTimers[i - 1]->Shutdown();
     }
 
     if (mAsyncShutdownRequired) {
       if (!mAsyncShutdownInProgress) {
         LOGD("%s: sending async shutdown notification", __FUNCTION__);
+#if defined(MOZ_CRASHREPORTER)
+      CrashReporter::AnnotateCrashReport(
+        nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+        NS_LITERAL_CSTRING("Sent BeginAsyncShutdown"));
+#endif
         mAsyncShutdownInProgress = true;
         if (!SendBeginAsyncShutdown() ||
             NS_FAILED(EnsureAsyncShutdownTimeoutSet())) {
+#if defined(MOZ_CRASHREPORTER)
+          CrashReporter::AnnotateCrashReport(
+            nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+            NS_LITERAL_CSTRING("Could not send BeginAsyncShutdown - Aborting"));
+#endif
           AbortAsyncShutdown();
         }
       }
     } else {
       // Any async shutdown must be complete. Shutdown GMPStorage.
       for (size_t i = mStorage.Length(); i > 0; i--) {
         mStorage[i - 1]->Shutdown();
       }
@@ -308,16 +336,21 @@ GMPParent::CloseActive(bool aDieWhenUnlo
 
   if (aDieWhenUnloaded) {
     mDeleteProcessOnlyOnUnload = true; // don't allow this to go back...
   }
   if (mState == GMPStateLoaded) {
     mState = GMPStateUnloading;
   }
   if (mState != GMPStateNotLoaded && IsUsed()) {
+#if defined(MOZ_CRASHREPORTER)
+    CrashReporter::AnnotateCrashReport(
+      nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+      nsPrintfCString("Sent CloseActive, content children to close: %u", mGMPContentChildCount));
+#endif
     unused << SendCloseActive();
   }
 }
 
 void
 GMPParent::MarkForDeletion()
 {
   mDeleteProcessOnlyOnUnload = true;
@@ -563,16 +596,21 @@ GMPParent::ActorDestroy(ActorDestroyReas
   mState = GMPStateClosing;
   mAbnormalShutdownInProgress = true;
   CloseActive(false);
 
   // Normal Shutdown() will delete the process on unwind.
   if (AbnormalShutdown == aWhy) {
     nsRefPtr<GMPParent> self(this);
     if (mAsyncShutdownRequired) {
+#if defined(MOZ_CRASHREPORTER)
+      CrashReporter::AnnotateCrashReport(
+        nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+        NS_LITERAL_CSTRING("Actor destroyed"));
+#endif
       mService->AsyncShutdownComplete(this);
       mAsyncShutdownRequired = false;
     }
     // Must not call Close() again in DeleteProcess(), as we'll recurse
     // infinitely if we do.
     MOZ_ASSERT(mState == GMPStateClosing);
     DeleteProcess();
     // Note: final destruction will be Dispatched to ourself
@@ -865,16 +903,21 @@ GMPParent::RecvAsyncShutdownRequired()
 }
 
 bool
 GMPParent::RecvAsyncShutdownComplete()
 {
   LOGD("%s", __FUNCTION__);
 
   MOZ_ASSERT(mAsyncShutdownRequired);
+#if defined(MOZ_CRASHREPORTER)
+  CrashReporter::AnnotateCrashReport(
+    nsPrintfCString("AsyncPluginShutdown-%s@%p", GetDisplayName().get(), this),
+    NS_LITERAL_CSTRING("Received AsyncShutdownComplete"));
+#endif
   AbortAsyncShutdown();
   return true;
 }
 
 class RunCreateContentParentCallbacks : public nsRunnable
 {
 public:
   explicit RunCreateContentParentCallbacks(GMPContentParent* aGMPContentParent)
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -311,17 +311,17 @@ SourceBuffer::SourceBuffer(MediaSource* 
   , mActive(false)
   , mUpdateID(0)
   , mReportedOffset(0)
   , mType(aType)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aMediaSource);
   mEvictionThreshold = Preferences::GetUint("media.mediasource.eviction_threshold",
-                                            75 * (1 << 20));
+                                            100 * (1 << 20));
   mContentManager =
     SourceBufferContentManager::CreateManager(this,
                                               aMediaSource->GetDecoder(),
                                               aType);
   MSE_DEBUG("Create mContentManager=%p",
             mContentManager.get());
   if (aType.LowerCaseEqualsLiteral("audio/mpeg") ||
       aType.LowerCaseEqualsLiteral("audio/aac")) {
@@ -576,30 +576,29 @@ SourceBuffer::PrepareAppend(const uint8_
     // We notify that we've evicted from the time range 0 through to
     // the current start point.
     mMediaSource->NotifyEvicted(0.0, newBufferStartTime.ToSeconds());
   }
 
   // See if we have enough free space to append our new data.
   // As we can only evict once we have playable data, we must give a chance
   // to the DASH player to provide a complete media segment.
-  if (aLength > mEvictionThreshold ||
+  if (aLength > mEvictionThreshold || evicted == Result::BUFFER_FULL ||
       ((!mIsUsingFormatReader &&
         mContentManager->GetSize() > mEvictionThreshold - aLength) &&
        evicted != Result::CANT_EVICT)) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
   }
 
   nsRefPtr<MediaByteBuffer> data = new MediaByteBuffer();
   if (!data->AppendElements(aData, aLength, fallible)) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
     return nullptr;
   }
-  // TODO: Test buffer full flag.
   return data.forget();
 }
 
 double
 SourceBuffer::GetBufferedStart()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ErrorResult dummy;
--- a/dom/media/mediasource/SourceBufferContentManager.h
+++ b/dom/media/mediasource/SourceBufferContentManager.h
@@ -52,16 +52,17 @@ public:
   // http://w3c.github.io/media-source/#sourcebuffer-coded-frame-removal
   virtual nsRefPtr<RangeRemovalPromise> RangeRemoval(TimeUnit aStart, TimeUnit aEnd) = 0;
 
   enum class EvictDataResult : int8_t
   {
     NO_DATA_EVICTED,
     DATA_EVICTED,
     CANT_EVICT,
+    BUFFER_FULL,
   };
 
   // Evicts data up to aPlaybackTime. aThreshold is used to
   // bound the data being evicted. It will not evict more than aThreshold
   // bytes. aBufferStartTime contains the new start time of the data after the
   // eviction.
   virtual EvictDataResult
   EvictData(TimeUnit aPlaybackTime, uint32_t aThreshold, TimeUnit* aBufferStartTime) = 0;
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -34,32 +34,63 @@ AppendStateToStr(TrackBuffersManager::Ap
       return "PARSING_MEDIA_SEGMENT";
     default:
       return "IMPOSSIBLE";
   }
 }
 
 static Atomic<uint32_t> sStreamSourceID(0u);
 
+#ifdef MOZ_EME
+class DispatchKeyNeededEvent : public nsRunnable {
+public:
+  DispatchKeyNeededEvent(AbstractMediaDecoder* aDecoder,
+                         nsTArray<uint8_t>& aInitData,
+                         const nsString& aInitDataType)
+    : mDecoder(aDecoder)
+    , mInitData(aInitData)
+    , mInitDataType(aInitDataType)
+  {
+  }
+  NS_IMETHOD Run() {
+    // Note: Null check the owner, as the decoder could have been shutdown
+    // since this event was dispatched.
+    MediaDecoderOwner* owner = mDecoder->GetOwner();
+    if (owner) {
+      owner->DispatchEncrypted(mInitData, mInitDataType);
+    }
+    mDecoder = nullptr;
+    return NS_OK;
+  }
+private:
+  nsRefPtr<AbstractMediaDecoder> mDecoder;
+  nsTArray<uint8_t> mInitData;
+  nsString mInitDataType;
+};
+#endif // MOZ_EME
+
 TrackBuffersManager::TrackBuffersManager(dom::SourceBuffer* aParent, MediaSourceDecoder* aParentDecoder, const nsACString& aType)
   : mInputBuffer(new MediaByteBuffer)
   , mAppendState(AppendState::WAITING_FOR_SEGMENT)
   , mBufferFull(false)
   , mFirstInitializationSegmentReceived(false)
   , mActiveTrack(false)
   , mType(aType)
   , mParser(ContainerParser::CreateForMIMEType(aType))
   , mProcessedInput(0)
   , mAppendRunning(false)
   , mTaskQueue(aParentDecoder->GetDemuxer()->GetTaskQueue())
   , mParent(new nsMainThreadPtrHolder<dom::SourceBuffer>(aParent, false /* strict */))
   , mParentDecoder(new nsMainThreadPtrHolder<MediaSourceDecoder>(aParentDecoder, false /* strict */))
   , mMediaSourceDemuxer(mParentDecoder->GetDemuxer())
   , mMediaSourceDuration(mTaskQueue, Maybe<double>(), "TrackBuffersManager::mMediaSourceDuration (Mirror)")
   , mAbort(false)
+  , mEvictionThreshold(Preferences::GetUint("media.mediasource.eviction_threshold",
+                                            100 * (1 << 20)))
+  , mEvictionOccurred(false)
   , mMonitor("TrackBuffersManager")
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be instanciated on the main thread");
   nsRefPtr<TrackBuffersManager> self = this;
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableFunction([self] () {
       self->mMediaSourceDuration.Connect(self->mParentDecoder->CanonicalExplicitDuration());
     });
@@ -171,16 +202,21 @@ TrackBuffersManager::EvictData(TimeUnit 
   int64_t toEvict = GetSize() - aThreshold;
   if (toEvict <= 0) {
     return EvictDataResult::NO_DATA_EVICTED;
   }
   if (toEvict <= 512*1024) {
     // Don't bother evicting less than 512KB.
     return EvictDataResult::CANT_EVICT;
   }
+
+  if (mBufferFull && mEvictionOccurred) {
+    return EvictDataResult::BUFFER_FULL;
+  }
+
   MSE_DEBUG("Reaching our size limit, schedule eviction of %lld bytes", toEvict);
 
   nsCOMPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArgs<TimeUnit, uint32_t>(
       this, &TrackBuffersManager::DoEvictData,
       aPlaybackTime, toEvict);
   GetTaskQueue()->Dispatch(task.forget());
 
@@ -340,22 +376,22 @@ TrackBuffersManager::CompleteResetParser
 }
 
 void
 TrackBuffersManager::DoEvictData(const TimeUnit& aPlaybackTime,
                                  uint32_t aSizeToEvict)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  // Remove any data we've already played, up to 5s behind.
-  TimeUnit lowerLimit = aPlaybackTime - TimeUnit::FromSeconds(5);
-  TimeUnit to;
   // Video is what takes the most space, only evict there if we have video.
   const auto& track = HasVideo() ? mVideoTracks : mAudioTracks;
   const auto& buffer = track.mBuffers.LastElement();
+  // Remove any data we've already played, or before the next sample to be
+  // demuxed whichever is lowest.
+  TimeUnit lowerLimit = std::min(track.mNextSampleTime, aPlaybackTime);
   uint32_t lastKeyFrameIndex = 0;
   int64_t toEvict = aSizeToEvict;
   uint32_t partialEvict = 0;
   for (uint32_t i = 0; i < buffer.Length(); i++) {
     const auto& frame = buffer[i];
     if (frame->mKeyframe) {
       lastKeyFrameIndex = i;
       toEvict -= partialEvict;
@@ -364,46 +400,59 @@ TrackBuffersManager::DoEvictData(const T
       }
       partialEvict = 0;
     }
     if (frame->mTime >= lowerLimit.ToMicroseconds()) {
       break;
     }
     partialEvict += sizeof(*frame) + frame->mSize;
   }
+
+  int64_t finalSize = mSizeSourceBuffer - aSizeToEvict;
+
   if (lastKeyFrameIndex > 0) {
+    MSE_DEBUG("Step1. Evicting %u bytes prior currentTime",
+              aSizeToEvict - toEvict);
     CodedFrameRemoval(
       TimeInterval(TimeUnit::FromMicroseconds(0),
-                   TimeUnit::FromMicroseconds(buffer[lastKeyFrameIndex-1]->mTime)));
+                   TimeUnit::FromMicroseconds(buffer[lastKeyFrameIndex]->mTime - 1)));
   }
-  if (toEvict <= 0) {
+
+  if (mSizeSourceBuffer <= finalSize) {
     return;
   }
 
-  // Still some to remove. Remove data starting from the end, up to 5s ahead
-  // of our playtime.
-  TimeUnit upperLimit = aPlaybackTime + TimeUnit::FromSeconds(5);
+  toEvict = mSizeSourceBuffer - finalSize;
+
+  // Still some to remove. Remove data starting from the end, up to 30s ahead
+  // of the later of the playback time or the next sample to be demuxed.
+  // 30s is a value chosen as it appears to work with YouTube.
+  TimeUnit upperLimit =
+    std::max(aPlaybackTime, track.mNextSampleTime) + TimeUnit::FromSeconds(30);
+  lastKeyFrameIndex = buffer.Length();
   for (int32_t i = buffer.Length() - 1; i >= 0; i--) {
     const auto& frame = buffer[i];
     if (frame->mKeyframe) {
       lastKeyFrameIndex = i;
       toEvict -= partialEvict;
       if (toEvict < 0) {
         break;
       }
       partialEvict = 0;
     }
     if (frame->mTime <= upperLimit.ToMicroseconds()) {
       break;
     }
     partialEvict += sizeof(*frame) + frame->mSize;
   }
-  if (lastKeyFrameIndex + 1 < buffer.Length()) {
+  if (lastKeyFrameIndex < buffer.Length()) {
+    MSE_DEBUG("Step2. Evicting %u bytes from trailing data",
+              mSizeSourceBuffer - finalSize);
     CodedFrameRemoval(
-      TimeInterval(TimeUnit::FromMicroseconds(buffer[lastKeyFrameIndex+1]->mTime),
+      TimeInterval(TimeUnit::FromMicroseconds(buffer[lastKeyFrameIndex]->GetEndTime() + 1),
                    TimeUnit::FromInfinity()));
   }
 }
 
 nsRefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::CodedFrameRemovalWithPromise(TimeInterval aInterval)
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -554,19 +603,17 @@ TrackBuffersManager::CodedFrameRemoval(T
 
     // 5. If this object is in activeSourceBuffers, the current playback position
     // is greater than or equal to start and less than the remove end timestamp,
     // and HTMLMediaElement.readyState is greater than HAVE_METADATA, then set the
     // HTMLMediaElement.readyState attribute to HAVE_METADATA and stall playback.
     // This will be done by the MDSM during playback.
     // TODO properly, so it works even if paused.
   }
-  // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
-  // TODO.
-  mBufferFull = false;
+
   {
     MonitorAutoLock mon(mMonitor);
     mVideoBufferedRanges = mVideoTracks.mBufferedRanges;
     mAudioBufferedRanges = mAudioTracks.mBufferedRanges;
   }
 
   if (HasVideo()) {
     MSE_DEBUG("after video ranges=%s",
@@ -575,16 +622,22 @@ TrackBuffersManager::CodedFrameRemoval(T
   if (HasAudio()) {
     MSE_DEBUG("after audio ranges=%s",
               DumpTimeRanges(mAudioTracks.mBufferedRanges).get());
   }
 
   // Update our reported total size.
   mSizeSourceBuffer = mVideoTracks.mSizeBuffer + mAudioTracks.mSizeBuffer;
 
+  // 4. If buffer full flag equals true and this object is ready to accept more bytes, then set the buffer full flag to false.
+  if (mBufferFull && mSizeSourceBuffer < mEvictionThreshold) {
+    mBufferFull = false;
+  }
+  mEvictionOccurred = true;
+
   // Tell our demuxer that data was removed.
   mMediaSourceDemuxer->NotifyTimeRangesChanged();
 
   return dataRemoved;
 }
 
 nsRefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::InitSegmentParserLoop()
@@ -948,27 +1001,29 @@ TrackBuffersManager::OnDemuxerInitDone(n
 
     // 6. Set first initialization segment received flag to true.
     mFirstInitializationSegmentReceived = true;
   } else {
     mAudioTracks.mLastInfo = new SharedTrackInfo(info.mAudio, streamID);
     mVideoTracks.mLastInfo = new SharedTrackInfo(info.mVideo, streamID);
   }
 
-  // TODO CHECK ENCRYPTION
   UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
   if (crypto && crypto->IsEncrypted()) {
 #ifdef MOZ_EME
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
-//      NS_DispatchToMainThread(
-//        new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc")));
+      NS_DispatchToMainThread(
+        new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc")));
     }
 #endif // MOZ_EME
     info.mCrypto = *crypto;
+    // We clear our crypto init data array, so the MediaFormatReader will
+    // not emit an encrypted event for the same init data again.
+    info.mCrypto.mInitDatas.Clear();
     mEncrypted = true;
   }
 
   {
     MonitorAutoLock mon(mMonitor);
     mInfo = info;
   }
 
@@ -1175,18 +1230,20 @@ TrackBuffersManager::CompleteCodedFrameP
     }
   }
 
   // Update our reported total size.
   mSizeSourceBuffer = mVideoTracks.mSizeBuffer + mAudioTracks.mSizeBuffer;
 
   // Return to step 6.4 of Segment Parser Loop algorithm
   // 4. If this SourceBuffer is full and cannot accept more media data, then set the buffer full flag to true.
-  // TODO
-  mBufferFull = false;
+  if (mSizeSourceBuffer >= mEvictionThreshold) {
+    mBufferFull = true;
+    mEvictionOccurred = false;
+  }
 
   // 5. If the input buffer does not contain a complete media segment, then jump to the need more data step below.
   if (mParser->MediaSegmentRange().IsNull()) {
     ResolveProcessing(true, __func__);
     return;
   }
 
   // 6. Remove the media segment bytes from the beginning of the input buffer.
@@ -1355,88 +1412,94 @@ TrackBuffersManager::ProcessFrame(MediaR
   //      Remove all coded frames from track buffer that have a presentation timestamp greater than or equal to highest end timestamp and less than frame end timestamp
 
   // There is an ambiguity on how to remove frames, which was lodged with:
   // https://www.w3.org/Bugs/Public/show_bug.cgi?id=28710, implementing as per
   // bug description.
   Maybe<uint32_t> firstRemovedIndex;
   TimeInterval removedInterval;
   TrackBuffer& data = trackBuffer.mBuffers.LastElement();
-  if (trackBuffer.mBufferedRanges.ContainsStrict(presentationTimestamp)) {
+  bool removeCodedFrames =
+    trackBuffer.mHighestEndTimestamp.isSome()
+      ? trackBuffer.mHighestEndTimestamp.ref() <= presentationTimestamp
+      : true;
+  if (removeCodedFrames) {
     TimeUnit lowerBound =
       trackBuffer.mHighestEndTimestamp.valueOr(presentationTimestamp);
-    for (uint32_t i = 0; i < data.Length();) {
-      MediaRawData* sample = data[i].get();
-      if (sample->mTime >= lowerBound.ToMicroseconds() &&
-          sample->mTime < frameEndTimestamp.ToMicroseconds()) {
-        if (firstRemovedIndex.isNothing()) {
-          removedInterval =
-            TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
-                         TimeUnit::FromMicroseconds(sample->GetEndTime()));
-          firstRemovedIndex = Some(i);
+    if (trackBuffer.mBufferedRanges.ContainsStrict(lowerBound)) {
+      for (uint32_t i = 0; i < data.Length();) {
+        MediaRawData* sample = data[i].get();
+        if (sample->mTime >= lowerBound.ToMicroseconds() &&
+            sample->mTime < frameEndTimestamp.ToMicroseconds()) {
+          if (firstRemovedIndex.isNothing()) {
+            removedInterval =
+              TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
+                           TimeUnit::FromMicroseconds(sample->GetEndTime()));
+            firstRemovedIndex = Some(i);
+          } else {
+            removedInterval = removedInterval.Span(
+              TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
+                           TimeUnit::FromMicroseconds(sample->GetEndTime())));
+          }
+          trackBuffer.mSizeBuffer -= sizeof(*sample) + sample->mSize;
+          MSE_DEBUGV("Overlapping frame:%u ([%f, %f))",
+                    i,
+                    TimeUnit::FromMicroseconds(sample->mTime).ToSeconds(),
+                    TimeUnit::FromMicroseconds(sample->GetEndTime()).ToSeconds());
+          data.RemoveElementAt(i);
+
+          if (trackBuffer.mNextGetSampleIndex.isSome()) {
+            if (trackBuffer.mNextGetSampleIndex.ref() == i) {
+              MSE_DEBUG("Next sample to be played got evicted");
+              trackBuffer.mNextGetSampleIndex.reset();
+            } else if (trackBuffer.mNextGetSampleIndex.ref() > i) {
+              trackBuffer.mNextGetSampleIndex.ref()--;
+            }
+          }
         } else {
-          removedInterval = removedInterval.Span(
-            TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
-                         TimeUnit::FromMicroseconds(sample->GetEndTime())));
+          i++;
         }
-        trackBuffer.mSizeBuffer -= sizeof(*sample) + sample->mSize;
-        MSE_DEBUGV("Overlapping frame:%u ([%f, %f))",
-                  i,
-                  TimeUnit::FromMicroseconds(sample->mTime).ToSeconds(),
-                  TimeUnit::FromMicroseconds(sample->GetEndTime()).ToSeconds());
-        data.RemoveElementAt(i);
-
-        if (trackBuffer.mNextGetSampleIndex.isSome()) {
-          if (trackBuffer.mNextGetSampleIndex.ref() == i) {
-            MSE_DEBUG("Next sample to be played got evicted");
-            trackBuffer.mNextGetSampleIndex.reset();
-          } else if (trackBuffer.mNextGetSampleIndex.ref() > i) {
-            trackBuffer.mNextGetSampleIndex.ref()--;
-          }
-        }
-      } else {
-        i++;
       }
     }
-  }
-  // 15. Remove decoding dependencies of the coded frames removed in the previous step:
-  // Remove all coded frames between the coded frames removed in the previous step and the next random access point after those removed frames.
-  if (firstRemovedIndex.isSome()) {
-    uint32_t start = firstRemovedIndex.ref();
-    uint32_t end = start;
-    for (;end < data.Length(); end++) {
-      MediaRawData* sample = data[end].get();
-      if (sample->mKeyframe) {
-        break;
+    // 15. Remove decoding dependencies of the coded frames removed in the previous step:
+    // Remove all coded frames between the coded frames removed in the previous step and the next random access point after those removed frames.
+    if (firstRemovedIndex.isSome()) {
+      uint32_t start = firstRemovedIndex.ref();
+      uint32_t end = start;
+      for (;end < data.Length(); end++) {
+        MediaRawData* sample = data[end].get();
+        if (sample->mKeyframe) {
+          break;
+        }
+        removedInterval = removedInterval.Span(
+          TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
+                       TimeUnit::FromMicroseconds(sample->GetEndTime())));
+        trackBuffer.mSizeBuffer -= sizeof(*sample) + sample->mSize;
       }
-      removedInterval = removedInterval.Span(
-        TimeInterval(TimeUnit::FromMicroseconds(sample->mTime),
-                     TimeUnit::FromMicroseconds(sample->GetEndTime())));
-      trackBuffer.mSizeBuffer -= sizeof(*sample) + sample->mSize;
-    }
-    data.RemoveElementsAt(start, end - start);
+      data.RemoveElementsAt(start, end - start);
 
-    MSE_DEBUG("Removing undecodable frames from:%u (frames:%u) ([%f, %f))",
-              start, end - start,
-              removedInterval.mStart.ToSeconds(), removedInterval.mEnd.ToSeconds());
+      MSE_DEBUG("Removing undecodable frames from:%u (frames:%u) ([%f, %f))",
+                start, end - start,
+                removedInterval.mStart.ToSeconds(), removedInterval.mEnd.ToSeconds());
 
-    if (trackBuffer.mNextGetSampleIndex.isSome()) {
-      if (trackBuffer.mNextGetSampleIndex.ref() >= start &&
-          trackBuffer.mNextGetSampleIndex.ref() < end) {
-        MSE_DEBUG("Next sample to be played got evicted");
-        trackBuffer.mNextGetSampleIndex.reset();
-      } else if (trackBuffer.mNextGetSampleIndex.ref() >= end) {
-        trackBuffer.mNextGetSampleIndex.ref() -= end - start;
+      if (trackBuffer.mNextGetSampleIndex.isSome()) {
+        if (trackBuffer.mNextGetSampleIndex.ref() >= start &&
+            trackBuffer.mNextGetSampleIndex.ref() < end) {
+          MSE_DEBUG("Next sample to be played got evicted");
+          trackBuffer.mNextGetSampleIndex.reset();
+        } else if (trackBuffer.mNextGetSampleIndex.ref() >= end) {
+          trackBuffer.mNextGetSampleIndex.ref() -= end - start;
+        }
       }
+
+      // Update our buffered range to exclude the range just removed.
+      trackBuffer.mBufferedRanges -= removedInterval;
+      MOZ_ASSERT(trackBuffer.mNextInsertionIndex.isNothing() ||
+                 trackBuffer.mNextInsertionIndex.ref() <= start);
     }
-
-    // Update our buffered range to exclude the range just removed.
-    trackBuffer.mBufferedRanges -= removedInterval;
-    MOZ_ASSERT(trackBuffer.mNextInsertionIndex.isNothing() ||
-               trackBuffer.mNextInsertionIndex.ref() <= start);
   }
 
   // 16. Add the coded frame with the presentation timestamp, decode timestamp, and frame duration to the track buffer.
   aSample->mTime = presentationTimestamp.ToMicroseconds();
   aSample->mTimecode = decodeTimestamp.ToMicroseconds();
   aSample->mTrackInfo = trackBuffer.mLastInfo;
 
   if (data.IsEmpty()) {
@@ -1559,21 +1622,21 @@ TrackBuffersManager::GetTracksList()
   return tracks;
 }
 
 void
 TrackBuffersManager::RestoreCachedVariables()
 {
   MOZ_ASSERT(OnTaskQueue());
   if (mTimestampOffset != mLastTimestampOffset) {
+    nsRefPtr<TrackBuffersManager> self = this;
     nsCOMPtr<nsIRunnable> task =
-      NS_NewRunnableMethodWithArg<TimeUnit>(
-        mParent.get(),
-        static_cast<void (dom::SourceBuffer::*)(const TimeUnit&)>(&dom::SourceBuffer::SetTimestampOffset), /* beauty uh? :) */
-        mTimestampOffset);
+      NS_NewRunnableFunction([self] {
+        self->mParent->SetTimestampOffset(self->mTimestampOffset);
+      });
     AbstractThread::MainThread()->Dispatch(task.forget());
   }
 }
 
 void
 TrackBuffersManager::SetAppendState(TrackBuffersManager::AppendState aAppendState)
 {
   MSE_DEBUG("AppendState changed from %s to %s",
--- a/dom/media/mediasource/TrackBuffersManager.h
+++ b/dom/media/mediasource/TrackBuffersManager.h
@@ -305,16 +305,18 @@ private:
 
   // Set to true if abort was called.
   Atomic<bool> mAbort;
   // Set to true if mediasource state changed to ended.
   Atomic<bool> mEnded;
 
   // Global size of this source buffer content.
   Atomic<int64_t> mSizeSourceBuffer;
+  uint32_t mEvictionThreshold;
+  Atomic<bool> mEvictionOccurred;
 
   // Monitor to protect following objects accessed across multipple threads.
   mutable Monitor mMonitor;
   // Stable audio and video track time ranges.
   TimeIntervals mVideoBufferedRanges;
   TimeIntervals mAudioBufferedRanges;
   // MediaInfo of the first init segment read.
   MediaInfo mInfo;
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -105,21 +105,16 @@ AudioContext::AudioContext(nsPIDOMWindow
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
   // We skip calling SetIsOnlyNodeForContext and the creation of the
   // audioChannelAgent during mDestination's constructor, because we can only
   // call them after mDestination has been set up.
   mDestination->CreateAudioChannelAgent();
   mDestination->SetIsOnlyNodeForContext(true);
-  // We don't use the cubeb context yet, but need to ensure it is created on
-  // the main thread.
-  if (!aIsOffline && !CubebUtils::GetCubebContext()) {
-    NS_WARNING("Audio backend initialization failed.");
-  }
 }
 
 AudioContext::~AudioContext()
 {
   nsPIDOMWindow* window = GetOwner();
   if (window) {
     window->RemoveAudioContext(this);
   }
--- a/dom/storage/DOMStorageCache.cpp
+++ b/dom/storage/DOMStorageCache.cpp
@@ -766,18 +766,21 @@ DOMStorageCache::StartDatabase()
 
     nsresult rv = db->Init();
     if (NS_FAILED(rv)) {
       return nullptr;
     }
 
     sDatabase = db.forget();
   } else {
+    // Use DOMLocalStorageManager::Ensure in case we're called from
+    // DOMSessionStorageManager's initializer and we haven't yet initialized the
+    // local storage manager.
     nsRefPtr<DOMStorageDBChild> db = new DOMStorageDBChild(
-        DOMLocalStorageManager::Self());
+        DOMLocalStorageManager::Ensure());
 
     nsresult rv = db->Init();
     if (NS_FAILED(rv)) {
       return nullptr;
     }
 
     db.forget(&sDatabase);
   }
--- a/dom/storage/DOMStorageManager.cpp
+++ b/dom/storage/DOMStorageManager.cpp
@@ -623,16 +623,31 @@ DOMLocalStorageManager::DOMLocalStorageM
   }
 }
 
 DOMLocalStorageManager::~DOMLocalStorageManager()
 {
   sSelf = nullptr;
 }
 
+DOMLocalStorageManager*
+DOMLocalStorageManager::Ensure()
+{
+  if (sSelf) {
+    return sSelf;
+  }
+
+  // Cause sSelf to be populated.
+  nsCOMPtr<nsIDOMStorageManager> initializer =
+    do_GetService("@mozilla.org/dom/localStorage-manager;1");
+  MOZ_ASSERT(sSelf, "Didn't initialize?");
+
+  return sSelf;
+}
+
 // DOMSessionStorageManager
 
 DOMSessionStorageManager::DOMSessionStorageManager()
   : DOMStorageManager(SessionStorage)
 {
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     // Do this only on the child process.  The thread IPC bridge
     // is also used to communicate chrome observer notifications.
--- a/dom/storage/DOMStorageManager.h
+++ b/dom/storage/DOMStorageManager.h
@@ -121,16 +121,19 @@ class DOMLocalStorageManager final : pub
 {
 public:
   DOMLocalStorageManager();
   virtual ~DOMLocalStorageManager();
 
   // Global getter of localStorage manager service
   static DOMLocalStorageManager* Self() { return sSelf; }
 
+  // Like Self, but creates an instance if we're not yet initialized.
+  static DOMLocalStorageManager* Ensure();
+
 private:
   static DOMLocalStorageManager* sSelf;
 };
 
 class DOMSessionStorageManager final : public DOMStorageManager
 {
 public:
   DOMSessionStorageManager();
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -374,51 +374,49 @@ NS_INTERFACE_MAP_BEGIN(ServiceWorkerMana
   NS_INTERFACE_MAP_ENTRY(nsIServiceWorkerManager)
   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIServiceWorkerManager)
 NS_INTERFACE_MAP_END
 
 ServiceWorkerManager::ServiceWorkerManager()
   : mActor(nullptr)
+  , mShuttingDown(false)
 {
   // Register this component to PBackground.
   MOZ_ALWAYS_TRUE(BackgroundChild::GetOrCreateForCurrentThread(this));
 }
 
 ServiceWorkerManager::~ServiceWorkerManager()
 {
   // The map will assert if it is not empty when destroyed.
   mRegistrationInfos.Clear();
-
-  if (mActor) {
-    mActor->ManagerShuttingDown();
-
-    nsRefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
-    nsresult rv = NS_DispatchToMainThread(runnable);
-    unused << NS_WARN_IF(NS_FAILED(rv));
-  }
+  MOZ_ASSERT(!mActor);
 }
 
 void
 ServiceWorkerManager::Init()
 {
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    DebugOnly<nsresult> rv;
+    rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     nsRefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
     MOZ_ASSERT(swr);
 
     nsTArray<ServiceWorkerRegistrationData> data;
     swr->GetRegistrations(data);
     LoadRegistrations(data);
 
-    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       DebugOnly<nsresult> rv;
-      rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false /* ownsWeak */);
-      MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_SESSION_HISTORY, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, PURGE_DOMAIN_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
       rv = obs->AddObserver(this, WEBAPPS_CLEAR_DATA, false /* ownsWeak */);
       MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
@@ -867,16 +865,17 @@ class ServiceWorkerRegisterJob final : p
   friend class ContinueInstallTask;
 
   nsCString mScope;
   nsCString mScriptSpec;
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsRefPtr<ServiceWorkerInfo> mUpdateAndInstallInfo;
+  nsCOMPtr<nsILoadGroup> mLoadGroup;
 
   ~ServiceWorkerRegisterJob()
   { }
 
   enum
   {
     REGISTER_JOB = 0,
     UPDATE_JOB = 1,
@@ -887,25 +886,29 @@ class ServiceWorkerRegisterJob final : p
 public:
   NS_DECL_ISUPPORTS_INHERITED
 
   // [[Register]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            const nsCString& aScope,
                            const nsCString& aScriptSpec,
                            ServiceWorkerUpdateFinishCallback* aCallback,
-                           nsIPrincipal* aPrincipal)
+                           nsIPrincipal* aPrincipal,
+                           nsILoadGroup* aLoadGroup)
     : ServiceWorkerJob(aQueue)
     , mScope(aScope)
     , mScriptSpec(aScriptSpec)
     , mCallback(aCallback)
     , mPrincipal(aPrincipal)
+    , mLoadGroup(aLoadGroup)
     , mJobType(REGISTER_JOB)
     , mCanceled(false)
-  { }
+  {
+    MOZ_ASSERT(mLoadGroup);
+  }
 
   // [[Update]]
   ServiceWorkerRegisterJob(ServiceWorkerJobQueue* aQueue,
                            ServiceWorkerRegistrationInfo* aRegistration,
                            ServiceWorkerUpdateFinishCallback* aCallback)
     : ServiceWorkerJob(aQueue)
     , mRegistration(aRegistration)
     , mCallback(aCallback)
@@ -1225,17 +1228,17 @@ private:
     // byte-for-byte match with the script resource of newestWorker...
     if (workerInfo && workerInfo->ScriptSpec().Equals(mRegistration->mScriptSpec)) {
       cacheName = workerInfo->CacheName();
     }
 
     nsresult rv =
       serviceWorkerScriptCache::Compare(mRegistration->mPrincipal, cacheName,
                                         NS_ConvertUTF8toUTF16(mRegistration->mScriptSpec),
-                                        this);
+                                        this, mLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return Fail(rv);
     }
   }
 
   void
   Succeed()
   {
@@ -1529,18 +1532,30 @@ ServiceWorkerManager::Register(nsIDOMWin
   }
 
   ServiceWorkerJobQueue* queue = GetOrCreateJobQueue(originSuffix, cleanedScope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerResolveWindowPromiseOnUpdateCallback> cb =
     new ServiceWorkerResolveWindowPromiseOnUpdateCallback(window, promise);
 
+  nsCOMPtr<nsILoadGroup> docLoadGroup = doc->GetDocumentLoadGroup();
+  nsRefPtr<WorkerLoadInfo::InterfaceRequestor> ir =
+    new WorkerLoadInfo::InterfaceRequestor(documentPrincipal, docLoadGroup);
+  ir->MaybeAddTabChild(docLoadGroup);
+
+  // Create a load group that is separate from, yet related to, the document's load group.
+  // This allows checks for interfaces like nsILoadContext to yield the values used by the
+  // the document, yet will not cancel the update job if the document's load group is cancelled.
+  nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
+  rv = loadGroup->SetNotificationCallbacks(ir);
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
+
   nsRefPtr<ServiceWorkerRegisterJob> job =
-    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal);
+    new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb, documentPrincipal, loadGroup);
   queue->Append(job);
 
   AssertIsOnMainThread();
   Telemetry::Accumulate(Telemetry::SERVICE_WORKER_REGISTRATIONS, 1);
 
   promise.forget(aPromise);
   return NS_OK;
 }
@@ -1548,29 +1563,33 @@ ServiceWorkerManager::Register(nsIDOMWin
 void
 ServiceWorkerManager::AppendPendingOperation(ServiceWorkerJobQueue* aQueue,
                                              ServiceWorkerJob* aJob)
 {
   MOZ_ASSERT(!mActor);
   MOZ_ASSERT(aQueue);
   MOZ_ASSERT(aJob);
 
-  PendingOperation* opt = mPendingOperations.AppendElement();
-  opt->mQueue = aQueue;
-  opt->mJob = aJob;
+  if (!mShuttingDown) {
+    PendingOperation* opt = mPendingOperations.AppendElement();
+    opt->mQueue = aQueue;
+    opt->mJob = aJob;
+  }
 }
 
 void
 ServiceWorkerManager::AppendPendingOperation(nsIRunnable* aRunnable)
 {
   MOZ_ASSERT(!mActor);
   MOZ_ASSERT(aRunnable);
 
-  PendingOperation* opt = mPendingOperations.AppendElement();
-  opt->mRunnable = aRunnable;
+  if (!mShuttingDown) {
+    PendingOperation* opt = mPendingOperations.AppendElement();
+    opt->mRunnable = aRunnable;
+  }
 }
 
 /*
  * Used to handle ExtendableEvent::waitUntil() and proceed with
  * installation/activation.
  */
 class LifecycleEventPromiseHandler final : public PromiseNativeHandler
 {
@@ -2378,18 +2397,20 @@ private:
     PrincipalInfo principalInfo;
     if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(mPrincipal,
                                                       &principalInfo)))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
-    MOZ_ASSERT(swm->mActor);
-    swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
+    // Could it be that we are shutting down.
+    if (swm->mActor) {
+      swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
+    }
 
     nsAutoCString scopeKey;
     nsresult rv = swm->PrincipalToScopeKey(mPrincipal, scopeKey);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     // "Let registration be the result of running [[Get Registration]]
@@ -2730,20 +2751,25 @@ ServiceWorkerManager::ActorCreated(mozil
   mPendingOperations.Clear();
 }
 
 void
 ServiceWorkerManager::StoreRegistration(
                                    nsIPrincipal* aPrincipal,
                                    ServiceWorkerRegistrationInfo* aRegistration)
 {
-  MOZ_ASSERT(mActor);
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aRegistration);
 
+  if (mShuttingDown) {
+    return;
+  }
+
+  MOZ_ASSERT(mActor);
+
   ServiceWorkerRegistrationData data;
   nsresult rv = PopulateRegistrationData(aPrincipal, aRegistration, data);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   PrincipalInfo principalInfo;
   if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(aPrincipal,
@@ -4059,16 +4085,20 @@ ServiceWorkerManager::MaybeRemoveRegistr
 }
 
 void
 ServiceWorkerManager::RemoveRegistrationInternal(ServiceWorkerRegistrationInfo* aRegistration)
 {
   MOZ_ASSERT(aRegistration);
   MOZ_ASSERT(!aRegistration->IsControllingDocuments());
 
+  if (mShuttingDown) {
+    return;
+  }
+
   // All callers should be either from a job in which case the actor is
   // available, or from MaybeStopControlling(), in which case, this will only be
   // called if a valid registration is found. If a valid registration exists,
   // it means the actor is available since the original map of registrations is
   // populated by it, and any new registrations wait until the actor is
   // available before proceeding (See ServiceWorkerRegisterJob::Start).
   MOZ_ASSERT(mActor);
 
@@ -4492,31 +4522,32 @@ ServiceWorkerManager::UpdateAllRegistrat
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::Observe(nsISupports* aSubject,
                               const char* aTopic,
                               const char16_t* aData)
 {
-  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
-
   if (strcmp(aTopic, PURGE_SESSION_HISTORY) == 0) {
+    MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     RemoveAll();
     PropagateRemoveAll();
     return NS_OK;
   }
 
   if (strcmp(aTopic, PURGE_DOMAIN_DATA) == 0) {
+    MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     nsAutoString domain(aData);
     RemoveAndPropagate(NS_ConvertUTF16toUTF8(domain));
     return NS_OK;
   }
 
   if (strcmp(aTopic, WEBAPPS_CLEAR_DATA) == 0) {
+    MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
     nsCOMPtr<mozIApplicationClearPrivateDataParams> params =
       do_QueryInterface(aSubject);
     if (NS_WARN_IF(!params)) {
       return NS_OK;
     }
 
     uint32_t appId;
     nsresult rv = params->GetAppId(&appId);
@@ -4536,28 +4567,45 @@ ServiceWorkerManager::Observe(nsISupport
 
     nsCOMPtr<nsIPrincipal> principal;
     app->GetPrincipal(getter_AddRefs(principal));
     if (NS_WARN_IF(!principal)) {
       return NS_OK;
     }
 
     RemoveAllRegistrations(principal);
-  } else if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    return NS_OK;
+  }
+
+  if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
+    mShuttingDown = true;
+
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
-      obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
-      obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
-      obs->RemoveObserver(this, WEBAPPS_CLEAR_DATA);
+
+      if (XRE_GetProcessType() == GeckoProcessType_Default) {
+        obs->RemoveObserver(this, PURGE_SESSION_HISTORY);
+        obs->RemoveObserver(this, PURGE_DOMAIN_DATA);
+        obs->RemoveObserver(this, WEBAPPS_CLEAR_DATA);
+      }
     }
-  } else {
-    MOZ_CRASH("Received message we aren't supposed to be registered for!");
-  }
-
+
+    if (mActor) {
+      mActor->ManagerShuttingDown();
+
+      nsRefPtr<TeardownRunnable> runnable = new TeardownRunnable(mActor);
+      nsresult rv = NS_DispatchToMainThread(runnable);
+      unused << NS_WARN_IF(NS_FAILED(rv));
+      mActor = nullptr;
+    }
+    return NS_OK;
+  }
+
+  MOZ_CRASH("Received message we aren't supposed to be registered for!");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 ServiceWorkerManager::PropagateSoftUpdate(JS::Handle<JS::Value> aOriginAttributes,
                                           const nsAString& aScope,
                                           JSContext* aCx)
 {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -547,15 +547,17 @@ private:
   // Removes all service worker registrations for a given principal.
   void
   RemoveAllRegistrations(nsIPrincipal* aPrincipal);
 
   nsRefPtr<ServiceWorkerManagerChild> mActor;
 
   struct PendingOperation;
   nsTArray<PendingOperation> mPendingOperations;
+
+  bool mShuttingDown;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_workers_serviceworkermanager_h
--- a/dom/workers/ServiceWorkerScriptCache.cpp
+++ b/dom/workers/ServiceWorkerScriptCache.cpp
@@ -72,31 +72,32 @@ public:
   explicit CompareNetwork(CompareManager* aManager)
     : mManager(aManager)
   {
     MOZ_ASSERT(aManager);
     AssertIsOnMainThread();
   }
 
   nsresult
-  Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL)
+  Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL, nsILoadGroup* aLoadGroup)
   {
     MOZ_ASSERT(aPrincipal);
     AssertIsOnMainThread();
 
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, nullptr);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     rv = NS_NewChannel(getter_AddRefs(mChannel),
                        uri, aPrincipal,
                        nsILoadInfo::SEC_NORMAL,
-                       nsIContentPolicy::TYPE_SCRIPT); // FIXME(nsm): TYPE_SERVICEWORKER
+                       nsIContentPolicy::TYPE_SCRIPT, // FIXME(nsm): TYPE_SERVICEWORKER
+                       aLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     nsLoadFlags flags;
     rv = mChannel->GetLoadFlags(&flags);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
@@ -275,34 +276,34 @@ public:
     , mCacheFinished(false)
     , mInCache(false)
   {
     AssertIsOnMainThread();
   }
 
   nsresult
   Initialize(nsIPrincipal* aPrincipal, const nsAString& aURL,
-             const nsAString& aCacheName)
+             const nsAString& aCacheName, nsILoadGroup* aLoadGroup)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aPrincipal);
 
     mURL = aURL;
 
     // Always create a CacheStorage since we want to write the network entry to
     // the cache even if there isn't an existing one.
     ErrorResult result;
     mCacheStorage = CreateCacheStorage(aPrincipal, result, getter_AddRefs(mSandbox));
     if (NS_WARN_IF(result.Failed())) {
       MOZ_ASSERT(!result.IsErrorWithMessage());
       return result.StealNSResult();
     }
 
     mCN = new CompareNetwork(this);
-    nsresult rv = mCN->Initialize(aPrincipal, aURL);
+    nsresult rv = mCN->Initialize(aPrincipal, aURL, aLoadGroup);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     if (!aCacheName.IsEmpty()) {
       mCC = new CompareCache(this);
       rv = mCC->Initialize(aPrincipal, aURL, aCacheName);
       if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -931,26 +932,27 @@ GenerateCacheName(nsAString& aName)
   id.ToProvidedString(chars);
   aName.AssignASCII(chars, NSID_LENGTH);
 
   return NS_OK;
 }
 
 nsresult
 Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
-        const nsAString& aURL, CompareCallback* aCallback)
+        const nsAString& aURL, CompareCallback* aCallback,
+        nsILoadGroup* aLoadGroup)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(!aURL.IsEmpty());
   MOZ_ASSERT(aCallback);
 
   nsRefPtr<CompareManager> cm = new CompareManager(aCallback);
 
-  nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName);
+  nsresult rv = cm->Initialize(aPrincipal, aURL, aCacheName, aLoadGroup);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 } // serviceWorkerScriptCache namespace
--- a/dom/workers/ServiceWorkerScriptCache.h
+++ b/dom/workers/ServiceWorkerScriptCache.h
@@ -39,17 +39,17 @@ public:
                    const nsACString& aMaxScope) = 0;
 
   NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
   NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
 };
 
 nsresult
 Compare(nsIPrincipal* aPrincipal, const nsAString& aCacheName,
-        const nsAString& aURL, CompareCallback* aCallback);
+        const nsAString& aURL, CompareCallback* aCallback, nsILoadGroup* aLoadGroup);
 
 } // serviceWorkerScriptCache namespace
 
 } // workers namespace
 } // dom namespace
 } // mozilla namespace
 
 #endif // mozilla_dom_workers_ServiceWorkerScriptCache_h
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5957,21 +5957,23 @@ WorkerPrivate::AddFeature(JSContext* aCx
     MutexAutoLock lock(mMutex);
 
     if (mStatus >= Canceling) {
       return false;
     }
   }
 
   MOZ_ASSERT(!mFeatures.Contains(aFeature), "Already know about this one!");
+
+  if (mFeatures.IsEmpty() && !ModifyBusyCountFromWorker(aCx, true)) {
+    return false;
+  }
+
   mFeatures.AppendElement(aFeature);
-
-  return mFeatures.Length() == 1 ?
-         ModifyBusyCountFromWorker(aCx, true) :
-         true;
+  return true;
 }
 
 void
 WorkerPrivate::RemoveFeature(JSContext* aCx, WorkerFeature* aFeature)
 {
   AssertIsOnWorkerThread();
 
   MOZ_ASSERT(mFeatures.Contains(aFeature), "Didn't know about this one!");
--- a/editor/libeditor/moz.build
+++ b/editor/libeditor/moz.build
@@ -65,9 +65,11 @@ LOCAL_INCLUDES += [
     '/editor/txmgr',
     '/extensions/spellcheck/src',
     '/layout/generic',
     '/layout/style',
     '/layout/tables',
     '/layout/xul',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
--- a/editor/libeditor/nsEditorEventListener.cpp
+++ b/editor/libeditor/nsEditorEventListener.cpp
@@ -54,16 +54,18 @@
 #include "nsServiceManagerUtils.h"      // for do_GetService
 #include "nsString.h"                   // for nsAutoString
 #include "nsQueryObject.h"              // for do_QueryObject
 #ifdef HANDLE_NATIVE_TEXT_DIRECTION_SWITCH
 #include "nsContentUtils.h"             // for nsContentUtils, etc
 #include "nsIBidiKeyboard.h"            // for nsIBidiKeyboard
 #endif
 
+#include "mozilla/dom/TabParent.h"
+
 class nsPresContext;
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static void
 DoCommandCallback(Command aCommand, void* aData)
 {
@@ -980,16 +982,24 @@ nsEditorEventListener::CanDrop(nsIDOMDra
   nsresult rv = sourceNode->GetOwnerDocument(getter_AddRefs(sourceDoc));
   NS_ENSURE_SUCCESS(rv, false);
 
   // If the source and the dest are not same document, allow to drop it always.
   if (domdoc != sourceDoc) {
     return true;
   }
 
+  // If the source node is a remote browser, treat this as coming from a
+  // different document and allow the drop.
+  nsCOMPtr<nsIContent> sourceContent = do_QueryInterface(sourceNode);
+  TabParent* tp = TabParent::GetFrom(sourceContent);
+  if (tp) {
+    return true;
+  }
+
   nsRefPtr<Selection> selection = mEditor->GetSelection();
   if (!selection) {
     return false;
   }
 
   // If selection is collapsed, allow to drop it always.
   if (selection->Collapsed()) {
     return true;
deleted file mode 100644
index ec288075e2874b7f8371930b7fdc8c4071fce40c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/embedding/tests/winEmbed/WebBrowserChrome.cpp
+++ /dev/null
@@ -1,591 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: Mozilla-sample-code 1.0
- *
- * Copyright (c) 2002 Netscape Communications Corporation and
- * other contributors
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this Mozilla sample software and associated documentation files
- * (the "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to permit
- * persons to whom the Software is furnished to do so, subject to the
- * following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * 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.
- *
- * Contributor(s):
- *
- * ***** END LICENSE BLOCK ***** */
-
-// Local includes
-#include "resource.h"
-#include "winEmbed.h"
-#include "WebBrowserChrome.h"
-
-// OS headers
-#include <stdio.h>
-
-// Frozen APIs
-
-#include "nsStringAPI.h"
-#include "nsIComponentManager.h"
-#include "nsIDOMWindow.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIRequest.h"
-#include "nsIURI.h"
-#include "nsIWebProgress.h"
-#include "nsCWebBrowser.h"
-
-// Glue APIs (not frozen, but safe to use because they are statically linked)
-#include "nsComponentManagerUtils.h"
-
-// NON-FROZEN APIS!
-#include "nsIWebNavigation.h"
-
-WebBrowserChrome::WebBrowserChrome()
-{
-    mNativeWindow = nullptr;
-    mSizeSet = false;
-}
-
-WebBrowserChrome::~WebBrowserChrome()
-{
-    WebBrowserChromeUI::Destroyed(this);
-}
-
-nsresult WebBrowserChrome::CreateBrowser(int32_t aX, int32_t aY,
-                                         int32_t aCX, int32_t aCY,
-                                         nsIWebBrowser **aBrowser)
-{
-    NS_ENSURE_ARG_POINTER(aBrowser);
-    *aBrowser = nullptr;
-
-    mWebBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
-    
-    if (!mWebBrowser)
-        return NS_ERROR_FAILURE;
-
-    (void)mWebBrowser->SetContainerWindow(static_cast<nsIWebBrowserChrome*>(this));
-
-    nsCOMPtr<nsIBaseWindow> browserBaseWindow = do_QueryInterface(mWebBrowser);
-
-    mNativeWindow = WebBrowserChromeUI::CreateNativeWindow(static_cast<nsIWebBrowserChrome*>(this));
-
-    if (!mNativeWindow)
-        return NS_ERROR_FAILURE;
-
-    browserBaseWindow->InitWindow( mNativeWindow,
-                             nullptr, 
-                             aX, aY, aCX, aCY);
-    browserBaseWindow->Create();
-
-    nsCOMPtr<nsIWebProgressListener> listener(static_cast<nsIWebProgressListener*>(this));
-    nsCOMPtr<nsIWeakReference> thisListener(do_GetWeakReference(listener));
-    (void)mWebBrowser->AddWebBrowserListener(thisListener, 
-        NS_GET_IID(nsIWebProgressListener));
-
-    // The window has been created. Now register for history notifications
-    mWebBrowser->AddWebBrowserListener(thisListener, NS_GET_IID(nsISHistoryListener));
-
-    if (mWebBrowser)
-    {
-      *aBrowser = mWebBrowser;
-      NS_ADDREF(*aBrowser);
-      return NS_OK;
-    }
-    return NS_ERROR_FAILURE;
-}
-
-//*****************************************************************************
-// WebBrowserChrome::nsISupports
-//*****************************************************************************   
-
-NS_IMPL_ADDREF(WebBrowserChrome)
-NS_IMPL_RELEASE(WebBrowserChrome)
-
-NS_INTERFACE_MAP_BEGIN(WebBrowserChrome)
-   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
-   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
-   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
-   NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
-   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener) // optional
-   NS_INTERFACE_MAP_ENTRY(nsISHistoryListener)
-   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
-   NS_INTERFACE_MAP_ENTRY(nsIObserver)
-   NS_INTERFACE_MAP_ENTRY(nsIContextMenuListener)
-   NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
-NS_INTERFACE_MAP_END
-
-//*****************************************************************************
-// WebBrowserChrome::nsIInterfaceRequestor
-//*****************************************************************************   
-
-NS_IMETHODIMP WebBrowserChrome::GetInterface(const nsIID &aIID, void** aInstancePtr)
-{
-    NS_ENSURE_ARG_POINTER(aInstancePtr);
-
-    *aInstancePtr = 0;
-    if (aIID.Equals(NS_GET_IID(nsIDOMWindow)))
-    {
-        if (mWebBrowser)
-        {
-            return mWebBrowser->GetContentDOMWindow((nsIDOMWindow **) aInstancePtr);
-        }
-        return NS_ERROR_NOT_INITIALIZED;
-    }
-    return QueryInterface(aIID, aInstancePtr);
-}
-
-//*****************************************************************************
-// WebBrowserChrome::nsIWebBrowserChrome
-//*****************************************************************************   
-
-NS_IMETHODIMP WebBrowserChrome::SetStatus(uint32_t aType, const char16_t* aStatus)
-{
-    WebBrowserChromeUI::UpdateStatusBarText(this, aStatus);
-    return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::GetWebBrowser(nsIWebBrowser** aWebBrowser)
-{
-    NS_ENSURE_ARG_POINTER(aWebBrowser);
-    *aWebBrowser = mWebBrowser;
-    NS_IF_ADDREF(*aWebBrowser);
-    return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::SetWebBrowser(nsIWebBrowser* aWebBrowser)
-{
-    mWebBrowser = aWebBrowser;
-    return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::GetChromeFlags(uint32_t* aChromeMask)
-{
-    *aChromeMask = mChromeFlags;
-    return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::SetChromeFlags(uint32_t aChromeMask)
-{
-    mChromeFlags = aChromeMask;
-    return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::DestroyBrowserWindow(void)
-{
-    WebBrowserChromeUI::Destroy(this);
-    return NS_OK;
-}
-
-
-// IN: The desired browser client area dimensions.
-NS_IMETHODIMP WebBrowserChrome::SizeBrowserTo(int32_t aWidth, int32_t aHeight)
-{
-  /* This isn't exactly correct: we're setting the whole window to
-     the size requested for the browser. At time of writing, though,
-     it's fine and useful for winEmbed's purposes. */
-  WebBrowserChromeUI::SizeTo(this, aWidth, aHeight);
-  mSizeSet = true;
-  return NS_OK;
-}
-
-
-NS_IMETHODIMP WebBrowserChrome::ShowAsModal(void)
-{
-  if (mDependentParent)
-    AppCallbacks::EnableChromeWindow(mDependentParent, false);
-
-  mContinueModalLoop = true;
-  AppCallbacks::RunEventLoop(mContinueModalLoop);
-
-  if (mDependentParent)
-    AppCallbacks::EnableChromeWindow(mDependentParent, true);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP WebBrowserChrome::IsWindowModal(bool *_retval)
-{
-    *_retval = false;
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMETHODIMP WebBrowserChrome::ExitModalEventLoop(nsresult aStatus)
-{
-  mContinueModalLoop = false;
-  return NS_OK;
-}
-