Merge m-c to b2g-inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 27 Jan 2015 16:19:48 -0800
changeset 239465 4a09a5431c88456fbf19fa6ca722598ad5123247
parent 239464 68cd3912adbbea2133d6ce4e26e0302c05eeb5c2 (current diff)
parent 239435 b2b10231606bb89fb75d11596abc49a9b6eecbc2 (diff)
child 239466 51e05062748c9217fb5f18f7bdcd60abf560a7b9
push id500
push userjoshua.m.grant@gmail.com
push dateThu, 29 Jan 2015 01:48:36 +0000
reviewersmerge
milestone38.0a1
Merge m-c to b2g-inbound a=merge
--- a/browser/base/content/browser-data-submission-info-bar.js
+++ b/browser/base/content/browser-data-submission-info-bar.js
@@ -21,18 +21,16 @@ let gDataNotificationInfoBar = {
   get _log() {
     let Log = Cu.import("resource://gre/modules/Log.jsm", {}).Log;
     delete this._log;
     return this._log = Log.repository.getLogger("Services.DataReporting.InfoBar");
   },
 
   init: function() {
     window.addEventListener("unload", function onUnload() {
-      window.removeEventListener("unload", onUnload, false);
-
       for (let o of this._OBSERVERS) {
         Services.obs.removeObserver(this, o);
       }
     }.bind(this), false);
 
     for (let o of this._OBSERVERS) {
       Services.obs.addObserver(this, o, true);
     }
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -1007,18 +1007,18 @@ chatbox:-moz-full-screen-ancestor > .cha
 }
 
 /* hide sidebar when fullscreen */
 *:-moz-full-screen-ancestor #social-sidebar-box:not(:-moz-full-screen-ancestor) {
   display: none;
 }
 
 /* Combobox dropdown renderer */
-#ContentSelectDropdown {
-  -moz-binding: url("chrome://global/content/bindings/popup.xml#popup-scrollbars");
+#ContentSelectDropdown > menupopup {
+  max-height: 350px;
 }
 
 .contentSelectDropdown-optgroup {
   font-weight: bold;
   /* color: menutext used to overwrite the disabled color */
   color: menutext;
 }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -141,18 +141,23 @@
     <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for search with one-off buttons -->
     <panel type="autocomplete" id="PopupSearchAutoComplete" noautofocus="true" hidden="true"/>
 
     <!-- for url bar autocomplete -->
     <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
 
-    <!-- for select dropdowns -->
-    <menupopup id="ContentSelectDropdown" rolluponmousewheel="true" hidden="true"/>
+    <!-- for select dropdowns. The menupopup is what shows the list of options,
+         and the popuponly menulist makes things like the menuactive attributes
+         work correctly on the menupopup. ContentSelectDropdown expects the
+         popuponly menulist to be its immediate parent. -->
+    <menulist popuponly="true" id="ContentSelectDropdown" hidden="true">
+      <menupopup rolluponmousewheel="true"/>
+    </menulist>
 
     <!-- for invalid form error message -->
     <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
       <description/>
     </panel>
 
     <panel id="editBookmarkPanel"
            type="arrow"
@@ -1164,17 +1169,17 @@
       <splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" hidden="true"/>
       <vbox id="appcontent" flex="1">
         <notificationbox id="high-priority-global-notificationbox"/>
         <tabbrowser id="content"
                     flex="1" contenttooltip="aHTMLTooltip"
                     tabcontainer="tabbrowser-tabs"
                     contentcontextmenu="contentAreaContextMenu"
                     autocompletepopup="PopupAutoComplete"
-                    selectpopup="ContentSelectDropdown"/>
+                    selectmenulist="ContentSelectDropdown"/>
         <chatbar id="pinnedchats" layer="true" mousethrough="always" hidden="true"/>
       </vbox>
       <splitter id="social-sidebar-splitter"
                 class="chromeclass-extrachrome sidebar-splitter"
                 observes="socialSidebarBroadcaster"/>
       <vbox id="social-sidebar-box"
             class="chromeclass-extrachrome"
             observes="socialSidebarBroadcaster"
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -25,17 +25,17 @@
                   flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
                   onselect="if (event.target.localName == 'tabpanels') this.parentNode.updateCurrentBrowser();">
         <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
           <xul:notificationbox flex="1">
             <xul:hbox flex="1" class="browserSidebarContainer">
               <xul:vbox flex="1" class="browserContainer">
                 <xul:stack flex="1" class="browserStack" anonid="browserStack">
                   <xul:browser anonid="initialBrowser" type="content-primary" message="true" messagemanagergroup="browsers"
-                               xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectpopup"/>
+                               xbl:inherits="tooltip=contenttooltip,contextmenu=contentcontextmenu,autocompletepopup,selectmenulist"/>
                 </xul:stack>
               </xul:vbox>
             </xul:hbox>
           </xul:notificationbox>
         </xul:tabpanels>
       </xul:tabbox>
       <children/>
     </content>
@@ -1595,18 +1595,18 @@
 
             if (window.gShowPageResizers && window.windowState == window.STATE_NORMAL) {
               b.setAttribute("showresizer", "true");
             }
 
             if (!isPreloadBrowser && this.hasAttribute("autocompletepopup"))
               b.setAttribute("autocompletepopup", this.getAttribute("autocompletepopup"));
 
-            if (this.hasAttribute("selectpopup"))
-              b.setAttribute("selectpopup", this.getAttribute("selectpopup"));
+            if (this.hasAttribute("selectmenulist"))
+              b.setAttribute("selectmenulist", this.getAttribute("selectmenulist"));
 
             b.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
 
             // Create the browserStack container
             var stack = document.createElementNS(NS_XUL, "stack");
             stack.className = "browserStack";
             stack.appendChild(b);
             stack.setAttribute("flex", "1");
--- a/browser/components/loop/content/shared/js/mixins.js
+++ b/browser/components/loop/content/shared/js/mixins.js
@@ -198,17 +198,16 @@ loop.shared.mixins = (function() {
       // @see https://bugzilla.mozilla.org/show_bug.cgi?id=1020445
       return {
         insertMode: "append",
         width: "100%",
         height: "100%",
         publishVideo: options.publishVideo,
         style: {
           audioLevelDisplayMode: "off",
-          bugDisplayMode: "off",
           buttonDisplayMode: "off",
           nameDisplayMode: "off",
           videoDisabledDisplayMode: "off"
         }
       };
     },
 
     /**
--- a/browser/components/loop/content/shared/libs/sdk-content/css/ot.css
+++ b/browser/components/loop/content/shared/libs/sdk-content/css/ot.css
@@ -113,226 +113,98 @@
     border: 0 none;
 }
 
 
 /* Modal dialog styles */
 
 /* Modal dialog styles */
 
+.OT_dialog-centering {
+  display: table;
+  width: 100%;
+  height: 100%;
+}
+
+.OT_dialog-centering-child {
+  display: table-cell;
+  vertical-align: middle;
+}
+
 .OT_dialog {
-  border: none;
-  border-radius: 0;
-  top: 50%;
-  left: 50%;
-  position: absolute;
-  position: fixed;
-  padding: 0;
+  position: relative;
+
+  box-sizing: border-box;
+  max-width: 576px;
+  margin-right: auto;
+  margin-left: auto;
+  padding: 36px;
+  text-align: center; /* centers all the inline content */
+
   background-color: #363636;
   color: #fff;
-  z-index: 9999;
   box-shadow: 2px 4px 6px #999;
   font-family: 'Didact Gothic', sans-serif;
-}
-
-.OT_dialog-blackout {
-  position: absolute;
-  position: fixed;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  background-color: #363636;
-}
-
-.OT_dialog-blackout .OT_dialog {
-  box-shadow: 0 0 0 transparent;
+  font-size: 13px;
+  line-height: 1.4;
 }
 
 .OT_dialog * {
-  font-family: 'Didact Gothic', sans-serif;
-}
-
-.OT_dialog-plugin-prompt {
-  margin-left: -350px;
-  margin-top: -127px;
-  width: 650px;
-  height: 254px;
-}
-
-.OT_dialog-plugin-reinstall {
-  margin-left: -271px;
-  margin-top: -107px;
-  width: 542px;
-  height: 214px;
-}
-
-.OT_dialog-plugin-upgrading {
-  margin-left: -267px;
-  margin-top: -94px;
-  width: 514px;
-  height: 188px;
-}
-
-.OT_dialog-plugin-upgraded {
-  margin-left: -300px;
-  margin-top: -100px;
-  width: 600px;
-  height: 200px;
-}
-
-.OT_dialog-allow-deny-chrome-first {
-  margin-left: -227px;
-  margin-top: -122px;
-  width: 453px;
-  height: 244px;
-}
-
-.OT_dialog-allow-deny-chrome-pre-denied {
-  margin-left: -263px;
-  margin-top: -135px;
-  width: 526px;
-  height: 270px;
-}
-
-.OT_dialog-allow-deny-chrome-now-denied {
-  margin-left: -120px;
-  margin-top: -85px;
-  width: 256px;
-  height: 170px;
-}
-
-.OT_dialog-allow-deny-firefox-denied {
-  margin-left: -160px;
-  margin-top: -105px;
-  width: 320px;
-  height: 190px;
-}
-
-.OT_dialog-allow-deny-firefox-maybe-denied {
-  margin-left: -281px;
-  margin-top: -126px;
-  width: 562px;
-  height: 252px;
-}
-
-.OT_dialog-allow-highlight-chrome {
-  display: inline-block;
-  margin-top: 20px;
-  width: 227px;
-  height: 94px;
-  background-image: url(../images/rtc/access-prompt-chrome.png);
+  font-family: inherit;
+  box-sizing: inherit;
 }
 
 .OT_closeButton {
   color: #999999;
   cursor: pointer;
   font-size: 32px;
-  line-height: 30px;
+  line-height: 36px;
   position: absolute;
-  right: 15px;
+  right: 18px;
   top: 0;
 }
 
 .OT_dialog-messages {
-  position: absolute;
-  top: 32px;
-  left: 32px;
-  right: 32px;
   text-align: center;
 }
 
-.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-messages {
-  top: 45px;
-}
-
 
 .OT_dialog-messages-main {
+  margin-bottom: 36px;
+  line-height: 36px;
+
   font-weight: 300;
-  font-size: 18pt;
-  line-height: 24px;
+  font-size: 24px;
 }
 
 .OT_dialog-messages-minor {
-  font-weight: 300;
-  margin-top: 12px;
+  margin-bottom: 18px;
+
   font-size: 13px;
   line-height: 18px;
   color: #A4A4A4;
 }
 
-.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-messages-minor {
-  margin-top: 4px;
-}
-
 .OT_dialog-messages-minor strong {
-  font-weight: 300;
   color: #ffffff;
 }
 
-.OT_dialog-hidden {
-  display: none;
-}
-
-.OT_dialog-single-button {
-  position: absolute;
-  bottom: 41px;
-  left: 50%;
-  margin-left: -97px;
-  height: 47px;
-  width: 193px;
-}
-
-
-.OT_dialog-single-button-wide {
-    bottom: 35px;
-    height: 140px;
-    left: 5px;
-    position: absolute;
-    right: 0;
-}
-  .OT_dialog-single-button-with-title {
-      margin: 0 auto;
-      padding-left: 30px;
-      padding-right: 30px;
-      width: 270px;
-  }
-
-
-.OT_dialog-button-pair {
-  position: absolute;
-  bottom: 45px;
-  left: 5px;
-  right: 0;
-  height: 94px;
-}
-
-.OT_dialog-button-with-title {
-  padding-left: 30px;
-  padding-right: 30px;
-  width: 260px;
-  float: left;
-}
-
-.OT_dialog-button-pair-seperator {
-  border-right: 1px solid #555555;
-  height: 112px;
-  width: 1px;
-  float: left;
+.OT_dialog-actions-card {
+  display: inline-block;
 }
 
 .OT_dialog-button-title {
+  margin-bottom: 18px;
+  line-height: 18px;
+
   font-weight: 300;
   text-align: center;
-  margin-bottom: 15px;
   font-size: 14px;
-  line-height: 150%;
   color: #999999;
 }
-
 .OT_dialog-button-title label {
   color: #999999;
 }
 
 .OT_dialog-button-title a,
 .OT_dialog-button-title a:link,
 .OT_dialog-button-title a:active {
   color: #02A1DE;
@@ -340,167 +212,96 @@
 
 .OT_dialog-button-title strong {
   color: #ffffff;
   font-weight: 100;
   display: block;
 }
 
 .OT_dialog-button {
-  font-weight: 100;
-  display: block;
-  line-height: 50px;
-  height: 47px;
+  display: inline-block;
+
+  margin-bottom: 18px;
+  padding: 0 1em;
+
   background-color: #1CA3DC;
   text-align: center;
-  font-size: 16pt;
   cursor: pointer;
 }
 
 .OT_dialog-button.OT_dialog-button-disabled {
   cursor: not-allowed;
 
   /* IE 8 */
   -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
 
   opacity: 0.5;
 }
 
-.OT_dialog-button.OT_dialog-button-large {
-  line-height: 60px;
-  height: 58px;
+.OT_dialog-button-large {
+  line-height: 36px;
+  padding-top: 9px;
+  padding-bottom: 9px;
+
+  font-weight: 100;
+  font-size: 24px;
 }
 
-.OT_dialog-button.OT_dialog-button-small {
+.OT_dialog-button-small {
+  line-height: 18px;
+  padding-top: 9px;
+  padding-bottom: 9px;
+
   background-color: #444444;
   color: #999999;
-  font-size: 12pt;
-  height: 40px;
-  line-height: 40px;
-  margin: 20px auto 0 auto;
-  width: 86px;
+  font-size: 16px;
 }
 
 .OT_dialog-progress-bar {
+  display: inline-block; /* prevents margin collapse */
+  width: 100%;
+  margin-top: 5px;
+  margin-bottom: 41px;
+
   border: 1px solid #4E4E4E;
   height: 8px;
 }
 
 .OT_dialog-progress-bar-fill {
+  height: 100%;
+
   background-color: #29A4DA;
-  height: 10px;
-  margin-top: -1px;
-  margin-left: -1px;
-  margin-right: -1px;
 }
 
 .OT_dialog-plugin-upgrading .OT_dialog-plugin-upgrade-percentage {
-  font-size: 36pt;
+  line-height: 54px;
+
+  font-size: 48px;
   font-weight: 100;
 }
 
-.OT_dialog-plugin-upgrading .OT_dialog-progress-bar {
-  margin-top: 25px;
-  margin-bottom: 25px;
-}
-
-.OT_dialog-3steps {
-  margin-top: 24px;
-}
-
-.OT_dialog-allow-deny-firefox-maybe-denied .OT_dialog-3steps {
-  margin-top: 21px;
-}
-
-.OT_dialog-3steps-step {
-  float: left;
-  -moz-box-sizing: border-box;
-       box-sizing: border-box;
-  width: 33%;
-  height: 140px;
-  padding: 0 10px;
-  color: #A4A4A4;
-}
+/* Helpers */
 
-.OT_dialog-3steps-seperator {
-  float: left;
-  -moz-box-sizing: border-box;
-       box-sizing: border-box;
-  margin-top: 10px;
-  width: 1px;
-  height: 68px;
-  background-color: #555555;
-}
-
-.OT_dialog-allow-deny-chrome-pre-denied .OT_dialog-3steps-seperator {
-  margin-top: 16px;
-}
-
-.OT_dialog-3steps-step-num {
-  font-size: 20px;
-  background-color: #2AA3D8;
-  border-radius: 20px;
-  line-height: 33px;
-  height: 33px;
-  width: 33px;
-  margin: 0 auto 17px;
+.OT_centered {
+  position: fixed;
+  left: 50%;
+  top: 50%;
+  margin: 0;
 }
 
-.OT_dialog-allow-deny-chrome-pre-denied .OT_dialog-3steps-step-num {
-  margin-bottom: 10px;
-}
-
-.OT_dialog-allow-camera-icon {
-  background-color: #000;
-  width: 113px;
-  height: 48px;
-  margin: 10px auto 0;
-  background-image: url(../images/rtc/access-predenied-chrome.png);
-}
-
-/* Publisher Deny Helpers */
-
-.OT_publisher-denied-firefox {
-  background-color: #fff;
+.OT_dialog-hidden {
+  display: none;
 }
 
-.OT_publisher-denied-firefox p {
-  width: 232px;
-  height: 103px;
-  top: 50%;
-  left: 50%;
+.OT_dialog-button-block {
   display: block;
-  position: absolute;
-  margin-top: -52px;
-  margin-left: -116px;
-  background-image: url(../images/rtc/access-denied-firefox.png);
-  background-position: 50% 0;
-  background-repeat: no-repeat;
 }
 
-.OT_publisher-denied-firefox span {
-  display: block;
-  position: absolute;
-  bottom: 0;
-  right: 0;
-  left: 0;
-  margin: 0 auto;
-  width: 232px;
-  height: 31px;
-  background-image: url(../images/rtc/access-denied-copy-firefox.png);
-  text-indent: 100%;
-  white-space: nowrap;
-  overflow: hidden;
-}
-
-.OT_centered {
-	position: fixed;
-	left: 50%;
-	top: 50%;
-	margin: 0;
+.OT_dialog-no-natural-margin {
+  margin-bottom: 0;
 }
 
 /* Publisher and Subscriber styles */
 
 .OT_publisher, .OT_subscriber {
     position: relative;
     min-width: 48px;
     min-height: 48px;
@@ -798,56 +599,34 @@
 .OT_mini .OT_name.OT_mode-on,
 .OT_mini .OT_name.OT_mode-auto,
 .OT_mini:hover .OT_name.OT_mode-auto {
     display: none;
 }
 
 .OT_publisher .OT_name,
 .OT_subscriber .OT_name {
-    left: 24px;
+    left: 10px;
     right: 37px;
     height: 34px;
-}
-
-.OT_publisher .OT_name-no-bug,
-.OT_subscriber .OT_name-no-bug {
-    left: 10px;
     padding-left: 0;
 }
 
 .OT_publisher .OT_mute,
-.OT_subscriber .OT_mute,
-.OT_publisher .OT_opentok,
-.OT_subscriber .OT_opentok {
+.OT_subscriber .OT_mute {
     border: none;
     cursor: pointer;
     display: block;
     position: absolute;
     text-align: center;
     text-indent: -9999em;
     background-color: transparent;
     background-repeat: no-repeat;
 }
 
-.OT_publisher .OT_opentok,
-.OT_subscriber .OT_opentok {
-    background: url(../images/rtc/buttons.png) 0 -32px no-repeat;
-    cursor: default;
-    height: 18px;
-    left: 8px;
-    line-height: 18px;
-    top: 8px;
-    width: 16px;
-}
-
-.OT_micro .OT_opentok {
-    display: none !important;
-}
-
 .OT_publisher .OT_mute,
 .OT_subscriber .OT_mute {
     right: 0;
     top: 0;
     border-left: 1px solid rgba(255, 255, 255, 0.2);
     height: 36px;
     width: 37px;
 }
@@ -878,31 +657,16 @@
     background-position: 8px 7px;
 }
 
 .OT_subscriber .OT_mute.OT_active {
     background-image: url(../images/rtc/speaker-off.png);
     background-position: 7px 7px;
 }
 
-/* Disabling this for now - see https://jira.tokbox.com/browse/OPENTOK-8870
-.OT_publisher .OT_opentok:hover:after,
-.OT_subscriber .OT_opentok:hover:after {
-    content: 'tokbox';
-    color: #fff;
-    font-weight: bold;
-    font-size: 14px;
-    letter-spacing: -1px;
-    top: 20px;
-    opacity: 0.5;
-    position: absolute;
-    text-indent: 0;
-    top: 0;
-}*/
-
 /**
  * Styles for display modes
  *
  * Note: It's important that these completely control the display and opacity
  * attributes, no other selectors should atempt to change them.
  */
 
 /* Default display mode transitions for various chrome elements */
@@ -970,33 +734,16 @@
 .OT_subscriber .OT_edge-bar-item.OT_edge-bottom.OT_mode-on,
 .OT_publisher:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto,
 .OT_subscriber:hover .OT_edge-bar-item.OT_edge-bottom.OT_mode-auto {
     top: auto;
     bottom: 0;
     opacity: 1;
 }
 
-.OT_publisher .OT_opentok.OT_mode-off,
-.OT_publisher .OT_opentok.OT_mode-auto,
-.OT_subscriber .OT_opentok.OT_mode-off,
-.OT_subscriber .OT_opentok.OT_mode-auto  {
-    top: -17px;
-}
-
-.OT_publisher .OT_opentok.OT_mode-on,
-.OT_publisher .OT_opentok.OT_mode-auto.OT_mode-on-hold,
-.OT_publisher:hover .OT_opentok.OT_mode-auto,
-.OT_subscriber .OT_opentok.OT_mode-on,
-.OT_subscriber .OT_opentok.OT_mode-auto.OT_mode-on-hold,
-.OT_subscriber:hover .OT_opentok.OT_mode-auto {
-    top: 8px;
-}
-
-
 /* Contains the video element, used to fix video letter-boxing */
 .OT_video-container {
     position: absolute;
     background-color: #000000;
     overflow: hidden;
 }
 
 .OT_hidden-audio {
@@ -1090,17 +837,17 @@
 }
 
 .OT_audio-level-meter__value {
     position: absolute;
     border-radius: 50%;
     background-image: radial-gradient(circle, rgba(151,206,0,1) 0%, rgba(151,206,0,0) 100%);
 }
 
-.OT_audio-level-meter {
+.OT_audio-level-meter.OT_mode-off {
     display: none;
 }
 
 .OT_audio-level-meter.OT_mode-on,
 .OT_audio-only .OT_audio-level-meter.OT_mode-auto {
     display: block;
 }
 
--- a/browser/components/loop/content/shared/libs/sdk.js
+++ b/browser/components/loop/content/shared/libs/sdk.js
@@ -1,25 +1,25 @@
 /**
- * @license  OpenTok JavaScript Library v2.2.9.1
+ * @license  OpenTok JavaScript Library v2.2.9.7
  * http://www.tokbox.com/
  *
  * Copyright (c) 2014 TokBox, Inc.
  * Released under the MIT license
  * http://opensource.org/licenses/MIT
  *
- * Date: September 08 10:17:05 2014
+ * Date: January 26 03:18:02 2015
  */
 
 (function(window) {
   if (!window.OT) window.OT = {};
 
   OT.properties = {
-    version: 'v2.2.9.1',         // The current version (eg. v2.0.4) (This is replaced by gradle)
-    build: '72b534e',    // The current build hash (This is replaced by gradle)
+    version: 'v2.2.9.7',         // The current version (eg. v2.0.4) (This is replaced by gradle)
+    build: '59e99bc',    // The current build hash (This is replaced by gradle)
 
     // Whether or not to turn on debug logging by default
     debug: 'false',
     // The URL of the tokbox website
     websiteURL: 'http://www.tokbox.com',
 
     // The URL of the CDN
     cdnURL: 'http://static.opentok.com',
@@ -3098,16 +3098,24 @@
   * @memberof OT
   * @function
   * @see <a href="#setLogLevel">OT.setLogLevel()</a>
   */
 
 })(window);
 !(function() {
 
+  var adjustModal = function(callback) {
+    return function setFullHeightDocument(window, document) {
+      // required in IE8
+      document.querySelector('html').style.height = document.body.style.height = '100%';
+      callback(window, document);
+    };
+  };
+
   var addCss = function(document, url, callback) {
     var head = document.head || document.getElementsByTagName('head')[0];
     var cssTag = OT.$.createElement('link', {
       type: 'text/css',
       media: 'screen',
       rel: 'stylesheet',
       href: url
     });
@@ -3162,213 +3170,20 @@
   var linkElement = function(children, href, classes) {
     var link = templateElement.call(this, classes || '', children, 'a');
     link.setAttribute('href', href);
     return link;
   };
 
   OT.Dialogs = {};
 
-  OT.Dialogs.AllowDeny = {
-    Chrome: {},
-    Firefox: {}
-  };
-
-  OT.Dialogs.AllowDeny.Chrome.initialPrompt = function() {
-    var modal = new OT.$.Modal(function(window, document) {
-
-      var el = OT.$.bind(templateElement, document),
-          close, root;
-
-      close = el('OT_closeButton', '&times;')
-        .on('click', function() {
-          modal.trigger('closeButtonClicked');
-          modal.close();
-        });
-
-      root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-first', [
-        close,
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', 'Allow camera and mic access'),
-          el('OT_dialog-messages-minor', 'Click the Allow button in the upper-right corner ' +
-            'of your browser to enable real-time communication.'),
-          el('OT_dialog-allow-highlight-chrome')
-        ])
-      ]);
-
-      addDialogCSS(document, [], function() {
-        document.body.appendChild(root);
-      });
-
-    });
-    return modal;
-  };
-
-  OT.Dialogs.AllowDeny.Chrome.previouslyDenied = function(website) {
-    var modal = new OT.$.Modal(function(window, document) {
-
-      var el = OT.$.bind(templateElement, document),
-          close,
-          root;
-
-      close = el('OT_closeButton', '&times;')
-        .on('click', function() {
-          modal.trigger('closeButtonClicked');
-          modal.close();
-        });
-
-      root = el('OT_root OT_dialog OT_dialog-allow-deny-chrome-pre-denied', [
-        close,
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', 'Allow camera and mic access'),
-          el('OT_dialog-messages-minor', [
-            'To interact with this app, follow these 3 steps:',
-            el('OT_dialog-3steps', [
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '1'),
-                'Find this icon in the URL bar and click it',
-                el('OT_dialog-allow-camera-icon')
-              ]),
-              el('OT_dialog-3steps-seperator'),
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '2'),
-                'Select "Ask if ' + website + ' wants to access your camera and mic" ' +
-                  'and then click Done.'
-              ]),
-              el('OT_dialog-3steps-seperator'),
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '3'),
-                'Refresh your browser.'
-              ])
-            ])
-          ])
-        ])
-      ]);
-
-      addDialogCSS(document, [], function() {
-        document.body.appendChild(root);
-      });
-
-    });
-    return modal;
-  };
-
-  OT.Dialogs.AllowDeny.Chrome.deniedNow = function() {
-    var modal = new OT.$.Modal(function(window, document) {
-
-      var el = OT.$.bind(templateElement, document),
-          root;
-
-      root = el('OT_root OT_dialog-blackout',
-        el('OT_dialog OT_dialog-allow-deny-chrome-now-denied', [
-          el('OT_dialog-messages', [
-            el('OT_dialog-messages-main ',
-              el('OT_dialog-allow-camera-icon')
-            ),
-            el('OT_dialog-messages-minor',
-              'Find & click this icon to allow camera and mic access.'
-            )
-          ])
-        ])
-      );
-
-      addDialogCSS(document, [], function() {
-        document.body.appendChild(root);
-      });
-
-    });
-    return modal;
-  };
-
-  OT.Dialogs.AllowDeny.Firefox.maybeDenied = function() {
-    var modal = new OT.$.Modal(function(window, document) {
-
-      var el = OT.$.bind(templateElement, document),
-          close,
-          root;
-
-      close = el('OT_closeButton', '&times;')
-        .on('click', function() {
-          modal.trigger('closeButtonClicked');
-          modal.close();
-        });
-
-      root = el('OT_root OT_dialog OT_dialog-allow-deny-firefox-maybe-denied', [
-        close,
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', 'Please allow camera & mic access'),
-          el('OT_dialog-messages-minor', [
-            'To interact with this app, follow these 3 steps:',
-            el('OT_dialog-3steps', [
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '1'),
-                'Reload the page, or click the camera icon ' +
-                  'in the browser URL bar.'
-              ]),
-              el('OT_dialog-3steps-seperator'),
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '2'),
-                'In the menu, select your camera & mic.'
-              ]),
-              el('OT_dialog-3steps-seperator'),
-              el('OT_dialog-3steps-step', [
-                el('OT_dialog-3steps-step-num', '3'),
-                'Click "Share Selected Devices."'
-              ])
-            ])
-          ])
-        ])
-      ]);
-
-      addDialogCSS(document, [], function() {
-        document.body.appendChild(root);
-      });
-
-    });
-    return modal;
-  };
-
-  OT.Dialogs.AllowDeny.Firefox.denied = function() {
-    var modal = new OT.$.Modal(function(window, document) {
-
-      var el = OT.$.bind(templateElement, document),
-          btn = OT.$.bind(templateElement, document, 'OT_dialog-button OT_dialog-button-large'),
-          root,
-          refreshButton;
-
-      refreshButton = btn('Reload')
-        .on('click', function() {
-          modal.trigger('refresh');
-        });
-
-      root = el('OT_root OT_dialog-blackout',
-        el('OT_dialog OT_dialog-allow-deny-firefox-denied', [
-          el('OT_dialog-messages', [
-            el('OT_dialog-messages-minor',
-              'Access to camera and microphone has been denied. ' +
-              'Click the button to reload page.'
-            )
-          ]),
-          el('OT_dialog-single-button', refreshButton)
-        ])
-      );
-
-      addDialogCSS(document, [], function() {
-        document.body.appendChild(root);
-      });
-
-    });
-
-    return modal;
-  };
-
   OT.Dialogs.Plugin = {};
 
   OT.Dialogs.Plugin.promptToInstall = function() {
-    var modal = new OT.$.Modal(function(window, document) {
+    var modal = new OT.$.Modal(adjustModal(function(window, document) {
 
       var el = OT.$.bind(templateElement, document),
           btn = function(children, size) {
             var classes = 'OT_dialog-button ' +
                           (size ? 'OT_dialog-button-' + size : 'OT_dialog-button-large'),
                 b = el(classes, children);
 
             b.enable = function() {
@@ -3386,22 +3201,25 @@
           downloadButton = btn('Download plugin'),
           cancelButton = btn('cancel', 'small'),
           refreshButton = btn('Refresh browser'),
           acceptEULA,
           checkbox,
           close,
           root;
 
+      OT.$.addClass(cancelButton, 'OT_dialog-no-natural-margin OT_dialog-button-block');
+      OT.$.addClass(refreshButton, 'OT_dialog-no-natural-margin');
+
       function onDownload() {
         modal.trigger('download');
         setTimeout(function() {
           root.querySelector('.OT_dialog-messages-main').innerHTML =
                                               'Plugin installation successful';
-          var sections = root.querySelectorAll('.OT_dialog-single-button-wide');
+          var sections = root.querySelectorAll('.OT_dialog-section');
           OT.$.addClass(sections[0], 'OT_dialog-hidden');
           OT.$.removeClass(sections[1], 'OT_dialog-hidden');
         }, 3000);
       }
 
       function onRefresh() {
         modal.trigger('refresh');
       }
@@ -3446,127 +3264,143 @@
         });
 
       acceptEULA = linkElement.call(document,
                                     'end-user license agreement',
                                     'http://tokbox.com/support/ie-eula');
 
       checkbox = checkBoxElement.call(document, null, 'acceptEULA', onToggleEULA);
 
-      root = el('OT_root OT_dialog OT_dialog-plugin-prompt', [
-        close,
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', 'This app requires real-time communication')
-        ]),
-        el('OT_dialog-single-button-wide', [
-          el('OT_dialog-single-button-with-title', [
-            el('OT_dialog-button-title', [
-              checkbox,
-              (function() {
-                var x = el('', 'accept', 'label');
-                x.setAttribute('for', checkbox.id);
-                x.style.margin = '0 5px';
-                return x;
-              })(),
-              acceptEULA
+      root = el('OT_dialog-centering', [
+        el('OT_dialog-centering-child', [
+          el('OT_root OT_dialog OT_dialog-plugin-prompt', [
+            close,
+            el('OT_dialog-messages', [
+              el('OT_dialog-messages-main', 'This app requires real-time communication')
             ]),
-            downloadButton,
-            cancelButton
-          ])
-        ]),
-        el('OT_dialog-single-button-wide OT_dialog-hidden', [
-          el('OT_dialog-single-button-with-title', [
-            el('OT_dialog-button-title', [
-              'You can now enjoy webRTC enabled video via Internet Explorer.'
+            el('OT_dialog-section', [
+              el('OT_dialog-single-button-with-title', [
+                el('OT_dialog-button-title', [
+                  checkbox,
+                  (function() {
+                    var x = el('', 'accept', 'label');
+                    x.setAttribute('for', checkbox.id);
+                    x.style.margin = '0 5px';
+                    return x;
+                  })(),
+                  acceptEULA
+                ]),
+                el('OT_dialog-actions-card', [
+                  downloadButton,
+                  cancelButton
+                ])
+              ])
             ]),
-            refreshButton
+            el('OT_dialog-section OT_dialog-hidden', [
+              el('OT_dialog-button-title', [
+                'You can now enjoy webRTC enabled video via Internet Explorer.'
+              ]),
+              refreshButton
+            ])
           ])
         ])
       ]);
 
       addDialogCSS(document, [], function() {
         document.body.appendChild(root);
       });
 
-    });
+    }));
     return modal;
   };
 
   OT.Dialogs.Plugin.promptToReinstall = function() {
-    var modal = new OT.$.Modal(function(window, document) {
+    var modal = new OT.$.Modal(adjustModal(function(window, document) {
 
       var el = OT.$.bind(templateElement, document),
           close,
           okayButton,
           root;
 
       close = el('OT_closeButton', '&times;');
-      okayButton = el('OT_dialog-button', 'Okay');
+      okayButton =
+        el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Okay');
 
       OT.$.on(okayButton, 'click', function() {
         modal.trigger('okay');
       });
 
       OT.$.on(close, 'click', function() {
         modal.trigger('closeButtonClicked');
         modal.close();
       });
 
-      root = el('OT_ROOT OT_dialog OT_dialog-plugin-reinstall', [
-        close,
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', 'Reinstall Opentok Plugin'),
-          el('OT_dialog-messages-minor', 'Uh oh! Try reinstalling the OpenTok plugin again to ' +
-            'enable real-time video communication for Internet Explorer.')
-        ]),
-        el('OT_dialog-single-button', okayButton)
+      root = el('OT_dialog-centering', [
+        el('OT_dialog-centering-child', [
+          el('OT_ROOT OT_dialog OT_dialog-plugin-reinstall', [
+            close,
+            el('OT_dialog-messages', [
+              el('OT_dialog-messages-main', 'Reinstall Opentok Plugin'),
+              el('OT_dialog-messages-minor', 'Uh oh! Try reinstalling the OpenTok plugin ' +
+                'again to enable real-time video communication for Internet Explorer.')
+            ]),
+            el('OT_dialog-section', [
+              el('OT_dialog-single-button', okayButton)
+            ])
+          ])
+        ])
       ]);
 
       addDialogCSS(document, [], function() {
         document.body.appendChild(root);
       });
 
-    });
+    }));
 
     return modal;
   };
 
   OT.Dialogs.Plugin.updateInProgress = function() {
 
     var progressBar,
         progressText,
         progressValue = 0;
 
-    var modal = new OT.$.Modal(function(window, document) {
+    var modal = new OT.$.Modal(adjustModal(function(window, document) {
 
       var el = OT.$.bind(templateElement, document),
           root;
 
       progressText = el('OT_dialog-plugin-upgrade-percentage', '0%', 'strong');
 
       progressBar = el('OT_dialog-progress-bar-fill');
 
-      root = el('OT_ROOT OT_dialog OT_dialog-plugin-upgrading', [
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', [
-            'One moment please... ',
-            progressText
-          ]),
-          el('OT_dialog-progress-bar', progressBar),
-          el('OT_dialog-messages-minor', 'Please wait while the OpenTok plugin is updated')
+      root = el('OT_dialog-centering', [
+        el('OT_dialog-centering-child', [
+          el('OT_ROOT OT_dialog OT_dialog-plugin-upgrading', [
+            el('OT_dialog-messages', [
+              el('OT_dialog-messages-main', [
+                'One moment please... ',
+                progressText
+              ]),
+              el('OT_dialog-progress-bar', progressBar),
+              el('OT_dialog-messages-minor OT_dialog-no-natural-margin',
+                'Please wait while the OpenTok plugin is updated')
+            ])
+          ])
         ])
       ]);
 
       addDialogCSS(document, [], function() {
         document.body.appendChild(root);
         if(progressValue != null) {
           modal.setUpdateProgress(progressValue);
         }
       });
-    });
+    }));
 
     modal.setUpdateProgress = function(newProgress) {
       if(progressBar && progressText) {
         if(newProgress > 99) {
           OT.$.css(progressBar, 'width', '');
           progressText.innerHTML = '100%';
         } else if(newProgress < 1) {
           OT.$.css(progressBar, 'width', '0%');
@@ -3579,48 +3413,54 @@
         progressValue = newProgress;
       }
     };
 
     return modal;
   };
 
   OT.Dialogs.Plugin.updateComplete = function(error) {
-    var modal = new OT.$.Modal(function(window, document) {
+    var modal = new OT.$.Modal(adjustModal(function(window, document) {
       var el = OT.$.bind(templateElement, document),
           reloadButton,
           root;
 
-      reloadButton = el('OT_dialog-button', 'Reload').on('click', function() {
-        modal.trigger('reload');
-      });
+      reloadButton =
+        el('OT_dialog-button OT_dialog-button-large OT_dialog-no-natural-margin', 'Reload')
+          .on('click', function() {
+            modal.trigger('reload');
+          });
 
       var msgs;
 
       if(error) {
         msgs = ['Update Failed.', error + '' || 'NO ERROR'];
       } else {
         msgs = ['Update Complete.',
           'The OpenTok plugin has been succesfully updated. ' +
           'Please reload your browser.'];
       }
 
-      root = el('OT_root OT_dialog OT_dialog-plugin-upgraded', [
-        el('OT_dialog-messages', [
-          el('OT_dialog-messages-main', msgs[0]),
-          el('OT_dialog-messages-minor', msgs[1])
-        ]),
-        el('OT_dialog-single-button', reloadButton)
+      root = el('OT_dialog-centering', [
+        el('OT_dialog-centering-child', [
+          el('OT_root OT_dialog OT_dialog-plugin-upgraded', [
+            el('OT_dialog-messages', [
+              el('OT_dialog-messages-main', msgs[0]),
+              el('OT_dialog-messages-minor', msgs[1])
+            ]),
+            el('OT_dialog-single-button', reloadButton)
+          ])
+        ])
       ]);
 
       addDialogCSS(document, [], function() {
         document.body.appendChild(root);
       });
 
-    });
+    }));
 
     return modal;
 
   };
 
 
 })();
 !(function(window) {
@@ -3806,24 +3646,22 @@
 
     OT.$.eventing(_this);
 
     return _this;
   })();
 
 })(window);
 /**
- * @license  TB Plugin 0.4.0.8 72b534e HEAD
+ * @license  TB Plugin 0.4.0.8 59e99bc HEAD
  * http://www.tokbox.com/
  *
- * Copyright (c) 2014 TokBox, Inc.
- * Released under the MIT license
- * http://opensource.org/licenses/MIT
- *
- * Date: September 08 10:17:49 2014
+ * Copyright (c) 2015 TokBox, Inc.
+ *
+ * Date: January 26 03:18:16 2015
  *
  */
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: false,
           trailing: true, browser: true, smarttabs:true */
 /* global scope:true, OT:true */
 /* exported TBPlugin */
 
@@ -3950,16 +3788,82 @@ var shim = function shim () {
 
       return res;
     };
   }
 };
 // tb_require('./header.js')
 // tb_require('./shims.js')
 
+/* global OT:true */
+/* exported PluginRumorSocket */
+
+var PluginRumorSocket = function(plugin, server) {
+  var connected = false,
+      rumorID;
+
+  try {
+    rumorID = plugin._.RumorInit(server, '');
+  }
+  catch(e) {
+    OT.error('Error creating the Rumor Socket: ', e.message);
+  }
+
+  if(!rumorID) {
+    throw new Error('Could not initialise plugin rumor connection');
+  }
+
+  var socket = {
+    open: function() {
+      connected = true;
+      plugin._.RumorOpen(rumorID);
+    },
+
+    close: function(code, reason) {
+      if (!connected) return;
+      connected = false;
+
+      plugin._.RumorClose(rumorID, code, reason);
+      plugin.removeRef(this);
+    },
+
+    destroy: function() {
+      this.close();
+    },
+
+    send: function(msg) {
+      plugin._.RumorSend(rumorID, msg.type, msg.toAddress,
+        JSON.parse(JSON.stringify(msg.headers)), msg.data);
+    },
+
+    onOpen: function(callback) {
+      plugin._.SetOnRumorOpen(rumorID, callback);
+    },
+
+    onClose: function(callback) {
+      plugin._.SetOnRumorClose(rumorID, callback);
+    },
+
+    onError: function(callback) {
+      plugin._.SetOnRumorError(rumorID, callback);
+    },
+
+    onMessage: function(callback) {
+      plugin._.SetOnRumorMessage(rumorID, callback);
+    }
+  };
+
+  plugin.addRef(socket);
+  return socket;
+
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
 /* global OT:true, TBPlugin:true, pluginInfo:true, debug:true, scope:true,
           _document:true */
 /* exported createMediaCaptureController:true, createPeerController:true,
             injectObject:true, plugins:true, mediaCaptureObject:true,
             removeAllObjects:true, curryCallAsync:true */
 
@@ -4351,470 +4255,16 @@ var VideoContainer = function VideoConta
 };
 
 // tb_require('./header.js')
 // tb_require('./shims.js')
 // tb_require('./plugin_object.js')
 
 /* jshint globalstrict: true, strict: false, undef: true, unused: true,
           trailing: true, browser: true, smarttabs:true */
-/* global OT:true, TBPlugin:true, pluginInfo:true, ActiveXObject:true,
-          injectObject:true, curryCallAsync:true */
-
-/* exported AutoUpdater:true */
-var AutoUpdater;
-
-(function() {
-
-  var autoUpdaterController,
-      updaterMimeType,        // <- cached version, use getInstallerMimeType instead
-      installedVersion = -1;  // <- cached version, use getInstallerMimeType instead
-
-
-  var versionGreaterThan = function versionGreaterThan (version1,version2) {
-    if (version1 === version2) return false;
-
-    var v1 = version1.split('.'),
-        v2 = version2.split('.');
-
-    v1 = parseFloat(parseInt(v1.shift(), 10) + '.' +
-                      v1.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
-
-    v2 = parseFloat(parseInt(v2.shift(), 10) + '.' +
-                      v2.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
-
-
-    return v1 > v2;
-  };
-
-
-  // Work out the full mimeType (including the currently installed version)
-  // of the installer.
-  var findMimeTypeAndVersion = function findMimeTypeAndVersion () {
-
-    if (updaterMimeType !== void 0) {
-      return updaterMimeType;
-    }
-
-    var activeXControlId = 'TokBox.otiePluginInstaller',
-        unversionedMimeType = 'application/x-otieplugininstaller',
-        plugin = navigator.plugins[activeXControlId];
-
-    installedVersion = -1;
-
-
-    if (plugin) {
-      // Look through the supported mime-types for the version
-      // There should only be one mime-type in our use case, and
-      // if there's more than one they should all have the same
-      // version.
-      var numMimeTypes = plugin.length,
-          extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') +
-                                                            ',version=([0-9]+)', 'i'),
-          mimeType,
-          bits;
-
-      for (var i=0; i<numMimeTypes; ++i) {
-        mimeType = plugin[i];
-
-        // Look through the supported mimeTypes and find
-        // the newest one.
-        if (mimeType && mimeType.enabledPlugin &&
-            (mimeType.enabledPlugin.name === plugin.name) &&
-            mimeType.type.indexOf(unversionedMimeType) !== -1) {
-
-          bits = extractVersion.exec(mimeType.type);
-
-          if (bits !== null && versionGreaterThan(bits[1], installedVersion)) {
-            installedVersion = bits[1];
-          }
-        }
-      }
-    }
-    else {
-      // This may mean that the installer plugin is not installed.
-      // Although it could also mean that we're on IE 9 and below,
-      // which does not support navigator.plugins. Fallback to
-      // using 'ActiveXObject' instead.
-      try {
-        plugin = new ActiveXObject(activeXControlId);
-        installedVersion = plugin.getMasterVersion();
-      } catch(e) {
-      }
-    }
-
-    updaterMimeType = installedVersion !== -1 ?
-                              unversionedMimeType + ',version=' + installedVersion :
-                              null;
-  };
-
-  var getInstallerMimeType = function getInstallerMimeType () {
-    if (updaterMimeType === void 0) {
-      findMimeTypeAndVersion();
-    }
-
-    return updaterMimeType;
-  };
-
-  var getInstalledVersion = function getInstalledVersion () {
-    if (installedVersion === void 0) {
-      findMimeTypeAndVersion();
-    }
-
-    return installedVersion;
-  };
-
-  // Version 0.4.0.4 autoupdate was broken. We want to prompt
-  // for install on 0.4.0.4 or earlier. We're also including
-  // earlier versions just in case...
-  var hasBrokenUpdater = function () {
-    var _broken = !versionGreaterThan(getInstalledVersion(), '0.4.0.4');
-
-    hasBrokenUpdater = function() { return _broken; };
-    return _broken;
-  };
-
-
-  AutoUpdater = function (plugin) {
-
-    // Returns true if the version of the plugin installed on this computer
-    // does not match the one expected by this version of TBPlugin.
-    this.isOutOfDate = function () {
-      return versionGreaterThan(pluginInfo.version, getInstalledVersion());
-    };
-
-    this.autoUpdate = function () {
-      var modal = OT.Dialogs.Plugin.updateInProgress(),
-          analytics = new OT.Analytics(),
-        payload = {
-          ieVersion: OT.$.browserVersion().version,
-          pluginOldVersion: TBPlugin.installedVersion(),
-          pluginNewVersion: TBPlugin.version()
-        };
-
-      var success = curryCallAsync(function() {
-            analytics.logEvent({
-              action: 'OTPluginAutoUpdate',
-              variation: 'Success',
-              partnerId: OT.APIKEY,
-              payload: JSON.stringify(payload)
-            });
-
-            plugin.destroy();
-
-            modal.close();
-            OT.Dialogs.Plugin.updateComplete().on({
-              reload: function() {
-                window.location.reload();
-              }
-            });
-          }),
-
-          error = curryCallAsync(function(errorCode, errorMessage, systemErrorCode) {
-            payload.errorCode = errorCode;
-            payload.systemErrorCode = systemErrorCode;
-
-            analytics.logEvent({
-              action: 'OTPluginAutoUpdate',
-              variation: 'Failure',
-              partnerId: OT.APIKEY,
-              payload: JSON.stringify(payload)
-            });
-
-            plugin.destroy();
-
-            modal.close();
-            var updateMessage = errorMessage + ' (' + errorCode +
-                                      '). Please restart your browser and try again.';
-
-            modal = OT.Dialogs.Plugin.updateComplete(updateMessage).on({
-              'reload': function() {
-                modal.close();
-              }
-            });
-
-            OT.error('autoUpdate failed: ' + errorMessage + ' (' + errorCode +
-                                      '). Please restart your browser and try again.');
-            // TODO log client event
-          }),
-
-          progress = curryCallAsync(function(progress) {
-            modal.setUpdateProgress(progress.toFixed());
-            // modalBody.innerHTML = 'Updating...' + progress.toFixed() + '%';
-          });
-
-      plugin._.updatePlugin(TBPlugin.pathToInstaller(), success, error, progress);
-    };
-
-    this.destroy = function() {
-      plugin.destroy();
-    };
-  };
-
-  AutoUpdater.get = function (completion) {
-    if (autoUpdaterController) {
-      completion.call(null, void 0, autoUpdaterController);
-      return;
-    }
-
-    if (!this.isinstalled()) {
-      completion.call(null, 'Plugin was not installed');
-      return;
-    }
-
-    injectObject(getInstallerMimeType(), false, {windowless: false}, function(err, plugin) {
-      if (plugin) autoUpdaterController = new AutoUpdater(plugin);
-      completion.call(null, err, autoUpdaterController);
-    });
-  };
-
-  AutoUpdater.isinstalled = function () {
-    return getInstallerMimeType() !== null && !hasBrokenUpdater();
-  };
-
-  AutoUpdater.installedVersion = function () {
-    return getInstalledVersion();
-  };
-
-})();
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-// tb_require('./video_container.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global OT:true, VideoContainer:true */
-/* exported MediaStream */
-
-var MediaStreamTrack = function MediaStreamTrack (mediaStreamId, options, plugin) {
-  this.id = options.id;
-  this.kind = options.kind;
-  this.label = options.label;
-  this.enabled = OT.$.castToBoolean(options.enabled);
-  this.streamId = mediaStreamId;
-
-  this.setEnabled = function (enabled) {
-    this.enabled = OT.$.castToBoolean(enabled);
-
-    if (this.enabled) {
-      plugin._.enableMediaStreamTrack(mediaStreamId, this.id);
-    }
-    else {
-      plugin._.disableMediaStreamTrack(mediaStreamId, this.id);
-    }
-  };
-};
-
-var MediaStream = function MediaStream (options, plugin) {
-  var audioTracks = [],
-      videoTracks = [];
-
-  this.id = options.id;
-  plugin.addRef(this);
-
-  // TODO
-  // this.ended =
-  // this.onended =
-
-  if (options.videoTracks) {
-    options.videoTracks.map(function(track) {
-      videoTracks.push( new MediaStreamTrack(options.id, track, plugin) );
-    });
-  }
-
-  if (options.audioTracks) {
-    options.audioTracks.map(function(track) {
-      audioTracks.push( new MediaStreamTrack(options.id, track, plugin) );
-    });
-  }
-
-  var hasTracksOfType = function (type) {
-    var tracks = type === 'video' ? videoTracks : audioTracks;
-
-    return OT.$.some(tracks, function(track) {
-      return track.enabled;
-    });
-  };
-
-  this.getVideoTracks = function () { return videoTracks; };
-  this.getAudioTracks = function () { return audioTracks; };
-
-  this.getTrackById = function (id) {
-    videoTracks.concat(audioTracks).forEach(function(track) {
-      if (track.id === id) return track;
-    });
-
-    return null;
-  };
-
-  this.hasVideo = function () {
-    return hasTracksOfType('video');
-  };
-
-  this.hasAudio = function () {
-    return hasTracksOfType('audio');
-  };
-
-  this.addTrack = function (/* MediaStreamTrack */) {
-    // TODO
-  };
-
-  this.removeTrack = function (/* MediaStreamTrack */) {
-    // TODO
-  };
-
-  this.stop = function() {
-    plugin._.stopMediaStream(this.id);
-    plugin.removeRef(this);
-  };
-
-  this.destroy = function() {
-    this.stop();
-  };
-
-  // Private MediaStream API
-  this._ = {
-    plugin: plugin,
-
-    // Get a VideoContainer to render the stream in.
-    render: OT.$.bind(function() {
-      return new VideoContainer(plugin, this);
-    }, this)
-  };
-};
-
-
-MediaStream.fromJson = function (json, plugin) {
-  if (!json) return null;
-  return new MediaStream( JSON.parse(json), plugin );
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-
-/* global OT:true */
-/* exported PluginRumorSocket */
-
-var PluginRumorSocket = function(plugin, server) {
-  var connected = false,
-      rumorID;
-
-  try {
-    rumorID = plugin._.RumorInit(server, '');
-  }
-  catch(e) {
-    OT.error('Error creating the Rumor Socket: ', e.message);
-  }
-
-  if(!rumorID) {
-    throw new Error('Could not initialise plugin rumor connection');
-  }
-
-  var socket = {
-    open: function() {
-      connected = true;
-      plugin._.RumorOpen(rumorID);
-    },
-
-    close: function(code, reason) {
-      if (!connected) return;
-      connected = false;
-
-      plugin._.RumorClose(rumorID, code, reason);
-      plugin.removeRef(this);
-    },
-
-    destroy: function() {
-      this.close();
-    },
-
-    send: function(msg) {
-      plugin._.RumorSend(rumorID, msg.type, msg.toAddress,
-        JSON.parse(JSON.stringify(msg.headers)), msg.data);
-    },
-
-    onOpen: function(callback) {
-      plugin._.SetOnRumorOpen(rumorID, callback);
-    },
-
-    onClose: function(callback) {
-      plugin._.SetOnRumorClose(rumorID, callback);
-    },
-
-    onError: function(callback) {
-      plugin._.SetOnRumorError(rumorID, callback);
-    },
-
-    onMessage: function(callback) {
-      plugin._.SetOnRumorMessage(rumorID, callback);
-    }
-  };
-
-  plugin.addRef(socket);
-  return socket;
-
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-// tb_require('./video_container.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
-/* global OT:true */
-/* exported MediaConstraints */
-
-var MediaConstraints = function(userConstraints) {
-  var constraints = OT.$.clone(userConstraints);
-
-  this.hasVideo = constraints.video !== void 0 && constraints.video !== false;
-  this.hasAudio = constraints.audio !== void 0 && constraints.audio !== false;
-
-  if (constraints.video === true) constraints.video = {};
-  if (constraints.audio === true)  constraints.audio = {};
-
-  if (this.hasVideo && !constraints.video.mandatory) {
-    constraints.video.mandatory = {};
-  }
-
-  if (this.hasAudio && !constraints.audio.mandatory) {
-    constraints.audio.mandatory = {};
-  }
-
-  this.screenSharing = this.hasVideo &&
-                ( constraints.video.mandatory.chromeMediaSource === 'screen' ||
-                  constraints.video.mandatory.chromeMediaSource === 'window' );
-
-  this.audio = constraints.audio;
-  this.video = constraints.video;
-
-  this.setVideoSource = function(sourceId) {
-    if (sourceId !== void 0) constraints.video.mandatory.sourceId =  sourceId;
-    else delete constraints.video;
-  };
-
-  this.setAudioSource = function(sourceId) {
-    if (sourceId !== void 0) constraints.audio.mandatory.sourceId =  sourceId;
-    else delete constraints.audio;
-  };
-
-  this.toHash = function() {
-    return constraints;
-  };
-};
-
-// tb_require('./header.js')
-// tb_require('./shims.js')
-// tb_require('./plugin_object.js')
-
-/* jshint globalstrict: true, strict: false, undef: true, unused: true,
-          trailing: true, browser: true, smarttabs:true */
 /* exported RTCStatsReport */
 
 var RTCStatsReport = function (reports) {
   this.forEach = function (callback, context) {
     for (var id in reports) {
       callback.call(context, reports[id]);
     }
   };
@@ -5033,16 +4483,404 @@ var PeerConnection = function PeerConnec
 };
 
 
 
 
 // tb_require('./header.js')
 // tb_require('./shims.js')
 // tb_require('./plugin_object.js')
+// tb_require('./video_container.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT:true, VideoContainer:true */
+/* exported MediaStream */
+
+var MediaStreamTrack = function MediaStreamTrack (mediaStreamId, options, plugin) {
+  this.id = options.id;
+  this.kind = options.kind;
+  this.label = options.label;
+  this.enabled = OT.$.castToBoolean(options.enabled);
+  this.streamId = mediaStreamId;
+
+  this.setEnabled = function (enabled) {
+    this.enabled = OT.$.castToBoolean(enabled);
+
+    if (this.enabled) {
+      plugin._.enableMediaStreamTrack(mediaStreamId, this.id);
+    }
+    else {
+      plugin._.disableMediaStreamTrack(mediaStreamId, this.id);
+    }
+  };
+};
+
+var MediaStream = function MediaStream (options, plugin) {
+  var audioTracks = [],
+      videoTracks = [];
+
+  this.id = options.id;
+  plugin.addRef(this);
+
+  // TODO
+  // this.ended =
+  // this.onended =
+
+  if (options.videoTracks) {
+    options.videoTracks.map(function(track) {
+      videoTracks.push( new MediaStreamTrack(options.id, track, plugin) );
+    });
+  }
+
+  if (options.audioTracks) {
+    options.audioTracks.map(function(track) {
+      audioTracks.push( new MediaStreamTrack(options.id, track, plugin) );
+    });
+  }
+
+  var hasTracksOfType = function (type) {
+    var tracks = type === 'video' ? videoTracks : audioTracks;
+
+    return OT.$.some(tracks, function(track) {
+      return track.enabled;
+    });
+  };
+
+  this.getVideoTracks = function () { return videoTracks; };
+  this.getAudioTracks = function () { return audioTracks; };
+
+  this.getTrackById = function (id) {
+    videoTracks.concat(audioTracks).forEach(function(track) {
+      if (track.id === id) return track;
+    });
+
+    return null;
+  };
+
+  this.hasVideo = function () {
+    return hasTracksOfType('video');
+  };
+
+  this.hasAudio = function () {
+    return hasTracksOfType('audio');
+  };
+
+  this.addTrack = function (/* MediaStreamTrack */) {
+    // TODO
+  };
+
+  this.removeTrack = function (/* MediaStreamTrack */) {
+    // TODO
+  };
+
+  this.stop = function() {
+    plugin._.stopMediaStream(this.id);
+    plugin.removeRef(this);
+  };
+
+  this.destroy = function() {
+    this.stop();
+  };
+
+  // Private MediaStream API
+  this._ = {
+    plugin: plugin,
+
+    // Get a VideoContainer to render the stream in.
+    render: OT.$.bind(function() {
+      return new VideoContainer(plugin, this);
+    }, this)
+  };
+};
+
+
+MediaStream.fromJson = function (json, plugin) {
+  if (!json) return null;
+  return new MediaStream( JSON.parse(json), plugin );
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
+// tb_require('./video_container.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT:true */
+/* exported MediaConstraints */
+
+var MediaConstraints = function(userConstraints) {
+  var constraints = OT.$.clone(userConstraints);
+
+  this.hasVideo = constraints.video !== void 0 && constraints.video !== false;
+  this.hasAudio = constraints.audio !== void 0 && constraints.audio !== false;
+
+  if (constraints.video === true) constraints.video = {};
+  if (constraints.audio === true)  constraints.audio = {};
+
+  if (this.hasVideo && !constraints.video.mandatory) {
+    constraints.video.mandatory = {};
+  }
+
+  if (this.hasAudio && !constraints.audio.mandatory) {
+    constraints.audio.mandatory = {};
+  }
+
+  this.screenSharing = this.hasVideo &&
+                ( constraints.video.mandatory.chromeMediaSource === 'screen' ||
+                  constraints.video.mandatory.chromeMediaSource === 'window' );
+
+  this.audio = constraints.audio;
+  this.video = constraints.video;
+
+  this.setVideoSource = function(sourceId) {
+    if (sourceId !== void 0) constraints.video.mandatory.sourceId =  sourceId;
+    else delete constraints.video;
+  };
+
+  this.setAudioSource = function(sourceId) {
+    if (sourceId !== void 0) constraints.audio.mandatory.sourceId =  sourceId;
+    else delete constraints.audio;
+  };
+
+  this.toHash = function() {
+    return constraints;
+  };
+};
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT:true, TBPlugin:true, pluginInfo:true, ActiveXObject:true,
+          injectObject:true, curryCallAsync:true */
+
+/* exported AutoUpdater:true */
+var AutoUpdater;
+
+(function() {
+
+  var autoUpdaterController,
+      updaterMimeType,        // <- cached version, use getInstallerMimeType instead
+      installedVersion = -1;  // <- cached version, use getInstallerMimeType instead
+
+
+  var versionGreaterThan = function versionGreaterThan (version1,version2) {
+    if (version1 === version2) return false;
+
+    var v1 = version1.split('.'),
+        v2 = version2.split('.');
+
+    v1 = parseFloat(parseInt(v1.shift(), 10) + '.' +
+                      v1.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
+
+    v2 = parseFloat(parseInt(v2.shift(), 10) + '.' +
+                      v2.map(function(vcomp) { return parseInt(vcomp, 10); }).join(''));
+
+
+    return v1 > v2;
+  };
+
+
+  // Work out the full mimeType (including the currently installed version)
+  // of the installer.
+  var findMimeTypeAndVersion = function findMimeTypeAndVersion () {
+
+    if (updaterMimeType !== void 0) {
+      return updaterMimeType;
+    }
+
+    var activeXControlId = 'TokBox.otiePluginInstaller',
+        unversionedMimeType = 'application/x-otieplugininstaller',
+        plugin = navigator.plugins[activeXControlId];
+
+    installedVersion = -1;
+
+
+    if (plugin) {
+      // Look through the supported mime-types for the version
+      // There should only be one mime-type in our use case, and
+      // if there's more than one they should all have the same
+      // version.
+      var numMimeTypes = plugin.length,
+          extractVersion = new RegExp(unversionedMimeType.replace('-', '\\-') +
+                                                            ',version=([0-9]+)', 'i'),
+          mimeType,
+          bits;
+
+      for (var i=0; i<numMimeTypes; ++i) {
+        mimeType = plugin[i];
+
+        // Look through the supported mimeTypes and find
+        // the newest one.
+        if (mimeType && mimeType.enabledPlugin &&
+            (mimeType.enabledPlugin.name === plugin.name) &&
+            mimeType.type.indexOf(unversionedMimeType) !== -1) {
+
+          bits = extractVersion.exec(mimeType.type);
+
+          if (bits !== null && versionGreaterThan(bits[1], installedVersion)) {
+            installedVersion = bits[1];
+          }
+        }
+      }
+    }
+    else {
+      // This may mean that the installer plugin is not installed.
+      // Although it could also mean that we're on IE 9 and below,
+      // which does not support navigator.plugins. Fallback to
+      // using 'ActiveXObject' instead.
+      try {
+        plugin = new ActiveXObject(activeXControlId);
+        installedVersion = plugin.getMasterVersion();
+      } catch(e) {
+      }
+    }
+
+    updaterMimeType = installedVersion !== -1 ?
+                              unversionedMimeType + ',version=' + installedVersion :
+                              null;
+  };
+
+  var getInstallerMimeType = function getInstallerMimeType () {
+    if (updaterMimeType === void 0) {
+      findMimeTypeAndVersion();
+    }
+
+    return updaterMimeType;
+  };
+
+  var getInstalledVersion = function getInstalledVersion () {
+    if (installedVersion === void 0) {
+      findMimeTypeAndVersion();
+    }
+
+    return installedVersion;
+  };
+
+  // Version 0.4.0.4 autoupdate was broken. We want to prompt
+  // for install on 0.4.0.4 or earlier. We're also including
+  // earlier versions just in case...
+  var hasBrokenUpdater = function () {
+    var _broken = !versionGreaterThan(getInstalledVersion(), '0.4.0.4');
+
+    hasBrokenUpdater = function() { return _broken; };
+    return _broken;
+  };
+
+
+  AutoUpdater = function (plugin) {
+
+    // Returns true if the version of the plugin installed on this computer
+    // does not match the one expected by this version of TBPlugin.
+    this.isOutOfDate = function () {
+      return versionGreaterThan(pluginInfo.version, getInstalledVersion());
+    };
+
+    this.autoUpdate = function () {
+      var modal = OT.Dialogs.Plugin.updateInProgress(),
+          analytics = new OT.Analytics(),
+        payload = {
+          ieVersion: OT.$.browserVersion().version,
+          pluginOldVersion: TBPlugin.installedVersion(),
+          pluginNewVersion: TBPlugin.version()
+        };
+
+      var success = curryCallAsync(function() {
+            analytics.logEvent({
+              action: 'OTPluginAutoUpdate',
+              variation: 'Success',
+              partnerId: OT.APIKEY,
+              payload: JSON.stringify(payload)
+            });
+
+            plugin.destroy();
+
+            modal.close();
+            OT.Dialogs.Plugin.updateComplete().on({
+              reload: function() {
+                window.location.reload();
+              }
+            });
+          }),
+
+          error = curryCallAsync(function(errorCode, errorMessage, systemErrorCode) {
+            payload.errorCode = errorCode;
+            payload.systemErrorCode = systemErrorCode;
+
+            analytics.logEvent({
+              action: 'OTPluginAutoUpdate',
+              variation: 'Failure',
+              partnerId: OT.APIKEY,
+              payload: JSON.stringify(payload)
+            });
+
+            plugin.destroy();
+
+            modal.close();
+            var updateMessage = errorMessage + ' (' + errorCode +
+                                      '). Please restart your browser and try again.';
+
+            modal = OT.Dialogs.Plugin.updateComplete(updateMessage).on({
+              'reload': function() {
+                modal.close();
+              }
+            });
+
+            OT.error('autoUpdate failed: ' + errorMessage + ' (' + errorCode +
+                                      '). Please restart your browser and try again.');
+            // TODO log client event
+          }),
+
+          progress = curryCallAsync(function(progress) {
+            modal.setUpdateProgress(progress.toFixed());
+            // modalBody.innerHTML = 'Updating...' + progress.toFixed() + '%';
+          });
+
+      plugin._.updatePlugin(TBPlugin.pathToInstaller(), success, error, progress);
+    };
+
+    this.destroy = function() {
+      plugin.destroy();
+    };
+  };
+
+  AutoUpdater.get = function (completion) {
+    if (autoUpdaterController) {
+      completion.call(null, void 0, autoUpdaterController);
+      return;
+    }
+
+    if (!this.isinstalled()) {
+      completion.call(null, 'Plugin was not installed');
+      return;
+    }
+
+    injectObject(getInstallerMimeType(), false, {windowless: false}, function(err, plugin) {
+      if (plugin) autoUpdaterController = new AutoUpdater(plugin);
+      completion.call(null, err, autoUpdaterController);
+    });
+  };
+
+  AutoUpdater.isinstalled = function () {
+    return getInstallerMimeType() !== null && !hasBrokenUpdater();
+  };
+
+  AutoUpdater.installedVersion = function () {
+    return getInstalledVersion();
+  };
+
+})();
+
+// tb_require('./header.js')
+// tb_require('./shims.js')
+// tb_require('./plugin_object.js')
 // tb_require('./auto_updater.js')
 // tb_require('./media_constraints.js')
 // tb_require('./peer_connection.js')
 // tb_require('./media_stream.js')
 // tb_require('./video_container.js')
 // tb_require('./rumor.js')
 
 /* jshint globalstrict: true, strict: false, undef: true,
@@ -7012,17 +6850,17 @@ waitForDomReady();
         // Map of camel-cased keys to underscored
         camelCasedKeys,
 
         browser = OT.$.browserVersion(),
 
         send = function(data, isQos, callback) {
           OT.$.post((isQos ? endPointQos : endPoint) + '?_=' + OT.$.uuid.v4(), {
             body: data,
-            xdomainrequest: (browser.browser === 'IE' & browser.version < 10),
+            xdomainrequest: (browser.browser === 'IE' && browser.version < 10),
             headers: {
               'Content-Type': 'application/x-www-form-urlencoded'
             }
           }, callback);
         },
 
         throttledPost = function() {
           // Throttle logs so that they only happen 1 at a time
@@ -9168,17 +9006,17 @@ waitForDomReady();
  *
  * Original source: https://github.com/inexorabletash/text-encoding
  ***/
 
 (function(global) {
   'use strict';
 
   var browser = OT.$.browserVersion();
-  if(browser.browser === 'IE' && browser.version < 10) {
+  if(browser && browser.browser === 'IE' && browser.version < 10) {
     return; // IE 8 doesn't do websockets. No websockets, no encoding.
   }
 
   if ( (global.TextEncoder !== void 0) && (global.TextDecoder !== void 0))  {
     // defer to the native ones
     // @todo is this a good idea?
     return;
   }
@@ -13533,18 +13371,20 @@ waitForDomReady();
     if (session.archives.has(dict.id)) return;
 
     var archive = parseArchive(dict);
     session.archives.add(archive);
 
     return archive;
   }
 
-  var sessionRead;
-  var sessionReadQueue = [];
+  var sessionRead,
+    sessionReadQueue = [],
+    // streams for which corresponding connectionCreated events have not been dispatched:
+    unconnectedStreams = {};
 
   function sessionReadQueuePush(type, args) {
     var triggerArgs = ['signal'];
     triggerArgs.push.apply(triggerArgs, args);
     sessionReadQueue.push(triggerArgs);
   }
 
   window.OT.SessionDispatcher = function(session) {
@@ -13604,25 +13444,39 @@ waitForDomReady();
 
     });
 
     dispatcher.on('connection#created', function(connection) {
       connection = OT.Connection.fromHash(connection);
       if (session.connection && connection.id !== session.connection.id) {
         session.connections.add( connection );
       }
+
+      OT.$.forEach(OT.$.keys(unconnectedStreams), function(streamId) {
+        var stream = unconnectedStreams[streamId];
+        if (stream && connection.id === stream.connection.id) {
+          // dispatch streamCreated event now that the connectionCreated has been dispatched
+          parseAndAddStreamToSession(stream, session);
+          delete unconnectedStreams[stream.id];
+        }
+      });
     });
 
     dispatcher.on('connection#deleted', function(connection, reason) {
       connection = session.connections.get(connection);
       connection.destroy(reason);
     });
 
     dispatcher.on('stream#created', function(stream, transactionId) {
-      stream = parseAndAddStreamToSession(stream, session);
+      var connectionId = stream.connectionId ? stream.connectionId : stream.connection.id;
+      if (session.connections.has(connectionId)) {
+        stream = parseAndAddStreamToSession(stream, session);
+      } else {
+        unconnectedStreams[stream.id] = stream;
+      }
 
       if (stream.publisher) {
         stream.publisher.setStream(stream);
       }
 
       dispatcher.triggerCallback(transactionId, null, stream);
     });
 
@@ -14767,16 +14621,32 @@ waitForDomReady();
 
     this.destroy = function() {};
 
   };
 
 })(window);
 !(function() {
 
+  /**
+   * Lazy instantiates an audio context and always return the same instance on following calls
+   *
+   * @returns {AudioContext}
+   */
+  OT.audioContext = function() {
+    var context = new window.AudioContext();
+    OT.audioContext = function() {
+      return context;
+    };
+    return context;
+  };
+
+})();
+!(function() {
+
 
   /*
    * A <code>RTCPeerConnection.getStats</code> based audio level sampler.
    *
    * It uses the the <code>getStats</code> method to get the <code>audioOutputLevel</code>.
    * This implementation expects the single parameter version of the <code>getStats</code> method.
    *
    * Currently the <code>audioOutputLevel</code> stats is only supported in Chrome.
@@ -14929,17 +14799,150 @@ waitForDomReady();
 
     this.stop = function() {
       window.clearInterval(_intervalId);
       _intervalId = null;
     };
   };
 
 })(window);
+// tb_require('../../helpers/helpers.js')
+
+/* jshint globalstrict: true, strict: false, undef: true, unused: true,
+          trailing: true, browser: true, smarttabs:true */
+/* global OT */
+/* exported SDPHelpers */
+
+var findIndex = function(array, iter, ctx) {
+  if (!OT.$.isFunction(iter)) {
+    throw new TypeError('iter must be a function');
+  }
+
+  for (var i = 0, count = array.length || 0; i < count; ++i) {
+    if (i in array && iter.call(ctx, array[i], i, array)) {
+      return i;
+    }
+  }
+
+  return -1;
+};
+
+// Here are the structure of the rtpmap attribute and the media line, most of the
+// complex Regular Expressions in this code are matching against one of these two
+// formats:
+// * a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
+// * m=<media> <port>/<number of ports> <proto> <fmts>
+//
+// References:
+// * https://tools.ietf.org/html/rfc4566
+// * http://en.wikipedia.org/wiki/Session_Description_Protocol
+//
+var SDPHelpers = {
+  // Search through sdpLines to find the Media Line of type +mediaType+.
+  getMLineIndex: function getMLineIndex(sdpLines, mediaType) {
+    var targetMLine = 'm=' + mediaType;
+
+    // Find the index of the media line for +type+
+    return findIndex(sdpLines, function(line) {
+      if (line.indexOf(targetMLine) !== -1) {
+        return true;
+      }
+
+      return false;
+    });
+  },
+
+  // Extract the payload types for a give Media Line.
+  //
+  getMLinePayloadTypes: function getMLinePayloadTypes (mediaLine, mediaType) {
+    var mLineSelector = new RegExp('^m=' + mediaType +
+                          ' \\d+(/\\d+)? [a-zA-Z0-9/]+(( [a-zA-Z0-9/]+)+)$', 'i');
+
+    // Get all payload types that the line supports
+    var payloadTypes = mediaLine.match(mLineSelector);
+    if (!payloadTypes || payloadTypes.length < 2) {
+      // Error, invalid M line?
+      return [];
+    }
+
+    return OT.$.trim(payloadTypes[2]).split(' ');
+  },
+
+  removeTypesFromMLine: function removeTypesFromMLine (mediaLine, payloadTypes) {
+    return mediaLine.replace(new RegExp(' ' + payloadTypes.join(' |'), 'ig') , '')
+                    .replace(/\s+/g, ' ');
+  },
+
+
+  // Remove all references to a particular encodingName from a particular media type
+  //
+  removeMediaEncoding: function removeMediaEncoding (sdp, mediaType, encodingName) {
+    var sdpLines = sdp.split('\r\n'),
+        mLineIndex = SDPHelpers.getMLineIndex(sdpLines, mediaType),
+        mLine = mLineIndex > -1 ? sdpLines[mLineIndex] : void 0,
+        typesToRemove = [],
+        payloadTypes,
+        match;
+
+    if (mLineIndex === -1) {
+      // Error, missing M line
+      return sdpLines.join('\r\n');
+    }
+
+    // Get all payload types that the line supports
+    payloadTypes = SDPHelpers.getMLinePayloadTypes(mLine, mediaType);
+    if (payloadTypes.length === 0) {
+      // Error, invalid M line?
+      return sdpLines.join('\r\n');
+    }
+
+    // Find the location of all the rtpmap lines that relate to +encodingName+
+    // and any of the supported payload types
+    var matcher = new RegExp('a=rtpmap:(' + payloadTypes.join('|') + ') ' +
+                                          encodingName + '\\/\\d+', 'i');
+
+    sdpLines = OT.$.filter(sdpLines, function(line, index) {
+      match = line.match(matcher);
+      if (match === null) return true;
+
+      typesToRemove.push(match[1]);
+
+      if (index < mLineIndex) {
+        // This removal changed the index of the mline, track it
+        mLineIndex--;
+      }
+
+      // remove this one
+      return false;
+    });
+
+    if (typesToRemove.length > 0 && mLineIndex > -1) {
+      // Remove all the payload types and we've removed from the media line
+      sdpLines[mLineIndex] = SDPHelpers.removeTypesFromMLine(mLine, typesToRemove);
+    }
+
+    return sdpLines.join('\r\n');
+  },
+
+  // Removes all Confort Noise from +sdp+.
+  //
+  // See https://jira.tokbox.com/browse/OPENTOK-7176
+  //
+  removeComfortNoise: function removeComfortNoise (sdp) {
+    return SDPHelpers.removeMediaEncoding(sdp, 'audio', 'CN');
+  },
+
+  removeVideoCodec: function removeVideoCodec (sdp, codec) {
+    return SDPHelpers.removeMediaEncoding(sdp, 'video', codec);
+  }
+};
+
+
 !(function(window) {
+  /* global SDPHelpers */
 
   // Normalise these
   var NativeRTCSessionDescription,
       NativeRTCIceCandidate;
 
   if (!TBPlugin.isInstalled()) {
     // order is very important: 'RTCSessionDescription' defined in Firefox Nighly but useless
     NativeRTCSessionDescription = (window.mozRTCSessionDescription ||
@@ -14998,84 +15001,16 @@ waitForDomReady();
 
     this.processPending = function() {
       while(_pendingIceCandidates.length) {
         _peerConnection.addIceCandidate(_pendingIceCandidates.shift());
       }
     };
   };
 
-  // Removes all Confort Noise from +sdp+.
-  //
-  // See https://jira.tokbox.com/browse/OPENTOK-7176
-  //
-  var removeComfortNoise = function removeComfortNoise (sdp) {
-    // a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
-    var matcher = /a=rtpmap:(\d+) CN\/\d+/i,
-        payloadTypes = [],
-        audioMediaLineIndex,
-        sdpLines,
-        match;
-
-    // Icky code. This filter operation has two side effects in addition
-    // to doing the actual filtering:
-    //   1. extract all the payload types from the rtpmap CN lines
-    //   2. find the index of the audio media line
-    //
-    sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) {
-      if (line.indexOf('m=audio') !== -1) audioMediaLineIndex = index;
-
-      match = line.match(matcher);
-      if (match !== null) {
-        payloadTypes.push(match[1]);
-
-        // remove this line as it contains CN
-        return false;
-      }
-
-      return true;
-    });
-
-    if (payloadTypes.length && audioMediaLineIndex) {
-      // Remove all CN payload types from the audio media line.
-      sdpLines[audioMediaLineIndex] = sdpLines[audioMediaLineIndex].replace(
-        new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' ');
-    }
-
-    return sdpLines.join('\r\n');
-  };
-
-  var removeVideoCodec = function removeVideoCodec (sdp, codec) {
-    var matcher =  new RegExp('a=rtpmap:(\\d+) ' + codec + '\\/\\d+', 'i'),
-        payloadTypes = [],
-        videoMediaLineIndex,
-        sdpLines,
-        match;
-
-    sdpLines = OT.$.filter(sdp.split('\r\n'), function(line, index) {
-      if (line.indexOf('m=video') !== -1) videoMediaLineIndex = index;
-
-      match = line.match(matcher);
-      if (match !== null) {
-        payloadTypes.push(match[1]);
-
-        // remove this line as it contains the codec
-        return false;
-      }
-
-      return true;
-    });
-
-    if (payloadTypes.length && videoMediaLineIndex) {
-      sdpLines[videoMediaLineIndex] = sdpLines[videoMediaLineIndex].replace(
-        new RegExp(payloadTypes.join('|'), 'ig') , '').replace(/\s+/g, ' ');
-    }
-
-    return sdpLines.join('\r\n');
-  };
 
   // Attempt to completely process +offer+. This will:
   // * set the offer as the remote description
   // * create an answer and
   // * set the new answer as the location description
   //
   // If there are no issues, the +success+ callback will be executed on completion.
   // Errors during any step will result in the +failure+ callback being executed.
@@ -15090,19 +15025,19 @@ waitForDomReady();
         OT.error(message);
         OT.error(errorReason);
 
         if (failure) failure(message, errorReason, prefix);
       };
     };
 
     setLocalDescription = function(answer) {
-      answer.sdp = removeComfortNoise(answer.sdp);
-      answer.sdp = removeVideoCodec(answer.sdp, 'ulpfec');
-      answer.sdp = removeVideoCodec(answer.sdp, 'red');
+      answer.sdp = SDPHelpers.removeComfortNoise(answer.sdp);
+      answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'ulpfec');
+      answer.sdp = SDPHelpers.removeVideoCodec(answer.sdp, 'red');
 
       peerConnection.setLocalDescription(
         answer,
 
         // Success
         function() {
           success(answer);
         },
@@ -15176,19 +15111,20 @@ waitForDomReady();
         OT.error(message);
         OT.error(errorReason);
 
         if (failure) failure(message, errorReason, prefix);
       };
     };
 
     setLocalDescription = function(offer) {
-      offer.sdp = removeComfortNoise(offer.sdp);
-      offer.sdp = removeVideoCodec(offer.sdp, 'ulpfec');
-      offer.sdp = removeVideoCodec(offer.sdp, 'red');
+      offer.sdp = SDPHelpers.removeComfortNoise(offer.sdp);
+      offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'ulpfec');
+      offer.sdp = SDPHelpers.removeVideoCodec(offer.sdp, 'red');
+
 
       peerConnection.setLocalDescription(
         offer,
 
         // Success
         function() {
           success(offer);
         },
@@ -16581,52 +16517,39 @@ waitForDomReady();
   // Whether to display the name. Possible values are: "auto" (the name is displayed
   // when the stream is first displayed and when the user mouses over the display),
   // "off" (the name is not displayed), and "on" (the name is displayed).
   //
   // displays a name
   // can be shown/hidden
   // can be destroyed
   OT.Chrome.NamePanel = function(options) {
-    var _name = options.name,
-        _bugMode = options.bugMode;
+    var _name = options.name;
 
     if (!_name || OT.$.trim(_name).length === '') {
       _name = null;
 
       // THere's no name, just flip the mode off
       options.mode = 'off';
     }
 
     this.setName = OT.$.bind(function(name) {
       if (!_name) this.setDisplayMode('auto');
       _name = name;
       this.domElement.innerHTML = _name;
     });
 
-    this.setBugMode = OT.$.bind(function(bugMode) {
-      _bugMode = bugMode;
-      if(bugMode === 'off') {
-        OT.$.addClass(this.domElement, 'OT_name-no-bug');
-      } else {
-        OT.$.removeClass(this.domElement, 'OT_name-no-bug');
-      }
-    }, this);
-
     // Mixin common widget behaviour
     OT.Chrome.Behaviour.Widget(this, {
       mode: options.mode,
       nodeName: 'h1',
       htmlContent: _name,
       htmlAttributes: {
         className: 'OT_name OT_edge-bar-item'
-      },
-      onCreate: OT.$.bind(function() {
-        this.setBugMode(_bugMode);
-      }, this)
+      }
     });
 
   };
 
 })(window);
 !(function() {
 
   OT.Chrome.MuteButton = function(options) {
@@ -16693,33 +16616,16 @@ waitForDomReady();
       onDestroy: OT.$.bind(detachEvents, this)
     });
   };
 
 
 })(window);
 !(function() {
 
-  OT.Chrome.OpenTokButton = function(options) {
-
-    // Mixin common widget behaviour
-    OT.Chrome.Behaviour.Widget(this, {
-      mode: options ? options.mode : null,
-      nodeName: 'span',
-      htmlContent: 'OpenTok',
-      htmlAttributes: {
-        className: 'OT_opentok OT_edge-bar-item'
-      }
-    });
-
-  };
-
-})(window);
-!(function() {
-
   // Archving Chrome Widget
   //
   // mode (String)
   // Whether to display the archving widget. Possible values are: "on" (the status is displayed
   // when archiving and briefly when archving ends) and "off" (the status is not displayed)
 
   // Whether to display the archving widget. Possible values are: "auto" (the name is displayed
   // when the status is first displayed and when the user mouses over the display),
@@ -16831,45 +16737,60 @@ waitForDomReady();
     var widget = this,
         _meterBarElement,
         _voiceOnlyIconElement,
         _meterValueElement,
         _value,
         _maxValue = options.maxValue || 1,
         _minValue = options.minValue || 0;
 
+    function onCreate() {
+      _meterBarElement = OT.$.createElement('div', {
+        className: 'OT_audio-level-meter__bar'
+      }, '');
+      _meterValueElement = OT.$.createElement('div', {
+        className: 'OT_audio-level-meter__value'
+      }, '');
+      _voiceOnlyIconElement = OT.$.createElement('div', {
+        className: 'OT_audio-level-meter__audio-only-img'
+      }, '');
+
+      widget.domElement.appendChild(_meterBarElement);
+      widget.domElement.appendChild(_voiceOnlyIconElement);
+      widget.domElement.appendChild(_meterValueElement);
+    }
+
+    function updateView() {
+      var percentSize = _value * 100 / (_maxValue - _minValue);
+      _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%';
+      _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%';
+    }
+
     // Mixin common widget behaviour
-    OT.Chrome.Behaviour.Widget(this, {
+    var widgetOptions = {
       mode: options ? options.mode : 'auto',
       nodeName: 'div',
       htmlAttributes: {
         className: 'OT_audio-level-meter'
       },
-      onCreate: function() {
-        _meterBarElement = OT.$.createElement('div', {
-          className: 'OT_audio-level-meter__bar'
-        }, '');
-        _meterValueElement = OT.$.createElement('div', {
-          className: 'OT_audio-level-meter__value'
-        }, '');
-        _voiceOnlyIconElement = OT.$.createElement('div', {
-          className: 'OT_audio-level-meter__audio-only-img'
-        }, '');
-
-        widget.domElement.appendChild(_meterBarElement);
-        widget.domElement.appendChild(_voiceOnlyIconElement);
-        widget.domElement.appendChild(_meterValueElement);
-      }
-    });
-
-    function updateView() {
-      var percentSize = _value * 100 / (_maxValue - _minValue);
-      _meterValueElement.style.width = _meterValueElement.style.height = 2 * percentSize + '%';
-      _meterValueElement.style.top = _meterValueElement.style.right = -percentSize + '%';
-    }
+      onCreate: onCreate
+    };
+
+    OT.Chrome.Behaviour.Widget(this, widgetOptions);
+
+    // override
+    var _setDisplayMode = OT.$.bind(widget.setDisplayMode, widget);
+    widget.setDisplayMode = function(mode) {
+      _setDisplayMode(mode);
+      if (mode === 'off') {
+        if (options.onPassivate) options.onPassivate();
+      } else {
+        if (options.onActivate) options.onActivate();
+      }
+    };
 
     widget.setValue = function(value) {
       _value = value;
       updateView();
     };
   };
 
 })(window);
@@ -17177,24 +17098,22 @@ waitForDomReady();
         isValidStyle,
         castValue;
 
     _COMPONENT_STYLES = [
       'showMicButton',
       'showSpeakerButton',
       'nameDisplayMode',
       'buttonDisplayMode',
-      'backgroundImageURI',
-      'bugDisplayMode'
+      'backgroundImageURI'
     ];
 
     _validStyleValues = {
       buttonDisplayMode: ['auto', 'mini', 'mini-auto', 'off', 'on'],
       nameDisplayMode: ['auto', 'off', 'on'],
-      bugDisplayMode: ['auto', 'off', 'on'],
       audioLevelDisplayMode: ['auto', 'off', 'on'],
       showSettingsButton: [true, false],
       showMicButton: [true, false],
       backgroundImageURI: null,
       showControlBar: [true, false],
       showArchiveStatus: [true, false],
       videoDisabledDisplayMode: ['auto', 'off', 'on']
     };
@@ -17688,38 +17607,38 @@ waitForDomReady();
         _audioLevelMeter,
         _analytics = new OT.Analytics(),
         _validResolutions,
         _validFrameRates = [ 1, 7, 15, 30 ],
         _prevStats,
         _state,
         _iceServers,
         _audioLevelCapable = OT.$.hasCapabilities('webAudio'),
-        _audioLevelSampler;
+        _audioLevelSampler,
+        _publisher = this;
 
     _validResolutions = {
       '320x240': {width: 320, height: 240},
       '640x480': {width: 640, height: 480},
       '1280x720': {width: 1280, height: 720}
     };
 
     _prevStats = {
       'timeStamp' : OT.$.now()
     };
 
     OT.$.eventing(this);
 
     if(_audioLevelCapable) {
-      _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext());
-
-      var publisher = this;
+      _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext());
+
       var audioLevelRunner = new OT.IntervalRunner(function() {
         _audioLevelSampler.sample(function(audioInputLevel) {
           OT.$.requestAnimationFrame(function() {
-            publisher.dispatchEvent(
+            _publisher.dispatchEvent(
               new OT.AudioLevelUpdatedEvent(audioInputLevel));
           });
         });
       }, 60);
 
       this.on({
         'audioLevelUpdated:added': function(count) {
           if (count === 1) {
@@ -17733,17 +17652,16 @@ waitForDomReady();
         }
       });
     }
 
     OT.StylableComponent(this, {
       showArchiveStatus: true,
       nameDisplayMode: 'auto',
       buttonDisplayMode: 'auto',
-      bugDisplayMode: 'auto',
       audioLevelDisplayMode: 'auto',
       backgroundImageURI: null
     });
 
         /// Private Methods
     var logAnalyticsEvent = function(action, variation, payloadType, payload) {
           _analytics.logEvent({
             action: action,
@@ -17838,19 +17756,17 @@ waitForDomReady();
           cleanupLocalStream();
           _webRTCStream = webOTStream;
 
           _microphone = new OT.Microphone(_webRTCStream, !_publishProperties.publishAudio);
           this.publishVideo(_publishProperties.publishVideo &&
             _webRTCStream.getVideoTracks().length > 0);
 
           this.accessAllowed = true;
-          this.dispatchEvent(
-            new OT.Event(OT.Event.names.ACCESS_ALLOWED, false)
-          );
+          this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_ALLOWED, false));
 
           var videoContainerOptions = {
             muted: true,
             error: OT.$.bind(onVideoError, this)
           };
 
           _targetElement = _container.bindVideo(_webRTCStream,
                                             videoContainerOptions,
@@ -17858,17 +17774,17 @@ waitForDomReady();
             if (err) {
               onLoadFailure.call(this, err);
               return;
             }
 
             onLoaded.call(this);
           }, this));
 
-          if(_audioLevelSampler) {
+          if(_audioLevelSampler && webOTStream.getAudioTracks().length > 0) {
             _audioLevelSampler.webOTStream = webOTStream;
           }
 
         },
 
         onStreamAvailableError = function(error) {
           OT.error('OT.Publisher.onStreamAvailableError ' + error.name + ': ' + error.message);
 
@@ -17896,115 +17812,34 @@ waitForDomReady();
           _state.set('Failed');
           this.trigger('publishComplete', new OT.Error(OT.ExceptionCodes.UNABLE_TO_PUBLISH,
               'Publisher Access Denied: Permission Denied' +
                   (error.message ? ': ' + error.message : '')));
 
           logAnalyticsEvent('publish', 'Failure', 'reason',
             'GetUserMedia:Publisher Access Denied: Permission Denied');
 
-          var browser = OT.$.browserVersion();
-
-          var event = new OT.Event(OT.Event.names.ACCESS_DENIED),
-            defaultAction = function() {
-              if(!event.isDefaultPrevented()) {
-                if(browser.browser === 'Chrome') {
-                  if (_container) {
-                    _container.addError('', null, 'OT_publisher-denied-chrome');
-                  }
-                  if(!accessDialogWasOpened) {
-                    OT.Dialogs.AllowDeny.Chrome.previouslyDenied(window.location.hostname);
-                  } else {
-                    OT.Dialogs.AllowDeny.Chrome.deniedNow();
-                  }
-                } else if(browser.browser === 'Firefox') {
-                  if(_container) {
-                    _container.addError('', 'Click the reload button in the URL bar to change ' +
-                      'camera & mic settings.', 'OT_publisher-denied-firefox');
-                  }
-                  OT.Dialogs.AllowDeny.Firefox.denied().on({
-                    refresh: function() {
-                      window.location.reload();
-                    }
-                  });
-                }
-              }
-            };
-
-          this.dispatchEvent(event, defaultAction);
-        },
-
-        accessDialogPrompt,
-        accessDialogChromeTimeout,
-        accessDialogFirefoxTimeout,
+          this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DENIED));
+        },
+
         accessDialogWasOpened = false,
 
         onAccessDialogOpened = function() {
 
           accessDialogWasOpened = true;
 
           logAnalyticsEvent('accessDialog', 'Opened', '', '');
 
-          var browser = OT.$.browserVersion();
-
-          this.dispatchEvent(
-            new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true),
-            function(event) {
-              if(!event.isDefaultPrevented()) {
-                if(browser.browser === 'Chrome') {
-                  accessDialogChromeTimeout = setTimeout(function() {
-                    accessDialogChromeTimeout = null;
-                    logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Chrome');
-                    accessDialogPrompt = OT.Dialogs.AllowDeny.Chrome.initialPrompt();
-                    accessDialogPrompt.on('closeButtonClicked', function() {
-                      logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Chrome');
-                    });
-                  }, 5000);
-                } else if(browser.browser === 'Firefox') {
-                  accessDialogFirefoxTimeout = setTimeout(function() {
-                    accessDialogFirefoxTimeout = null;
-                    logAnalyticsEvent('allowDenyHelpers', 'show', 'version', 'Firefox');
-                    accessDialogPrompt = OT.Dialogs.AllowDeny.Firefox.maybeDenied();
-                    accessDialogPrompt.on('closeButtonClicked', function() {
-                      logAnalyticsEvent('allowDenyHelpers', 'dismissed', 'version', 'Firefox');
-                    });
-                  }, 7000);
-                }
-              } else {
-                logAnalyticsEvent('allowDenyHelpers', 'developerPrevented', '', '');
-              }
-            }
-          );
+          this.dispatchEvent(new OT.Event(OT.Event.names.ACCESS_DIALOG_OPENED, true));
         },
 
         onAccessDialogClosed = function() {
           logAnalyticsEvent('accessDialog', 'Closed', '', '');
 
-          if(accessDialogChromeTimeout) {
-            clearTimeout(accessDialogChromeTimeout);
-            logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Chrome');
-            accessDialogChromeTimeout = null;
-          }
-
-          if(accessDialogFirefoxTimeout) {
-            clearTimeout(accessDialogFirefoxTimeout);
-            logAnalyticsEvent('allowDenyHelpers', 'notShown', 'version', 'Firefox');
-            accessDialogFirefoxTimeout = null;
-          }
-
-          if(accessDialogPrompt) {
-            accessDialogPrompt.close();
-            var browser = OT.$.browserVersion();
-            logAnalyticsEvent('allowDenyHelpers', 'closed', 'version', browser.browser);
-            accessDialogPrompt = null;
-          }
-
-          this.dispatchEvent(
-            new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false)
-          );
+          this.dispatchEvent( new OT.Event(OT.Event.names.ACCESS_DIALOG_CLOSED, false));
         },
 
         onVideoError = function(errorCode, errorReason) {
           OT.error('OT.Publisher.onVideoError');
 
           var message = errorReason + (errorCode ? ' (' + errorCode + ')' : '');
           logAnalyticsEvent('stream', null, 'reason',
             'Publisher while playing stream: ' + message);
@@ -18155,79 +17990,79 @@ waitForDomReady();
               _chrome.muteButton.setDisplayMode(value);
               _chrome.backingBar.setMuteMode(value);
               break;
 
             case 'audioLevelDisplayMode':
               _chrome.audioLevel.setDisplayMode(value);
               break;
 
-            case 'bugDisplayMode':
-              // bugDisplayMode can't be updated but is used by some partners
-
             case 'backgroundImageURI':
               _container.setBackgroundImageURI(value);
           }
         },
 
         _createChrome = function() {
 
-          if(this.getStyle('bugDisplayMode') === 'off') {
-            logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off');
-          }
           if(!this.getStyle('showArchiveStatus')) {
             logAnalyticsEvent('showArchiveStatus', 'createChrome', 'mode', 'off');
           }
 
           var widgets = {
             backingBar: new OT.Chrome.BackingBar({
               nameMode: !_publishProperties.name ? 'off' : this.getStyle('nameDisplayMode'),
               muteMode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode'))
             }),
 
             name: new OT.Chrome.NamePanel({
               name: _publishProperties.name,
-              mode: this.getStyle('nameDisplayMode'),
-              bugMode: this.getStyle('bugDisplayMode')
+              mode: this.getStyle('nameDisplayMode')
             }),
 
             muteButton: new OT.Chrome.MuteButton({
               muted: _publishProperties.publishAudio === false,
               mode: chromeButtonMode.call(this, this.getStyle('buttonDisplayMode'))
             }),
 
-            opentokButton: new OT.Chrome.OpenTokButton({
-              mode: this.getStyle('bugDisplayMode')
-            }),
-
             archive: new OT.Chrome.Archiving({
               show: this.getStyle('showArchiveStatus'),
               archiving: false
             })
           };
 
-          if(_audioLevelCapable) {
+          if (_audioLevelCapable) {
+            var audioLevelTransformer = new OT.AudioLevelTransformer();
+
+            var audioLevelUpdatedHandler = function(evt) {
+              _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+            };
+
             _audioLevelMeter = new OT.Chrome.AudioLevelMeter({
-              mode: this.getStyle('audioLevelDisplayMode')
-            });
-
-            var audioLevelTransformer = new OT.AudioLevelTransformer();
-            this.on('audioLevelUpdated', function(evt) {
-              _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+              mode: this.getStyle('audioLevelDisplayMode'),
+              onActivate: function() {
+                _publisher.on('audioLevelUpdated', audioLevelUpdatedHandler);
+              },
+              onPassivate: function() {
+                _publisher.off('audioLevelUpdated', audioLevelUpdatedHandler);
+              }
             });
 
             widgets.audioLevel = _audioLevelMeter;
           }
 
           _chrome = new OT.Chrome({
             parent: _container.domElement
           }).set(widgets).on({
             muted: OT.$.bind(this.publishAudio, this, false),
             unmuted: OT.$.bind(this.publishAudio, this, true)
           });
+
+          if(_audioLevelMeter && this.getStyle('audioLevelDisplayMode') === 'auto') {
+            _audioLevelMeter[_container.audioOnly() ? 'show' : 'hide']();
+          }
         },
 
         reset = OT.$.bind(function() {
           if (_chrome) {
             _chrome.destroy();
             _chrome = null;
           }
 
@@ -18255,16 +18090,27 @@ waitForDomReady();
           this.stream = _stream = null;
           _loaded = false;
 
           this.session = _session = null;
 
           if (!_state.isDestroyed()) _state.set('NotPublishing');
         }, this);
 
+    var setAudioOnly = function(audioOnly) {
+      if (_container) {
+        _container.audioOnly(audioOnly);
+        _container.showPoster(audioOnly);
+      }
+
+      if (_audioLevelMeter && _publisher.getStyle('audioLevelDisplayMode') === 'auto') {
+        _audioLevelMeter[audioOnly ? 'show' : 'hide']();
+      }
+    };
+
     this.publish = function(targetElement, properties) {
       OT.debug('OT.Publisher: publish');
 
       if ( _state.isAttemptingToPublish() || _state.isPublishing() ) reset();
       _state.set('GetUserMedia');
 
       _publishProperties = OT.$.defaults(properties || {}, {
         publishAudio : true,
@@ -18452,18 +18298,17 @@ waitForDomReady();
 
       if (_session && _stream) {
         _stream.setChannelActiveState('audio', value);
       }
 
       return this;
     };
 
-
- /**
+    /**
   * Starts publishing video (if it is currently not being published)
   * when the <code>value</code> is <code>true</code>; stops publishing video
   * (if it is currently being published) when the <code>value</code> is <code>false</code>.
   *
   * @param {Boolean} value Whether to start publishing video (<code>true</code>)
   * or not (<code>false</code>).
   *
   * @see <a href="OT.html#initPublisher">OT.initPublisher()</a>
@@ -18485,20 +18330,17 @@ waitForDomReady();
       // the value of publishVideo at this point. This will be tidied up shortly.
       if (_webRTCStream) {
         var videoTracks = _webRTCStream.getVideoTracks();
         for (var i=0, num=videoTracks.length; i<num; ++i) {
           videoTracks[i].setEnabled(value);
         }
       }
 
-      if(_container) {
-        _container.audioOnly(!value);
-        _container.showPoster(!value);
-      }
+      setAudioOnly(!value);
 
       return this;
     };
 
 
     /**
     * Deletes the Publisher object and removes it from the HTML DOM.
     * <p>
@@ -18933,17 +18775,18 @@ waitForDomReady();
         _audioVolume = 100,
         _state,
         _prevStats,
         _lastSubscribeToVideoReason,
         _audioLevelCapable =  OT.$.hasCapabilities('audioOutputLevelStat') ||
                               OT.$.hasCapabilities('webAudioCapableRemoteStream'),
         _audioLevelSampler,
         _audioLevelRunner,
-        _frameRateRestricted = false;
+        _frameRateRestricted = false,
+        _subscriber = this;
 
     this.id = _domId;
     this.widgetId = _widgetId;
     this.session = _session;
 
     _prevStats = {
       timeStamp: OT.$.now()
     };
@@ -18976,18 +18819,17 @@ waitForDomReady();
 
     OT.StylableComponent(this, {
       nameDisplayMode: 'auto',
       buttonDisplayMode: 'auto',
       audioLevelDisplayMode: 'auto',
       videoDisabledIndicatorDisplayMode: 'auto',
       backgroundImageURI: null,
       showArchiveStatus: true,
-      showMicButton: true,
-      bugDisplayMode: 'auto'
+      showMicButton: true
     });
 
     var logAnalyticsEvent = function(action, variation, payloadType, payload) {
           /* jshint camelcase:false*/
           _analytics.logEvent({
             action: action,
             variation: variation,
             payload_type: payloadType,
@@ -19156,17 +18998,18 @@ waitForDomReady();
               width: _stream.videoDimensions.width,
               height: _stream.videoDimensions.height,
               videoOrientation: _stream.videoDimensions.orientation
             });
 
             onLoaded.call(this, null);
           }, this));
 
-          if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler) {
+          if (OT.$.hasCapabilities('webAudioCapableRemoteStream') && _audioLevelSampler &&
+            webOTStream.getAudioTracks().length > 0) {
             _audioLevelSampler.webOTStream = webOTStream;
           }
 
           logAnalyticsEvent('createPeerConnection', 'StreamAdded', '', '');
           this.trigger('streamAdded', this);
         },
 
         onRemoteStreamRemoved = function(webOTStream) {
@@ -19184,36 +19027,38 @@ waitForDomReady();
         streamDestroyed = function () {
           this.disconnect();
         },
 
         streamUpdated = function(event) {
 
           switch(event.changedProperty) {
             case 'videoDimensions':
+              if (!_streamContainer) {
+                // Ignore videoEmension updates before streamContainer is created OPENTOK-17253
+                break;
+              }
               _streamContainer.orientation({
                 width: event.newValue.width,
                 height: event.newValue.height,
                 videoOrientation: event.newValue.orientation
               });
               break;
 
             case 'videoDisableWarning':
               _chrome.videoDisabledIndicator.setWarning(event.newValue);
               this.dispatchEvent(new OT.VideoDisableWarningEvent(
                 event.newValue ? 'videoDisableWarning' : 'videoDisableWarningLifted'
               ));
               break;
 
             case 'hasVideo':
-              if(_container) {
-                var audioOnly = !(_stream.hasVideo && _properties.subscribeToVideo);
-                _container.audioOnly(audioOnly);
-                _container.showPoster(audioOnly);
-              }
+
+              setAudioOnly(!(_stream.hasVideo && _properties.subscribeToVideo));
+
               this.dispatchEvent(new OT.VideoEnabledChangedEvent(
                 _stream.hasVideo ? 'videoEnabled' : 'videoDisabled', {
                 reason: 'publishVideo'
               }));
               break;
 
             case 'hasAudio':
               // noop
@@ -19258,65 +19103,60 @@ waitForDomReady();
               _chrome.muteButton.setDisplayMode(value);
               _chrome.backingBar.setMuteMode(value);
               break;
 
             case 'audioLevelDisplayMode':
               _chrome.audioLevel.setDisplayMode(value);
               break;
 
-            case 'bugDisplayMode':
-              // bugDisplayMode can't be updated but is used by some partners
-
             case 'backgroundImageURI':
               _container.setBackgroundImageURI(value);
           }
         },
 
         _createChrome = function() {
-          
-          if(this.getStyle('bugDisplayMode') === 'off') {
-            logAnalyticsEvent('bugDisplayMode', 'createChrome', 'mode', 'off');
-          }
 
           var widgets = {
             backingBar: new OT.Chrome.BackingBar({
               nameMode: !_properties.name ? 'off' : this.getStyle('nameDisplayMode'),
               muteMode: chromeButtonMode.call(this, this.getStyle('showMuteButton'))
             }),
 
             name: new OT.Chrome.NamePanel({
               name: _properties.name,
-              mode: this.getStyle('nameDisplayMode'),
-              bugMode: this.getStyle('bugDisplayMode')
+              mode: this.getStyle('nameDisplayMode')
             }),
 
             muteButton: new OT.Chrome.MuteButton({
               muted: _properties.muted,
               mode: chromeButtonMode.call(this, this.getStyle('showMuteButton'))
             }),
 
-            opentokButton: new OT.Chrome.OpenTokButton({
-              mode: this.getStyle('bugDisplayMode')
-            }),
-
             archive: new OT.Chrome.Archiving({
               show: this.getStyle('showArchiveStatus'),
               archiving: false
             })
           };
 
-          if(_audioLevelCapable) {
+          if (_audioLevelCapable) {
+            var audioLevelTransformer = new OT.AudioLevelTransformer();
+
+            var audioLevelUpdatedHandler = function(evt) {
+              _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+            };
+
             _audioLevelMeter = new OT.Chrome.AudioLevelMeter({
-              mode: this.getStyle('audioLevelDisplayMode')
-            });
-
-            var audioLevelTransformer = new OT.AudioLevelTransformer();
-            this.on('audioLevelUpdated', function(evt) {
-              _audioLevelMeter.setValue(audioLevelTransformer.transform(evt.audioLevel));
+              mode: this.getStyle('audioLevelDisplayMode'),
+              onActivate: function() {
+                _subscriber.on('audioLevelUpdated', audioLevelUpdatedHandler);
+              },
+              onPassivate: function() {
+                _subscriber.off('audioLevelUpdated', audioLevelUpdatedHandler);
+              }
             });
 
             widgets.audioLevel = _audioLevelMeter;
           }
 
           widgets.videoDisabledIndicator = new OT.Chrome.VideoDisabledIndicator({
             mode: this.getStyle('videoDisabledDisplayMode')
           });
@@ -19327,29 +19167,43 @@ waitForDomReady();
             muted: function() {
               muteAudio.call(this, true);
             },
 
             unmuted: function() {
               muteAudio.call(this, false);
             }
           }, this);
+
+          if(_audioLevelMeter && this.getStyle('audioLevelDisplayMode') === 'auto') {
+            _audioLevelMeter[_container.audioOnly() ? 'show' : 'hide']();
+          }
         },
 
         _showError = function() {
           // Display the error message inside the container, assuming it's
           // been created by now.
           if (_container) {
             _container.addError(
               'The stream was unable to connect due to a network error.',
               'Make sure your connection isn\'t blocked by a firewall.'
             );
           }
         };
 
+    var setAudioOnly = function(audioOnly) {
+      if(_container) {
+        _container.audioOnly(audioOnly);
+        _container.showPoster(audioOnly);
+      }
+
+      if (_audioLevelMeter && _subscriber.getStyle('audioLevelDisplayMode') === 'auto') {
+        _audioLevelMeter[audioOnly ? 'show' : 'hide']();
+      }
+    };
 
     this.subscribe = function(stream) {
       OT.debug('OT.Subscriber: subscribe to ' + stream.id);
 
       if (_state.isSubscribing()) {
         // @todo error
         OT.error('OT.Subscriber.Subscribe: Cannot subscribe, already subscribing.');
         return false;
@@ -19413,28 +19267,27 @@ waitForDomReady();
         }, this);
 
         // initialize the peer connection AFTER we've added the event listeners
         _peerConnection.init();
 
         if (OT.$.hasCapabilities('audioOutputLevelStat')) {
           _audioLevelSampler = new OT.GetStatsAudioLevelSampler(_peerConnection, 'out');
         } else if (OT.$.hasCapabilities('webAudioCapableRemoteStream')) {
-          _audioLevelSampler = new OT.AnalyserAudioLevelSampler(new window.AudioContext());
+          _audioLevelSampler = new OT.AnalyserAudioLevelSampler(OT.audioContext());
         }
 
         if(_audioLevelSampler) {
-          var subscriber = this;
           // sample with interval to minimise disturbance on animation loop but dispatch the
           // event with RAF since the main purpose is animation of a meter
           _audioLevelRunner = new OT.IntervalRunner(function() {
             _audioLevelSampler.sample(function(audioOutputLevel) {
               if (audioOutputLevel !== null) {
                 OT.$.requestAnimationFrame(function() {
-                  subscriber.dispatchEvent(
+                  _subscriber.dispatchEvent(
                     new OT.AudioLevelUpdatedEvent(audioOutputLevel));
                 });
               }
             });
           }, 60);
         }
       } else {
         logAnalyticsEvent('createPeerConnection', 'Attempt', '', '');
@@ -19744,26 +19597,23 @@ waitForDomReady();
     * @see <a href="StreamPropertyChangedEvent.html">StreamPropertyChangedEvent</a>
     *
     * @method #subscribeToVideo
     * @memberOf Subscriber
     */
     this.subscribeToVideo = function(pValue, reason) {
       var value = OT.$.castToBoolean(pValue, true);
 
-      if(_container) {
-        var audioOnly = !(value && _stream.hasVideo);
-        _container.audioOnly(audioOnly);
-        _container.showPoster(audioOnly);
-        if(value && _container.video()) {
-          _container.loading(value);
-          _container.video().whenTimeIncrements(function(){
-            _container.loading(false);
-          }, this);
-        }
+      setAudioOnly(!(value && _stream.hasVideo));
+
+      if ( value && _container  && _container.video()) {
+        _container.loading(value);
+        _container.video().whenTimeIncrements(function() {
+          _container.loading(false);
+        }, this);
       }
 
       if (_chrome && _chrome.videoDisabledIndicator) {
         _chrome.videoDisabledIndicator.disableVideo(false);
       }
 
       if (_peerConnection) {
         _peerConnection.subscribeToVideo(value);
--- a/browser/config/mozconfigs/win64/l10n-mozconfig
+++ b/browser/config/mozconfigs/win64/l10n-mozconfig
@@ -1,9 +1,10 @@
 . "$topsrcdir/browser/config/mozconfigs/common"
+. "$topsrcdir/browser/config/mozconfigs/win64/common-win64"
 
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-update-packaging
 ac_add_options --with-l10n-base=../../l10n
 ac_add_options --with-windows-version=603
 
 export MOZILLA_OFFICIAL=1
 
--- a/browser/devtools/canvasdebugger/canvasdebugger.js
+++ b/browser/devtools/canvasdebugger/canvasdebugger.js
@@ -696,17 +696,17 @@ let CallsListView = Heritage.extend(Widg
 
     let screenshotNode = $("#screenshot-image");
     screenshotNode.setAttribute("flipped", flipped);
     drawBackground("screenshot-rendering", width, height, pixels);
 
     let dimensionsNode = $("#screenshot-dimensions");
     let actualWidth = (width / scaling) | 0;
     let actualHeight = (height / scaling) | 0;
-    dimensionsNode.setAttribute("value", actualWidth + " x " + actualHeight);
+    dimensionsNode.setAttribute("value", actualWidth + " \u00D7 " + actualHeight);
 
     window.emit(EVENTS.CALL_SCREENSHOT_DISPLAYED);
   },
 
   /**
    * Populates this container's footer with a list of thumbnails, one generated
    * for each draw call in the recorded animation frame snapshot.
    *
--- a/browser/devtools/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
+++ b/browser/devtools/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
@@ -15,17 +15,17 @@ function ifTestingSupported() {
   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
   SnapshotsListView._onRecordButtonClick();
   yield promise.all([recordingFinished, callListPopulated, screenshotDisplayed]);
 
   is($("#screenshot-container").hidden, false,
     "The screenshot container should now be visible.");
 
-  is($("#screenshot-dimensions").getAttribute("value"), "128 x 128",
+  is($("#screenshot-dimensions").getAttribute("value"), "128" + " \u00D7 " + "128",
     "The screenshot dimensions label has the expected value.");
 
   is($("#screenshot-image").getAttribute("flipped"), "false",
     "The screenshot element should not be flipped vertically.");
 
   ok(window.getComputedStyle($("#screenshot-image")).backgroundImage.contains("#screenshot-rendering"),
     "The screenshot element should have an offscreen canvas element as a background.");
 
--- a/browser/devtools/inspector/test/browser_inspector_infobar_01.js
+++ b/browser/devtools/inspector/test/browser_inspector_infobar_01.js
@@ -15,33 +15,33 @@ add_task(function*() {
 
   let testData = [
     {
       selector: "#top",
       position: "bottom",
       tag: "DIV",
       id: "top",
       classes: ".class1.class2",
-      dims: "500 x 100"
+      dims: "500" + " \u00D7 " + "100"
     },
     {
       selector: "#vertical",
       position: "overlap",
       tag: "DIV",
       id: "vertical",
       classes: ""
       // No dims as they will vary between computers
     },
     {
       selector: "#bottom",
       position: "top",
       tag: "DIV",
       id: "bottom",
       classes: "",
-      dims: "500 x 100"
+      dims: "500" + " \u00D7 " + "100"
     },
     {
       selector: "body",
       position: "bottom",
       tag: "BODY",
       classes: ""
       // No dims as they will vary between computers
     },
--- a/browser/devtools/layoutview/test/browser_layoutview.js
+++ b/browser/devtools/layoutview/test/browser_layoutview.js
@@ -4,35 +4,35 @@
 
 "use strict";
 
 // Test that the layout-view displays the right values and that it updates when
 // the node's style is changed
 
 // Expected values:
 let res1 = [
-      {selector: "#element-size",              value: "160x160"},
-      {selector: ".size > span",               value: "100x100"},
+      {selector: "#element-size",              value: "160" + "\u00D7" + "160"},
+      {selector: ".size > span",               value: "100" + "\u00D7" + "100"},
       {selector: ".margin.top > span",         value: 30},
       {selector: ".margin.left > span",        value: "auto"},
       {selector: ".margin.bottom > span",      value: 30},
       {selector: ".margin.right > span",       value: "auto"},
       {selector: ".padding.top > span",        value: 20},
       {selector: ".padding.left > span",       value: 20},
       {selector: ".padding.bottom > span",     value: 20},
       {selector: ".padding.right > span",      value: 20},
       {selector: ".border.top > span",         value: 10},
       {selector: ".border.left > span",        value: 10},
       {selector: ".border.bottom > span",      value: 10},
       {selector: ".border.right > span",       value: 10},
 ];
 
 let res2 = [
-      {selector: "#element-size",              value: "190x210"},
-      {selector: ".size > span",               value: "100x150"},
+      {selector: "#element-size",              value: "190" + "\u00D7" + "210"},
+      {selector: ".size > span",               value: "100" + "\u00D7" + "150"},
       {selector: ".margin.top > span",         value: 30},
       {selector: ".margin.left > span",        value: "auto"},
       {selector: ".margin.bottom > span",      value: 30},
       {selector: ".margin.right > span",       value: "auto"},
       {selector: ".padding.top > span",        value: 20},
       {selector: ".padding.left > span",       value: 20},
       {selector: ".padding.bottom > span",     value: 20},
       {selector: ".padding.right > span",      value: 50},
--- a/browser/devtools/layoutview/test/browser_layoutview_update-after-navigation.js
+++ b/browser/devtools/layoutview/test/browser_layoutview_update-after-navigation.js
@@ -40,26 +40,26 @@ function*(inspector, view) {
 
 addTest("Test that the layout-view works on the second page",
 function*(inspector, view) {
   info("Selecting the test node");
   yield selectNode("p", inspector);
 
   info("Checking that the layout-view shows the right value");
   let sizeElt = view.doc.querySelector(".size > span");
-  is(sizeElt.textContent, "100x100");
+  is(sizeElt.textContent, "100" + "\u00D7" + "100");
 
   info("Listening for layout-view changes and modifying the size");
   let onUpdated = waitForUpdate(inspector);
   getNode("p").style.width = "200px";
   yield onUpdated;
   ok(true, "Layout-view got updated");
 
   info("Checking that the layout-view shows the right value after update");
-  is(sizeElt.textContent, "200x100");
+  is(sizeElt.textContent, "200" + "\u00D7" + "100");
 });
 
 addTest("Go back to the first page",
 function*(inspector, view) {
   content.history.back();
   yield inspector.once("markuploaded");
 });
 
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -396,17 +396,17 @@ LayoutView.prototype = {
       // If a subsequent request has been made, wait for that one instead.
       if (this._lastRequest != lastRequest) {
         return this._lastRequest;
       }
 
       this._lastRequest = null;
       let width = layout.width;
       let height = layout.height;
-      let newLabel = width + "x" + height;
+      let newLabel = width + "\u00D7" + height;
       if (this.sizeHeadingLabel.textContent != newLabel) {
         this.sizeHeadingLabel.textContent = newLabel;
       }
 
       // If the view is dimmed, no need to do anything more.
       if (this.dimmed) {
         this.inspector.emit("layoutview-updated");
         return null;
@@ -447,17 +447,17 @@ LayoutView.prototype = {
       }
 
       width -= this.map.borderLeft.value + this.map.borderRight.value +
                this.map.paddingLeft.value + this.map.paddingRight.value;
 
       height -= this.map.borderTop.value + this.map.borderBottom.value +
                 this.map.paddingTop.value + this.map.paddingBottom.value;
 
-      let newValue = width + "x" + height;
+      let newValue = width + "\u00D7" + height;
       if (this.sizeLabel.textContent != newValue) {
         this.sizeLabel.textContent = newValue;
       }
 
       this.elementRules = [e.rule for (e of styleEntries)];
 
       this.inspector.emit("layoutview-updated");
     }).bind(this)).then(null, console.error);
--- a/browser/devtools/markupview/test/browser_markupview_image_tooltip.js
+++ b/browser/devtools/markupview/test/browser_markupview_image_tooltip.js
@@ -11,20 +11,20 @@
 const PAGE_CONTENT = [
   '<img class="local" src="chrome://branding/content/about-logo.png" />',
   '<img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADI5JREFUeNrsWwuQFNUVPf1m5z87szv7HWSWj8CigBFMEFZKiQsB1PgJwUAZg1HBpIQsKmokEhNjWUnFVPnDWBT+KolJYbRMoqUVq0yCClpqiX8sCchPWFwVlt2db7+X93pez7zu6Vn2NxsVWh8987p7pu+9555z7+tZjTGGY3kjOMa34w447oBjfKsY7i/UNM3Y8eFSAkD50Plgw03K5P9gvGv7U5ieeR3PszeREiPNX3/0DL4hjslzhm8THh+OITfXk3dhiv4GDtGPVzCaeJmPLYzuu5qJuWfuw2QTlcN1X9pwQU7LhdZ/ZAseD45cOh9hHvDkc/yAF/DNhdb5Mrr3PvBMaAYW8fMSIi2G497IMEK/YutGtAYr6+ej+nxu/NN8Ks3N7AR6HgcLz0Eg1Ljg1UcxZzi5qewIkMYLRweTr2Kzp+nmyXAd5pS3XQDd+N/4h4zgu9FI7brlXf90nMEnuwQxlvv+hosE3TuexmWeysmT4W+WxkMaLzf9Y8ATgjcUn7T9H1gqrpFq8eV1gMn6t16NhngjfoX6q4DUP032Rd4LJgpSLwJ1yzFqBG69eRkah0MVyo0Acfe+yy9AG4nMiYCkeM53KKFXncBLAXqEm+wCqZwaueq7WCmuLTcKSJmj737ol2hurA9eq9VdyiO8yWa3NNyog+SB5CZodSsQq/dfu34tJpYbBaTMzvVddDZu16q5smXf4G8zEvqm4cyaAmJPuTJk3oJWdS4WzcVtfMZbThSQckb/pYfRGgo3zNOqZnEHbJPGK4abaDCQIIsT8V/qTaBqHkLh6LzXH8XZQhbLhYKyyCC/WeHYcNdmvOgfe8skzbWL270/T3wf7tSx/lGCbTu8xlzzmCSWLc5iwmgikcCHi3Mga0Ry913vBFvQwg90l6M4ImWKfsWOp7DSWxmfpPlCFuPFfsNfKrCnPYpQKIRgqBK7D0SxYaNHwkEiJMtl0ReDp3Lc5D3PGoTo/sKngCl7a5chFqvBatKwjBd7WwqIlzB/78NcoUcp5VSgGxm+7b8eqQRGnHMO634epO4S1EZww09/iFg5UmGoESDuznP1xVhTUX1WWHPzjpd25wyH0hRxI3LGM75nxmuNEEUVpAN0XgxmPoKralakbQnWlIMQyVBD/w+3orkq4lvualjKyWwzt4MaxqspQHVhPOWG64bxYuhZXSFGWhipbSDVragOu5Y9eAsmDDUKyBA703vemVhHoueD6e9wAzJK1WfmN0Umk5GGM4kEMZcuIECqgjm0nldAqmbjwtm4VxZH5AvlADP6mx9Eqy9Q0+KqW8Ch+47FaMMYmnNGfY1iPMshoC6qFxme4wQ+0p+ARE6H3+9veWEDWgUhDhUKyFARn4jM5BNxT0XsMg7bfymGK1ov3wtjDfhL4w0HVGUVBEjDaaE+QNdrcNWch1PG4W6xrjBUXECGivg++Cva3JUT4iQUz3V2RsSVaKLwOuDT89A3HdBQoxhNC+fnVm74ual2EG893P6G+PuP4SfiO4cCBWQooL9qCWKNXPbcI37Aa/lnlZxXRt4RFONGwSDCPAHqOuqjWct1QiEMw5mChM5X4K47FyNqcd3aK9AwFH0CGYLoe1ctxk2eWi57rg5JfGp9rzC6ggCdFlAgHBDw5Yxlcg6G8SyHCjMlsgmDD9zhSeHlF+JnAgWDTQUy2NxfdwOao1UVV3pi3+bE97YSbWpLAbn6zefHNQkp1PMpIBwwvslKgIYTKM2nEpNzrGcH3FXTEal0L38kJ4uDQgEZbO4vnI173LXf5NHZaiUxtaCxyZuo/rK6LpUg54yg3zTWRAArvDcRIPZ6BqzrQ1REpmL+DNw32OKIDCb3X1qPVn8wNNMT4w2bvs+q4bAZrqBh2skaL3yyhhIIZ4i6oHkUK0RckcB8GigEyRIH4A6Mgc8fatl0/+BkkQxC9gIT4ljna1rIZW9rEdNbjJcNjsnoYj7LHWCUwpITzEgzRQKZ3XAFHbTzA3hrz8TEUUZxFBhoKpABQt/97p+w0hMZG68I8R6FtlsJT3FELndZntjM+VMnylKYq8GJI3UZaRMpquGSGFVOEfv0YZBMNzz+uvjbfzS6xQERIhlI9FcvQWNdFVb7x1zCb+QNK8vb9NsiifmI5hBgVoOCBC1sb0ab5RomqENxLO3eA1/0NDRU47q2RQNbRCUDIb7lF2CNL3ZGxEV4n08TVvZWYG4pZyV0zUdS45tyCBByOHWiyvZmxFXDCyRo1ge5+Sy0TA+8lWMiP/6O0S32exGV9Jf4fr8azdUR3zL/CZz4MtvzdX5uOYs6NDOmpkuj5Huh+7qUQSYl0ThHzw0YQzcGo6bhzEqoYq5rN3yRiYiG3Vfe2Ybm/qKA9NNZ3nNm4F7/yDkg9AN+U1mHiBcXP8zuDN76jj8hg1QyiWQigalj02BJPhK8I0zxijAjhp5zhlpLUDvS+BCy2HMAvvB4XDgL9/SXC0g/ou/5+6/xLX8w0uJrOIkXfPvyhY0F6gr7M8H0KWFYikcqAXakB+xwD9CdREBLoau7Gz3cAdSIdLFxFtJTCqRChSjnutvhDcREtzjz2Tswtz+yeNRFUeXZXtWux7C1fuoVcbd3J//ipDX3uZZDLGrwweS+UBLL5TDliVBnF8P7H+XI8aRRGsIBJg/Zlslt1+W+D1JWoSyi+kD9jfhs78t7mhZhSl+fLfY1Bdyv3I8V/qpY3B1McgN7ZFT5/vNO0I5DPLLdPBIJA8qc4h2I0QplYfDpJwHT+aj0246r5S8rToG8OjCle8wk4OLvvYGa+Ovr84uo2qBSwJS9G5egoZFLTfiEqWDtbwGfHgKOdPHcS+ai7XDzMPW/FJRLGGcxnBbK4YJC2K+h+T6Bdu5CqHqCWERd3bawb7JI+iJ735+LNaHaprBLLHBm08U3XxShEsdt+f3eTh3v7aC95Dct4RCWL5OZWh/oXBZThxAIxyOXLzBk8aiEWJID8rK3CpPOmeHaGpvCS+7EHv5FujVHUSJPLXvIFeHcNc+9xrB2gws9KZdxuLFax/WLM5gzzSm/lTXF/OdAcapyvjxPqxqHjr2v4ckX2bS2dRBrc5lSdpKjEJ9/9tdwX2WMd53ZQ2IVo3RES+UwVSpCPvYepNx4gmTGDUKIMQ4eduPnD7mx9xOn/KZKOlFbStjONxHTtR+BYAPmnoZ1Zp8wkBRwP/EL3u0F/C2hGl7vpz7vW37T3vP7if8wroKuoh8ribknX9BK5rcF+mo1qKaKyRPJTgTDjbzY8szcuLb3bpH00u35T47j7prRpwDJTxzyG0dHgxPp5bPG8VdkpfPbUg3SgoOo2mwVukb98D5EqpswZTTulCggTk4gpYhv0++wIhCJxr0+Hq1sondis0SE2oxQe3qWXwWyO4DSQg9gJ8Iiw1VFcGqXxet0N9xE4ygIxv/9W6wo9WyROEX/R+eiobYSq2vHTOR631Eiv2lRfh9dvxkumkXh92Qsx8XrAJ+7YGbWuhxOi/U+31NQmzyqNYG8N/3wfo6CRtRHcN01FzkvojohwLu0VVvDa56IS/xcj2b7nN+O+m0jqpE1wMPXZxAN9iCVThtDvH7gmiRGRpU8Lspv1Uhq4wIVdQoyuGSLNYPKUCS8+CzNURbzMmjK3i8u0U793lmuV0ef9nWQ5MGC/DiUqEUSaCtXna9RJEspZS1lrXINK/pcq+SpT50t98QKMq1FRmDfx3vxty102k0PM4ssEnvuz5+G26Ij4yDpz6z9fV8bkyIkqBFkhej0Ib+ZQ34XJK9AfozaiimqIoX3Jp3tiISrcfYpuN2+iFph/02P36PNC9fVcCnp6H9jYouKyfaWufz5Tp9tVxcUniw7IohZv4dZz81/ns67z3AYPrc2n0+Ix2q8k0PWjgBy88XaibnfK9A+5LdDY2Ivhy36fbT8Zv3Lb1U1qLqUxorXEEXIs0mjjrtxoTZWtdvigNs2sgPiujTv6DIZLld6b/V5742JZV3fUsUVFy5gdsNtKWFzUCEVbNepD1MkSMVbsb6SZm7jI3/zODtQKgUMsOw8wDZ63t5xcV1TnaEAxoc6wrqY+Fj+N4DsqOnhOIdicrQSm1MPYCPlIqHn5bbHg8/bj2D3QfZnCX3mpAICDZV8jH5kpbZqTD0W+DxaA74CWzLN2nd14OlL72J38Lf7+TjC7dadZFDoZJQPrtaIKL/G0L6ktptPZVJ8fMqHYPZOKYPMyQGadIJfDvdXwAFiZOTvDBPydf5vk4rWA+RfdhBlaF/yDDBRoMu9pfnSjv/p7DG+HXfAcQcc49v/BBgAcFAO4DmB2GQAAAAASUVORK5CYII=" />',
   '<img class="remote" src="' + TEST_URL_ROOT + 'doc_markup_tooltip.png" />',
   '<canvas class="canvas" width="600" height="600"></canvas>'
 ].join("\n");
 
 const TEST_NODES = [
-  {selector: "img.local", size: "192 x 192"},
-  {selector: "img.data", size: "64 x 64"},
-  {selector: "img.remote", size: "22 x 23"},
-  {selector: ".canvas", size: "600 x 600"}
+  {selector: "img.local", size: "192" + " \u00D7 " + "192"},
+  {selector: "img.data", size: "64" + " \u00D7 " + "64"},
+  {selector: "img.remote", size: "22" + " \u00D7 " + "23"},
+  {selector: ".canvas", size: "600" + " \u00D7 " + "600"}
 ];
 
 add_task(function*() {
   yield addTab("data:text/html,markup view tooltip test");
   createPage();
 
   let {inspector} = yield openInspector();
 
--- a/browser/devtools/netmonitor/netmonitor-view.js
+++ b/browser/devtools/netmonitor/netmonitor-view.js
@@ -1678,17 +1678,17 @@ RequestsMenuView.prototype = Heritage.ex
   },
 
   /**
    * A handler that opens the security tab in the details view if secure or
    * broken security indicator is clicked.
    */
   _onSecurityIconClick: function(e) {
     let state = this.selectedItem.attachment.securityState;
-    if (state === "broken" || state === "secure") {
+    if (state !== "insecure") {
       // Choose the security tab.
       NetMonitorView.NetworkDetails.widget.selectedIndex = 5;
     }
   },
 
   /**
    * The resize listener for this container's window.
    */
@@ -2600,17 +2600,17 @@ NetworkDetailsView.prototype = {
       $("#response-content-image-encoding-value").setAttribute("value", encoding);
 
       // Wait for the image to load in order to display the width and height.
       $("#response-content-image").onload = e => {
         // XUL images are majestic so they don't bother storing their dimensions
         // in width and height attributes like the rest of the folk. Hack around
         // this by getting the bounding client rect and subtracting the margins.
         let { width, height } = e.target.getBoundingClientRect();
-        let dimensions = (width - 2) + " x " + (height - 2);
+        let dimensions = (width - 2) + " \u00D7 " + (height - 2);
         $("#response-content-image-dimensions-value").setAttribute("value", dimensions);
       };
     }
     // Handle anything else.
     else {
       $("#response-content-textarea-box").hidden = false;
       let editor = yield NetMonitorView.editor("#response-content-textarea");
       editor.setMode(Editor.modes.text);
@@ -2760,20 +2760,32 @@ NetworkDetailsView.prototype = {
         label.value = value;
         label.setAttribute("tooltiptext", value);
       }
     }
 
     let errorbox = $("#security-error");
     let infobox = $("#security-information");
 
-    if (securityInfo.state === "secure") {
+    if (securityInfo.state === "secure" || securityInfo.state === "weak") {
       infobox.hidden = false;
       errorbox.hidden = true;
 
+      // Warning icons
+      let cipher = $("#security-warning-cipher");
+      let sslv3 = $("#security-warning-sslv3");
+
+      if (securityInfo.state === "weak") {
+        cipher.hidden = securityInfo.weaknessReasons.indexOf("cipher") === -1;
+        sslv3.hidden = securityInfo.weaknessReasons.indexOf("sslv3") === -1;
+      } else {
+        cipher.hidden = true;
+        sslv3.hidden = true;
+      }
+
       let enabledLabel = L10N.getStr("netmonitor.security.enabled");
       let disabledLabel = L10N.getStr("netmonitor.security.disabled");
 
       // Connection parameters
       setLabel("#security-protocol-version-value", securityInfo.protocolVersion);
       setLabel("#security-ciphersuite-value", securityInfo.cipherSuite);
 
       // Host header
--- a/browser/devtools/netmonitor/netmonitor.xul
+++ b/browser/devtools/netmonitor/netmonitor.xul
@@ -501,26 +501,32 @@
                               class="tabpanel-summary-container"
                               align="center">
                           <label class="plain tabpanel-summary-label"
                                  value="&netmonitorUI.security.protocolVersion;"/>
                           <label id="security-protocol-version-value"
                                  class="plain tabpanel-summary-value devtools-monospace"
                                  crop="end"
                                  flex="1"/>
+                          <image class="security-warning-icon"
+                                 id="security-warning-sslv3"
+                                 tooltiptext="&netmonitorUI.security.warning.sslv3;" />
                         </hbox>
                         <hbox id="security-ciphersuite"
                               class="tabpanel-summary-container"
                               align="center">
                           <label class="plain tabpanel-summary-label"
                                  value="&netmonitorUI.security.cipherSuite;"/>
                           <label id="security-ciphersuite-value"
                                  class="plain tabpanel-summary-value devtools-monospace"
                                  crop="end"
                                  flex="1"/>
+                          <image class="security-warning-icon"
+                                 id="security-warning-cipher"
+                                 tooltiptext="&netmonitorUI.security.warning.cipher;" />
                         </hbox>
                       </vbox>
                     </vbox>
                     <vbox id="security-info-domain"
                           class="tabpanel-summary-container">
                       <label class="plain tabpanel-summary-label"
                              id="security-info-host-header"/>
                       <vbox class="security-info-section">
--- a/browser/devtools/netmonitor/test/browser.ini
+++ b/browser/devtools/netmonitor/test/browser.ini
@@ -87,16 +87,17 @@ skip-if = e10s # Bug 1091603
 skip-if = e10s # Bug 1091612
 [browser_net_security-details.js]
 [browser_net_security-error.js]
 [browser_net_security-icon-click.js]
 [browser_net_security-redirect.js]
 [browser_net_security-state.js]
 [browser_net_security-tab-deselect.js]
 [browser_net_security-tab-visibility.js]
+[browser_net_security-warnings.js]
 [browser_net_simple-init.js]
 [browser_net_simple-request-data.js]
 [browser_net_simple-request-details.js]
 [browser_net_simple-request.js]
 [browser_net_sort-01.js]
 [browser_net_sort-02.js]
 [browser_net_sort-03.js]
 [browser_net_statistics-01.js]
--- a/browser/devtools/netmonitor/test/browser_net_content-type.js
+++ b/browser/devtools/netmonitor/test/browser_net_content-type.js
@@ -203,17 +203,17 @@ function test() {
                 "The image name info isn't correct.");
               is(tabpanel.querySelector("#response-content-image-mime-value")
                 .getAttribute("value"), "image/png",
                 "The image mime info isn't correct.");
               is(tabpanel.querySelector("#response-content-image-encoding-value")
                 .getAttribute("value"), "base64",
                 "The image encoding info isn't correct.");
               is(tabpanel.querySelector("#response-content-image-dimensions-value")
-                .getAttribute("value"), "16 x 16",
+                .getAttribute("value"), "16" + " \u00D7 " + "16",
                 "The image dimensions info isn't correct.");
 
               deferred.resolve();
             });
 
             return deferred.promise;
           }
         }
--- a/browser/devtools/netmonitor/test/browser_net_security-state.js
+++ b/browser/devtools/netmonitor/test/browser_net_security-state.js
@@ -8,16 +8,17 @@
  * state.
  */
 
 add_task(function* () {
   const EXPECTED_SECURITY_STATES = {
     "test1.example.com": "security-state-insecure",
     "example.com": "security-state-secure",
     "nocert.example.com": "security-state-broken",
+    "rc4.example.com": "security-state-weak",
   };
 
   let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
   let { $, EVENTS, NetMonitorView } = monitor.panelWin;
   let { RequestsMenu } = NetMonitorView;
   RequestsMenu.lazyUpdate = false;
 
   yield performRequests();
@@ -65,17 +66,22 @@ add_task(function* () {
     debuggee.performRequests(1, "http://test1.example.com" + CORS_SJS_PATH);
     yield done;
 
     done = waitForNetworkEvents(monitor, 1);
     info("Requesting a resource over HTTPS.");
     debuggee.performRequests(1, "https://example.com" + CORS_SJS_PATH);
     yield done;
 
-    is(RequestsMenu.itemCount, 3, "Three events logged.");
+    done = waitForNetworkEvents(monitor, 1);
+    info("Requesting a resource over HTTPS with RC4.");
+    debuggee.performRequests(1, "https://rc4.example.com" + CORS_SJS_PATH);
+    yield done;
+
+    is(RequestsMenu.itemCount, 4, "Four events logged.");
   }
 
   /**
    * Returns a promise that's resolved once a request with security issues is
    * completed.
    */
   function waitForSecurityBrokenNetworkEvent() {
     let awaitedEvents = [
new file mode 100644
--- /dev/null
+++ b/browser/devtools/netmonitor/test/browser_net_security-warnings.js
@@ -0,0 +1,81 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/**
+ * Test that warning indicators are shown when appropriate.
+ */
+
+const TEST_CASES = [
+  {
+    desc: "no warnings",
+    uri: "https://example.com" + CORS_SJS_PATH,
+    warnCipher: false,
+    warnSSLv3: false,
+  },
+  {
+    desc: "sslv3 warning",
+    uri: "https://ssl3.example.com" + CORS_SJS_PATH,
+    warnCipher: false,
+    warnSSLv3: true,
+  },
+  {
+    desc: "cipher warning",
+    uri: "https://rc4.example.com" + CORS_SJS_PATH,
+    warnCipher: true,
+    warnSSLv3: false,
+  },
+  {
+    desc: "cipher and sslv3 warning",
+    uri: "https://ssl3rc4.example.com" + CORS_SJS_PATH,
+    warnCipher: true,
+    warnSSLv3: true,
+  },
+];
+
+add_task(function* () {
+  let [tab, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
+  let { $, EVENTS, NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu, NetworkDetails } = NetMonitorView;
+  RequestsMenu.lazyUpdate = false;
+
+  info("Enabling SSLv3 for the test.");
+  yield new promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [["security.tls.version.min", 0]]}, resolve);
+  });
+
+  let cipher = $("#security-warning-cipher");
+  let sslv3 = $("#security-warning-sslv3");
+
+  for (let test of TEST_CASES) {
+    info("Testing site with " + test.desc);
+
+    info("Performing request to " + test.uri);
+    debuggee.performRequests(1, test.uri);
+    yield waitForNetworkEvents(monitor, 1);
+
+    info("Selecting the request.");
+    RequestsMenu.selectedIndex = 0;
+
+    info("Waiting for details pane to be updated.");
+    yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
+
+    if (NetworkDetails.widget.selectedIndex !== 5) {
+      info("Selecting security tab.");
+      NetworkDetails.widget.selectedIndex = 5;
+
+      info("Waiting for details pane to be updated.");
+      yield monitor.panelWin.once(EVENTS.TAB_UPDATED);
+    }
+
+    is(cipher.hidden, !test.warnCipher, "Cipher suite warning is hidden.");
+    is(sslv3.hidden, !test.warnSSLv3, "SSLv3 warning is hidden.");
+
+    RequestsMenu.clear();
+
+  }
+
+  yield teardown(monitor);
+
+});
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -599,17 +599,17 @@ ResponsiveUI.prototype = {
 
   /**
    * Set the menuitem label of a preset.
    *
    * @param aMenuitem menuitem to edit.
    * @param aPreset associated preset.
    */
   setMenuLabel: function RUI_setMenuLabel(aMenuitem, aPreset) {
-    let size = Math.round(aPreset.width) + "x" + Math.round(aPreset.height);
+    let size = Math.round(aPreset.width) + "\u00D7" + Math.round(aPreset.height);
 
     // .inputField might be not reachable yet (async XBL loading)
     if (this.menulist.inputField) {
       this.menulist.inputField.value = size;
     }
 
     if (aPreset.custom) {
       size = this.strings.formatStringFromName("responsiveUI.customResolution", [size], 1);
--- a/browser/devtools/responsivedesign/test/browser_responsiveui.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -205,17 +205,17 @@ function test() {
 
 
     // Invalid input
 
 
     initialWidth = content.innerWidth;
     initialHeight = content.innerHeight;
     index = instance.menulist.selectedIndex;
-    let expectedValue = initialWidth + "x" + initialHeight;
+    let expectedValue = initialWidth + "\u00D7" + initialHeight;
     let expectedLabel = instance.menulist.firstChild.firstChild.getAttribute("label");
 
     userInput = "I'm wrong";
 
     instance.menulist.inputField.value = "";
     instance.menulist.focus();
     processStringAsKey(userInput);
     EventUtils.synthesizeKey("VK_RETURN", {});
--- a/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -110,17 +110,17 @@ function test() {
     synthesizeKeyFromKeyTag("key_responsiveUI");
 
     yield once(mgr, "on");
 
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     instance = mgr.getResponsiveUIForTab(gBrowser.selectedTab);
 
-    let customPresetIndex = getPresetIndex("456x123 (Testing preset)");
+    let customPresetIndex = getPresetIndex("456" + "\u00D7" + "123 (Testing preset)");
     info(customPresetIndex);
     ok(customPresetIndex >= 0, "is the previously added preset (idx = " + customPresetIndex + ") in the list of items");
 
     instance.menulist.selectedIndex = customPresetIndex;
 
     is(content.innerWidth, 456, "add preset, and selected in the list, dimension valid (width)");
     is(content.innerHeight, 123, "add preset, and selected in the list, dimension valid (height)");
 
--- a/browser/devtools/shared/widgets/Tooltip.js
+++ b/browser/devtools/shared/widgets/Tooltip.js
@@ -721,17 +721,17 @@ Tooltip.prototype = {
       }
 
       vbox.appendChild(label);
     }
 
     this.content = vbox;
   },
 
-  _getImageDimensionLabel: (w, h) => w + " x " + h,
+  _getImageDimensionLabel: (w, h) => w + " \u00D7 " + h,
 
   /**
    * Fill the tooltip with a new instance of the spectrum color picker widget
    * initialized with the given color, and return a promise that resolves to
    * the instance of spectrum
    */
   setColorPickerContent: function(color) {
     let def = promise.defer();
--- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.dtd
@@ -197,16 +197,24 @@
   -  in a "wait" state. -->
 <!ENTITY netmonitorUI.timings.wait        "Waiting:">
 
 <!-- LOCALIZATION NOTE (debuggerUI.timings.receive): This is the label displayed
   -  in the network details timings tab identifying the amount of time spent
   -  in a "receive" state. -->
 <!ENTITY netmonitorUI.timings.receive     "Receiving:">
 
+<!-- LOCALIZATION NOTE (netmonitorUI.security.warning.protocol): A tooltip
+  -  for warning icon that indicates a connection uses insecure protocol. -->
+<!ENTITY netmonitorUI.security.warning.sslv3      "The protocol SSL 3.0 is deprecated and insecure.">
+
+<!-- LOCALIZATION NOTE (netmonitorUI.security.warning.cipher): A tooltip
+  -  for warning icon that indicates a connection uses insecure cipher suite. -->
+<!ENTITY netmonitorUI.security.warning.cipher     "The cipher used for encryption is deprecated and insecure.">
+
 <!-- LOCALIZATION NOTE (netmonitorUI.security.error): This is the label displayed
   -  in the security tab if a security error prevented the connection. -->
 <!ENTITY netmonitorUI.security.error      "An error occured:">
 
 <!-- LOCALIZATION NOTE (netmonitorUI.security.protocolVersion): This is the label displayed
   -  in the security tab describing TLS/SSL protocol version. -->
 <!ENTITY netmonitorUI.security.protocolVersion "Protocol version:">
 
--- a/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/netmonitor.properties
@@ -39,16 +39,20 @@ netmonitor.security.state.secure=The con
 # channel i.e. the connection was not encrypted.
 netmonitor.security.state.insecure=The connection used to fetch this resource was not encrypted.
 
 # LOCALIZATION NOTE (netmonitor.security.state.broken)
 # This string is used as an tooltip for request that failed due to security
 # issues.
 netmonitor.security.state.broken=A security error prevented the resource from being loaded.
 
+# LOCALIZATION NOTE (netmonitor.security.state.weak)
+# This string is used as an tooltip for request that had minor security issues
+netmonitor.security.state.weak=This resource was transferred over a connection that used weak encryption.
+
 # LOCALIZATION NOTE (netmonitor.security.enabled):
 # This string is used to indicate that a specific security feature is used by
 # a connection in the security details tab.
 # For example: "HTTP Strict Transport Security: Enabled"
 netmonitor.security.enabled=Enabled
 
 # LOCALIZATION NOTE (netmonitor.security.disabled):
 # This string is used to indicate that a specific security feature is not used by
--- a/browser/themes/shared/devtools/netmonitor.inc.css
+++ b/browser/themes/shared/devtools/netmonitor.inc.css
@@ -173,16 +173,21 @@
   list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
 }
 
 .security-state-secure {
   cursor: pointer;
   list-style-image: url(chrome://browser/skin/identity-icons-https.png);
 }
 
+.security-state-weak {
+  cursor: pointer;
+  list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-display.png);
+}
+
 .security-state-broken {
   cursor: pointer;
   list-style-image: url(chrome://browser/skin/identity-icons-https-mixed-active.png);
 }
 
 .requests-menu-type {
   text-align: center;
   width: 4em;
@@ -573,16 +578,31 @@ label.requests-menu-status-code {
 #security-tabpanel {
   overflow: auto;
 }
 
 #security-error-message {
   white-space: pre-wrap;
 }
 
+.security-warning-icon {
+  background-image: url(alerticon-warning.png);
+  background-size: 13px 12px;
+  -moz-margin-start: 5px;
+  vertical-align: top;
+  width: 13px;
+  height: 12px;
+}
+
+@media (min-resolution: 2dppx) {
+  .security-warning-icon {
+    background-image: url(alerticon-warning@2x.png);
+  }
+}
+
 /* Custom request form */
 
 #custom-pane {
   padding: 0.6em 0.5em;
 }
 
 .custom-header {
   font-size: 1.1em;
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -20,16 +20,24 @@
 button,
 treecol {
   /* override the * rule */
   -moz-user-select: none;
 }
 
 /* Category List */
 
+#categories {
+  max-height: 100vh;
+}
+
+#categories > scrollbox {
+  overflow-x: hidden !important;
+}
+
 .category-icon {
   list-style-image: url("chrome://browser/skin/preferences/in-content/icons.png");
 }
 
 #category-general > .category-icon {
   -moz-image-region: rect(0, 24px, 24px, 0);
 }
 
--- a/configure.in
+++ b/configure.in
@@ -4101,33 +4101,16 @@ fi
 if test "$BUILDING_RELEASE"; then
   # Override value in defines.sh, if any
   EARLY_BETA_OR_EARLIER=
 elif test "$EARLY_BETA_OR_EARLIER"; then
   AC_DEFINE(EARLY_BETA_OR_EARLIER)
 fi
 AC_SUBST(EARLY_BETA_OR_EARLIER)
 
-# Allow the application to provide a subconfigure script
-if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then
-  do_output_subdirs() {
-    if test -n "$_subconfigure_subdirs"; then
-      AC_MSG_ERROR([Cannot specify more than one sub-sub-configure])
-     fi
-    _subconfigure_subdir="$1"
-    _subconfigure_config_args="$ac_configure_args"
-  }
-  tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1
-  m4 "${srcdir}/build/autoconf/subconfigure.m4" \
-     "${srcdir}/build/autoconf/altoptions.m4" \
-     "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript
-  . $tmpscript
-  rm -f $tmpscript
-fi
-
 # Allow someone to change MOZ_APP_NAME and MOZ_APP_BASENAME in mozconfig
 MOZ_ARG_WITH_STRING(app-name,
 [--with-app-name=APPNAME sets MOZ_APP_NAME to APPNAME],
 WITH_APP_NAME=$withval,
 )
 
 if test -n "$WITH_APP_NAME" ; then
     MOZ_APP_NAME="$WITH_APP_NAME"
@@ -5959,17 +5942,16 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
           MOZ_DIRECTX_SDK_PATH=
         fi
       else
         AC_MSG_RESULT([Couldn't determine the D3DX9 version for the DirectX SDK.])
         MOZ_DIRECTX_SDK_PATH=
       fi
     else
       AC_MSG_RESULT([Couldn't find an acceptable DirectX SDK for ANGLE, needed for d3dcompiler_43.])
-      AC_MSG_RESULT([  Either ignore, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
     fi
   fi
 
   ######################################
   # Check that we found what we needed.
   MOZ_FOUND_A_D3D_COMPILER=
   MOZ_FOUND_BOTH_D3D_COMPILERS=1
 
@@ -5985,17 +5967,16 @@ if test -n "$MOZ_ANGLE_RENDERER"; then
     AC_MSG_RESULT([Found d3dcompiler DLL for XP: $MOZ_D3DCOMPILER_XP_DLL])
   else
     MOZ_FOUND_BOTH_D3D_COMPILERS=
   fi
 
   if test -z "$CROSS_COMPILE"; then
     if test -z "MOZ_FOUND_A_D3D_COMPILER"; then
       AC_MSG_ERROR([Couldn't find an acceptable D3D compiler DLL.])
-      AC_MSG_ERROR([  Either install Windows SDK 8.0+, install DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
     fi
 
     if test -n "$MOZ_REQUIRE_ALL_D3DCS" -a -z "$MOZ_FOUND_BOTH_D3D_COMPILERS"; then
       AC_MSG_ERROR([Both D3D compilers _43 and _46+ are required by --enable-require-d3d-compilers.])
       AC_MSG_ERROR([  Install Windows SDK 8.0+, as well as DirectX SDK (June 2010 version or newer), or reconfigure without this flag.])
     fi
   fi
 fi
@@ -7252,16 +7233,35 @@ fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
 AC_SUBST(MOZ_JEMALLOC3)
 AC_SUBST(MOZ_NATIVE_JEMALLOC)
 AC_SUBST(MOZ_CRT)
 export MOZ_CRT
 AC_SUBST(MOZ_GLUE_IN_PROGRAM)
 AC_SUBST_LIST(WIN32_CRT_LIBS)
 
+# Allow the application to provide a subconfigure script.
+# This should be after 'export MOZ_NO_DEBUG_RTL=1' since
+# ldap/c-sdk/configure refers to the enviroment value.
+if test -f "${srcdir}/${MOZ_BUILD_APP}/configure.in" ; then
+  do_output_subdirs() {
+    if test -n "$_subconfigure_subdirs"; then
+      AC_MSG_ERROR([Cannot specify more than one sub-sub-configure])
+     fi
+    _subconfigure_subdir="$1"
+    _subconfigure_config_args="$ac_configure_args"
+  }
+  tmpscript=`$PYTHON -c 'import os, tempfile; print tempfile.mktemp(prefix="subscript.").replace(os.sep, "/")'` || exit 1
+  m4 "${srcdir}/build/autoconf/subconfigure.m4" \
+     "${srcdir}/build/autoconf/altoptions.m4" \
+     "${srcdir}/${MOZ_BUILD_APP}/configure.in" > $tmpscript
+  . $tmpscript
+  rm -f $tmpscript
+fi
+
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
         MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2"
     fi
     if test "$MOZ_WIDGET_TOOLKIT" = android; then
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -12,16 +12,17 @@
  * The goal of the utility functions is to cut down on the size of
  * the generated code itself.
  */
 
 #include "mozilla/Assertions.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/Conversions.h"
 #include "nsString.h"
 
 class nsIScriptContext;
 class nsIScriptGlobalObject;
 
 namespace mozilla {
 namespace dom {
 class AutoJSAPI;
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_BindingUtils_h__
 #define mozilla_dom_BindingUtils_h__
 
 #include "jsfriendapi.h"
 #include "jswrapper.h"
+#include "js/Conversions.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Alignment.h"
 #include "mozilla/Array.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/CallbackObject.h"
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
--- a/dom/bindings/PrimitiveConversions.h
+++ b/dom/bindings/PrimitiveConversions.h
@@ -11,16 +11,17 @@
 #ifndef mozilla_dom_PrimitiveConversions_h
 #define mozilla_dom_PrimitiveConversions_h
 
 #include <limits>
 #include <math.h>
 #include <stdint.h>
 
 #include "jsapi.h"
+#include "js/Conversions.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/FloatingPoint.h"
 
 namespace mozilla {
 namespace dom {
 
 template<typename T>
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -63,16 +63,17 @@
 #include "nsIMemoryReporter.h"
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
+#include "js/Conversions.h"
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/dom/PBrowserParent.h"
@@ -4815,20 +4816,20 @@ CanvasRenderingContext2D::GetImageData(J
     return nullptr;
   }
 
   if (!aSw || !aSh) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  int32_t x = JS_DoubleToInt32(aSx);
-  int32_t y = JS_DoubleToInt32(aSy);
-  int32_t wi = JS_DoubleToInt32(aSw);
-  int32_t hi = JS_DoubleToInt32(aSh);
+  int32_t x = JS::ToInt32(aSx);
+  int32_t y = JS::ToInt32(aSy);
+  int32_t wi = JS::ToInt32(aSw);
+  int32_t hi = JS::ToInt32(aSh);
 
   // Handle negative width and height by flipping the rectangle over in the
   // relevant direction.
   uint32_t w, h;
   if (aSw < 0) {
     w = -wi;
     x -= w;
   } else {
@@ -5011,39 +5012,39 @@ CanvasRenderingContext2D::FillRuleChange
 void
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, ErrorResult& error)
 {
   dom::Uint8ClampedArray arr;
   DebugOnly<bool> inited = arr.Init(imageData.GetDataObject());
   MOZ_ASSERT(inited);
 
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+  error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 &arr, false, 0, 0, 0, 0);
 }
 
 void
 CanvasRenderingContext2D::PutImageData(ImageData& imageData, double dx,
                                        double dy, double dirtyX,
                                        double dirtyY, double dirtyWidth,
                                        double dirtyHeight,
                                        ErrorResult& error)
 {
   dom::Uint8ClampedArray arr;
   DebugOnly<bool> inited = arr.Init(imageData.GetDataObject());
   MOZ_ASSERT(inited);
 
-  error = PutImageData_explicit(JS_DoubleToInt32(dx), JS_DoubleToInt32(dy),
+  error = PutImageData_explicit(JS::ToInt32(dx), JS::ToInt32(dy),
                                 imageData.Width(), imageData.Height(),
                                 &arr, true,
-                                JS_DoubleToInt32(dirtyX),
-                                JS_DoubleToInt32(dirtyY),
-                                JS_DoubleToInt32(dirtyWidth),
-                                JS_DoubleToInt32(dirtyHeight));
+                                JS::ToInt32(dirtyX),
+                                JS::ToInt32(dirtyY),
+                                JS::ToInt32(dirtyWidth),
+                                JS::ToInt32(dirtyHeight));
 }
 
 // void putImageData (in ImageData d, in float x, in float y);
 // void putImageData (in ImageData d, in double x, in double y, in double dirtyX, in double dirtyY, in double dirtyWidth, in double dirtyHeight);
 
 nsresult
 CanvasRenderingContext2D::PutImageData_explicit(int32_t x, int32_t y, uint32_t w, uint32_t h,
                                                 dom::Uint8ClampedArray* aArray,
@@ -5201,18 +5202,18 @@ already_AddRefed<ImageData>
 CanvasRenderingContext2D::CreateImageData(JSContext* cx, double sw,
                                           double sh, ErrorResult& error)
 {
   if (!sw || !sh) {
     error.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
-  int32_t wi = JS_DoubleToInt32(sw);
-  int32_t hi = JS_DoubleToInt32(sh);
+  int32_t wi = JS::ToInt32(sw);
+  int32_t hi = JS::ToInt32(sh);
 
   uint32_t w = Abs(wi);
   uint32_t h = Abs(hi);
   return mozilla::dom::CreateImageData(cx, this, w, h, error);
 }
 
 already_AddRefed<ImageData>
 CanvasRenderingContext2D::CreateImageData(JSContext* cx,
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -27,16 +27,22 @@ class nsPresContext;
 namespace mozilla {
 namespace dom {
 
 class EventTarget;
 class WantsPopupControlCheck;
 #define GENERATED_EVENT(EventClass_) class EventClass_;
 #include "mozilla/dom/GeneratedEventList.h"
 #undef GENERATED_EVENT
+// ExtendableEvent and InstallEvent are ServiceWorker events that are not
+// autogenerated since they have some extra methods.
+namespace workers {
+class ExtendableEvent;
+class InstallEvent;
+} // namespace workers
 
 // Dummy class so we can cast through it to get from nsISupports to
 // Event subclasses with only two non-ambiguous static casts.
 class EventBase : public nsIDOMEvent
 {
 };
 
 class Event : public EventBase,
@@ -99,16 +105,28 @@ public:
 #define GENERATED_EVENT(EventClass_) \
   virtual EventClass_* As##EventClass_()  \
   {                                       \
     return nullptr;                       \
   }
 #include "mozilla/dom/GeneratedEventList.h"
 #undef GENERATED_EVENT
 
+  // ExtendableEvent and InstallEvent are ServiceWorker events that are not
+  // autogenerated since they have some extra methods.
+  virtual workers::ExtendableEvent* AsExtendableEvent()
+  {
+    return nullptr;
+  }
+
+  virtual workers::InstallEvent* AsInstallEvent()
+  {
+    return nullptr;
+  }
+
   // nsIDOMEvent Interface
   NS_DECL_NSIDOMEVENT
 
   void InitPresContextData(nsPresContext* aPresContext);
 
   // Returns true if the event should be trusted.
   bool Init(EventTarget* aGlobal);
 
--- a/dom/media/eme/CDMProxy.cpp
+++ b/dom/media/eme/CDMProxy.cpp
@@ -410,16 +410,17 @@ CDMProxy::OnResolveLoadSessionPromise(ui
 }
 
 static dom::MediaKeyMessageType
 ToMediaKeyMessageType(GMPSessionMessageType aMessageType) {
   switch (aMessageType) {
     case kGMPLicenseRequest: return dom::MediaKeyMessageType::License_request;
     case kGMPLicenseRenewal: return dom::MediaKeyMessageType::License_renewal;
     case kGMPLicenseRelease: return dom::MediaKeyMessageType::License_release;
+    case kGMPIndividualizationRequest: return dom::MediaKeyMessageType::Individualization_request;
     default: return dom::MediaKeyMessageType::License_request;
   };
 };
 
 void
 CDMProxy::OnSessionMessage(const nsAString& aSessionId,
                            GMPSessionMessageType aMessageType,
                            nsTArray<uint8_t>& aMessage)
--- a/dom/media/fmp4/MP4Stream.cpp
+++ b/dom/media/fmp4/MP4Stream.cpp
@@ -23,22 +23,25 @@ MP4Stream::~MP4Stream()
   MOZ_ASSERT(mPinCount == 0);
 }
 
 bool
 MP4Stream::BlockingReadIntoCache(int64_t aOffset, size_t aCount, Monitor* aToUnlock)
 {
   MOZ_ASSERT(mPinCount > 0);
   CacheBlock block(aOffset, aCount);
+  if (!block.Init()) {
+    return false;
+  }
 
   uint32_t sum = 0;
   uint32_t bytesRead = 0;
   do {
     uint64_t offset = aOffset + sum;
-    char* buffer = reinterpret_cast<char*>(block.mBuffer.get()) + sum;
+    char* buffer = block.Buffer() + sum;
     uint32_t toRead = aCount - sum;
     MonitorAutoUnlock unlock(*aToUnlock);
     nsresult rv = mResource->ReadAt(offset, buffer, toRead, &bytesRead);
     if (NS_FAILED(rv)) {
       return false;
     }
     sum += bytesRead;
   } while (sum < aCount && bytesRead > 0);
@@ -71,17 +74,17 @@ MP4Stream::ReadAt(int64_t aOffset, void*
 
 bool
 MP4Stream::CachedReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
                         size_t* aBytesRead)
 {
   // First, check our local cache.
   for (size_t i = 0; i < mCache.Length(); ++i) {
     if (mCache[i].mOffset == aOffset && mCache[i].mCount >= aCount) {
-      memcpy(aBuffer, mCache[i].mBuffer, aCount);
+      memcpy(aBuffer, mCache[i].Buffer(), aCount);
       *aBytesRead = aCount;
       return true;
     }
   }
 
   nsresult rv = mResource->ReadFromCache(reinterpret_cast<char*>(aBuffer),
                                          aOffset, aCount);
   if (NS_FAILED(rv)) {
--- a/dom/media/fmp4/MP4Stream.h
+++ b/dom/media/fmp4/MP4Stream.h
@@ -6,16 +6,17 @@
 
 #ifndef MP4_STREAM_H_
 #define MP4_STREAM_H_
 
 #include "mp4_demuxer/mp4_demuxer.h"
 
 #include "MediaResource.h"
 
+#include "mozilla/fallible.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
 
 namespace mozilla {
 
 class Monitor;
 
 class MP4Stream : public mp4_demuxer::Stream {
@@ -65,19 +66,33 @@ public:
 
 private:
   nsRefPtr<MediaResource> mResource;
   Maybe<ReadRecord> mFailedRead;
   uint32_t mPinCount;
 
   struct CacheBlock {
     CacheBlock(int64_t aOffset, size_t aCount)
-      : mOffset(aOffset), mCount(aCount), mBuffer(new uint8_t[aCount]) {}
+      : mOffset(aOffset), mCount(aCount), mBuffer(nullptr) {}
     int64_t mOffset;
     size_t mCount;
-    nsAutoArrayPtr<uint8_t> mBuffer;
+
+    bool Init()
+    {
+      mBuffer = new ((fallible_t())) char[mCount];
+      return !!mBuffer;
+    }
+
+    char* Buffer()
+    {
+      MOZ_ASSERT(mBuffer.get());
+      return mBuffer.get();
+    }
+
+  private:
+    nsAutoArrayPtr<char> mBuffer;
   };
   nsTArray<CacheBlock> mCache;
 };
 
 }
 
 #endif
--- a/dom/media/gmp-plugin/fake.info
+++ b/dom/media/gmp-plugin/fake.info
@@ -1,5 +1,5 @@
 Name: fake
 Description: Fake GMP Plugin
 Version: 1.0
-APIs: encode-video[h264], decode-video[h264], eme-decrypt-v3[fake]
+APIs: encode-video[h264], decode-video[h264], eme-decrypt-v4[fake]
 Libraries: dxva2.dll
--- a/dom/media/gmp/gmp-api/gmp-decryption.h
+++ b/dom/media/gmp/gmp-api/gmp-decryption.h
@@ -67,17 +67,18 @@ enum GMPDOMException {
   kGMPQuotaExceededError = 22,
   kGMPTimeoutError = 23
 };
 
 enum GMPSessionMessageType {
   kGMPLicenseRequest = 0,
   kGMPLicenseRenewal = 1,
   kGMPLicenseRelease = 2,
-  kGMPMessageInvalid = 3 // Must always be last.
+  kGMPIndividualizationRequest = 3,
+  kGMPMessageInvalid = 4 // Must always be last.
 };
 
 // Time in milliseconds, as offset from epoch, 1 Jan 1970.
 typedef int64_t GMPTimestamp;
 
 // Capability definitions. The capabilities of the EME GMP are reported
 // to Gecko by calling the GMPDecryptorCallback::SetCapabilities()
 // callback and specifying the logical OR of the GMP_EME_CAP_* flags below.
@@ -214,17 +215,17 @@ public:
 };
 
 enum GMPSessionType {
   kGMPTemporySession = 0,
   kGMPPersistentSession = 1,
   kGMPSessionInvalid = 2 // Must always be last.
 };
 
-#define GMP_API_DECRYPTOR "eme-decrypt-v3"
+#define GMP_API_DECRYPTOR "eme-decrypt-v4"
 
 // API exposed by plugin library to manage decryption sessions.
 // When the Host requests this by calling GMPGetAPIFunc().
 //
 // API name macro: GMP_API_DECRYPTOR
 // Host API: GMPDecryptorHost
 class GMPDecryptor {
 public:
--- a/dom/media/gstreamer/GStreamerReader.h
+++ b/dom/media/gstreamer/GStreamerReader.h
@@ -36,36 +36,36 @@ class AbstractMediaDecoder;
 class GStreamerReader : public MediaDecoderReader
 {
   typedef gfx::IntRect IntRect;
 
 public:
   explicit GStreamerReader(AbstractMediaDecoder* aDecoder);
   virtual ~GStreamerReader();
 
-  virtual nsresult Init(MediaDecoderReader* aCloneDonor);
-  virtual nsresult ResetDecode();
-  virtual bool DecodeAudioData();
+  virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
+  virtual nsresult ResetDecode() MOZ_OVERRIDE;
+  virtual bool DecodeAudioData() MOZ_OVERRIDE;
   virtual bool DecodeVideoFrame(bool &aKeyframeSkip,
-                                int64_t aTimeThreshold);
+                                int64_t aTimeThreshold) MOZ_OVERRIDE;
   virtual nsresult ReadMetadata(MediaInfo* aInfo,
-                                MetadataTags** aTags);
+                                MetadataTags** aTags) MOZ_OVERRIDE;
   virtual nsRefPtr<SeekPromise>
   Seek(int64_t aTime, int64_t aEndTime) MOZ_OVERRIDE;
-  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered);
+  virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
 
   virtual void NotifyDataArrived(const char *aBuffer,
                                  uint32_t aLength,
                                  int64_t aOffset) MOZ_OVERRIDE;
 
-  virtual bool HasAudio() {
+  virtual bool HasAudio() MOZ_OVERRIDE {
     return mInfo.HasAudio();
   }
 
-  virtual bool HasVideo() {
+  virtual bool HasVideo() MOZ_OVERRIDE {
     return mInfo.HasVideo();
   }
 
   layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); }
 
   virtual bool IsMediaSeekable() MOZ_OVERRIDE;
 
 private:
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -226,17 +226,17 @@ protected:
     virtual nsresult BeginUpdateBackground(NPP instance,
                                            const nsIntRect& aRect,
                                            gfxContext** aCtx) MOZ_OVERRIDE;
     virtual nsresult EndUpdateBackground(NPP instance,
                                          gfxContext* aCtx,
                                          const nsIntRect& aRect) MOZ_OVERRIDE;
 
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
-    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error);
+    virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) MOZ_OVERRIDE;
 #else
     virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) MOZ_OVERRIDE;
 #endif
     virtual nsresult NP_Shutdown(NPError* error) MOZ_OVERRIDE;
 
     virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) MOZ_OVERRIDE;
     virtual nsresult NP_GetValue(void *future, NPPVariable aVariable,
                                  void *aValue, NPError* error) MOZ_OVERRIDE;
--- a/dom/webidl/InstallEvent.webidl
+++ b/dom/webidl/InstallEvent.webidl
@@ -11,11 +11,11 @@
  Exposed=ServiceWorker]
 interface InstallEvent : ExtendableEvent {
   readonly attribute ServiceWorker? activeWorker;
   void replace();
 };
 
 // Should be in the spec soon to satisfy conventions about events.
 // https://github.com/slightlyoff/ServiceWorker/issues/216.
-dictionary InstallEventInit : EventInit {
+dictionary InstallEventInit : ExtendableEventInit {
   ServiceWorker? activeWorker = null;
 };
--- a/dom/webidl/MediaKeyMessageEvent.webidl
+++ b/dom/webidl/MediaKeyMessageEvent.webidl
@@ -8,17 +8,18 @@
  *
  * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
  * W3C liability, trademark and document use rules apply.
  */
 
 enum MediaKeyMessageType {
   "license-request",
   "license-renewal",
-  "license-release"
+  "license-release",
+  "individualization-request"
 };
 
 [Pref="media.eme.enabled", Constructor(DOMString type, optional MediaKeyMessageEventInit eventInitDict)]
 interface MediaKeyMessageEvent : Event {
   readonly attribute MediaKeyMessageType messageType;
   [Throws]
   readonly attribute ArrayBuffer message;
 };
--- a/dom/webidl/SVGClipPathElement.webidl
+++ b/dom/webidl/SVGClipPathElement.webidl
@@ -6,14 +6,16 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGClipPathElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedEnumeration clipPathUnits;
+  [Constant]
   readonly attribute SVGAnimatedTransformList transform;
 };
 
 SVGClipPathElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGComponentTransferFunctionElement.webidl
+++ b/dom/webidl/SVGComponentTransferFunctionElement.webidl
@@ -14,16 +14,23 @@ interface SVGComponentTransferFunctionEl
   // Component Transfer Types
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_UNKNOWN = 0;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_IDENTITY = 1;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_TABLE = 2;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_DISCRETE = 3;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_LINEAR = 4;
   const unsigned short SVG_FECOMPONENTTRANSFER_TYPE_GAMMA = 5;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
+  [Constant]
   readonly attribute SVGAnimatedNumberList tableValues;
+  [Constant]
   readonly attribute SVGAnimatedNumber slope;
+  [Constant]
   readonly attribute SVGAnimatedNumber intercept;
+  [Constant]
   readonly attribute SVGAnimatedNumber amplitude;
+  [Constant]
   readonly attribute SVGAnimatedNumber exponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber offset;
 };
--- a/dom/webidl/SVGDocument.webidl
+++ b/dom/webidl/SVGDocument.webidl
@@ -5,11 +5,11 @@
  *
  * The origin of this IDL file is:
  * dom/interfaces/svg/nsIDOMSVGDocument.idl
  */
 
 interface SVGDocument : Document {
   [Throws]
   readonly attribute DOMString domain;
-  [Throws]
+  [Pure, Throws]
   readonly attribute SVGElement? rootElement;
 };
--- a/dom/webidl/SVGElement.webidl
+++ b/dom/webidl/SVGElement.webidl
@@ -10,16 +10,17 @@
  * liability, trademark and document use rules apply.
  */
 
 interface SVGElement : Element {
            attribute DOMString id;
 /*           [SetterThrows]
            attribute DOMString xmlbase; */
 
+  [Constant]
   readonly attribute SVGAnimatedString className;
   [PutForwards=cssText, Constant]
   readonly attribute CSSStyleDeclaration style;
 
   /*[SetterThrows]
   attribute DOMString xmllang;
   [SetterThrows]
   attribute DOMString xmlspace;*/
--- a/dom/webidl/SVGFEBlendElement.webidl
+++ b/dom/webidl/SVGFEBlendElement.webidl
@@ -25,14 +25,17 @@ interface SVGFEBlendElement : SVGElement
   const unsigned short SVG_FEBLEND_MODE_HARD_LIGHT = 9;
   const unsigned short SVG_FEBLEND_MODE_SOFT_LIGHT = 10;
   const unsigned short SVG_FEBLEND_MODE_DIFFERENCE = 11;
   const unsigned short SVG_FEBLEND_MODE_EXCLUSION = 12;
   const unsigned short SVG_FEBLEND_MODE_HUE = 13;
   const unsigned short SVG_FEBLEND_MODE_SATURATION = 14;
   const unsigned short SVG_FEBLEND_MODE_COLOR = 15;
   const unsigned short SVG_FEBLEND_MODE_LUMINOSITY = 16;
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration mode;
 };
 
 SVGFEBlendElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEColorMatrixElement.webidl
+++ b/dom/webidl/SVGFEColorMatrixElement.webidl
@@ -14,14 +14,17 @@ interface SVGFEColorMatrixElement : SVGE
 
   // Color Matrix Types
   const unsigned short SVG_FECOLORMATRIX_TYPE_UNKNOWN = 0;
   const unsigned short SVG_FECOLORMATRIX_TYPE_MATRIX = 1;
   const unsigned short SVG_FECOLORMATRIX_TYPE_SATURATE = 2;
   const unsigned short SVG_FECOLORMATRIX_TYPE_HUEROTATE = 3;
   const unsigned short SVG_FECOLORMATRIX_TYPE_LUMINANCETOALPHA = 4;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
+  [Constant]
   readonly attribute SVGAnimatedNumberList values;
 };
 
 SVGFEColorMatrixElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEComponentTransferElement.webidl
+++ b/dom/webidl/SVGFEComponentTransferElement.webidl
@@ -6,12 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEComponentTransferElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
 
 SVGFEComponentTransferElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFECompositeElement.webidl
+++ b/dom/webidl/SVGFECompositeElement.webidl
@@ -16,18 +16,25 @@ interface SVGFECompositeElement : SVGEle
   const unsigned short SVG_FECOMPOSITE_OPERATOR_UNKNOWN = 0;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_OVER = 1;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_IN = 2;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_OUT = 3;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_ATOP = 4;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_XOR = 5;
   const unsigned short SVG_FECOMPOSITE_OPERATOR_ARITHMETIC = 6;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration operator;
+  [Constant]
   readonly attribute SVGAnimatedNumber k1;
+  [Constant]
   readonly attribute SVGAnimatedNumber k2;
+  [Constant]
   readonly attribute SVGAnimatedNumber k3;
+  [Constant]
   readonly attribute SVGAnimatedNumber k4;
 };
 
 SVGFECompositeElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEConvolveMatrixElement.webidl
+++ b/dom/webidl/SVGFEConvolveMatrixElement.webidl
@@ -13,23 +13,35 @@
 interface SVGFEConvolveMatrixElement : SVGElement {
 
   // Edge Mode Values
   const unsigned short SVG_EDGEMODE_UNKNOWN = 0;
   const unsigned short SVG_EDGEMODE_DUPLICATE = 1;
   const unsigned short SVG_EDGEMODE_WRAP = 2;
   const unsigned short SVG_EDGEMODE_NONE = 3;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedInteger orderX;
+  [Constant]
   readonly attribute SVGAnimatedInteger orderY;
+  [Constant]
   readonly attribute SVGAnimatedNumberList kernelMatrix;
+  [Constant]
   readonly attribute SVGAnimatedNumber divisor;
+  [Constant]
   readonly attribute SVGAnimatedNumber bias;
+  [Constant]
   readonly attribute SVGAnimatedInteger targetX;
+  [Constant]
   readonly attribute SVGAnimatedInteger targetY;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration edgeMode;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
+  [Constant]
   readonly attribute SVGAnimatedBoolean preserveAlpha;
 };
 
 SVGFEConvolveMatrixElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDiffuseLightingElement.webidl
+++ b/dom/webidl/SVGFEDiffuseLightingElement.webidl
@@ -6,16 +6,21 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDiffuseLightingElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber surfaceScale;
+  [Constant]
   readonly attribute SVGAnimatedNumber diffuseConstant;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFEDiffuseLightingElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDisplacementMapElement.webidl
+++ b/dom/webidl/SVGFEDisplacementMapElement.webidl
@@ -14,16 +14,21 @@ interface SVGFEDisplacementMapElement : 
 
   // Channel Selectors
   const unsigned short SVG_CHANNEL_UNKNOWN = 0;
   const unsigned short SVG_CHANNEL_R = 1;
   const unsigned short SVG_CHANNEL_G = 2;
   const unsigned short SVG_CHANNEL_B = 3;
   const unsigned short SVG_CHANNEL_A = 4;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedString in2;
+  [Constant]
   readonly attribute SVGAnimatedNumber scale;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration xChannelSelector;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration yChannelSelector;
 };
 
 SVGFEDisplacementMapElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEDistantLightElement.webidl
+++ b/dom/webidl/SVGFEDistantLightElement.webidl
@@ -6,11 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDistantLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber azimuth;
+  [Constant]
   readonly attribute SVGAnimatedNumber elevation;
 };
--- a/dom/webidl/SVGFEDropShadowElement.webidl
+++ b/dom/webidl/SVGFEDropShadowElement.webidl
@@ -6,18 +6,23 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEDropShadowElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber dx;
+  [Constant]
   readonly attribute SVGAnimatedNumber dy;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationX;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationY;
 
   void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEDropShadowElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEGaussianBlurElement.webidl
+++ b/dom/webidl/SVGFEGaussianBlurElement.webidl
@@ -6,16 +6,19 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEGaussianBlurElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationX;
+  [Constant]
   readonly attribute SVGAnimatedNumber stdDeviationY;
 
   void setStdDeviation(float stdDeviationX, float stdDeviationY);
 };
 
 SVGFEGaussianBlurElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEImageElement.webidl
+++ b/dom/webidl/SVGFEImageElement.webidl
@@ -6,13 +6,14 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEImageElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio;
 };
 
 SVGFEImageElement implements SVGFilterPrimitiveStandardAttributes;
 SVGFEImageElement implements SVGURIReference;
--- a/dom/webidl/SVGFEMergeNodeElement.webidl
+++ b/dom/webidl/SVGFEMergeNodeElement.webidl
@@ -6,10 +6,11 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEMergeNodeElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
--- a/dom/webidl/SVGFEMorphologyElement.webidl
+++ b/dom/webidl/SVGFEMorphologyElement.webidl
@@ -12,15 +12,19 @@
 
 interface SVGFEMorphologyElement : SVGElement {
 
   // Morphology Operators
   const unsigned short SVG_MORPHOLOGY_OPERATOR_UNKNOWN = 0;
   const unsigned short SVG_MORPHOLOGY_OPERATOR_ERODE = 1;
   const unsigned short SVG_MORPHOLOGY_OPERATOR_DILATE = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration operator;
+  [Constant]
   readonly attribute SVGAnimatedNumber radiusX;
+  [Constant]
   readonly attribute SVGAnimatedNumber radiusY;
 };
 
 SVGFEMorphologyElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEOffsetElement.webidl
+++ b/dom/webidl/SVGFEOffsetElement.webidl
@@ -6,14 +6,17 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEOffsetElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber dx;
+  [Constant]
   readonly attribute SVGAnimatedNumber dy;
 };
 
 SVGFEOffsetElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFEPointLightElement.webidl
+++ b/dom/webidl/SVGFEPointLightElement.webidl
@@ -6,12 +6,15 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFEPointLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber x;
+  [Constant]
   readonly attribute SVGAnimatedNumber y;
+  [Constant]
   readonly attribute SVGAnimatedNumber z;
 };
--- a/dom/webidl/SVGFESpecularLightingElement.webidl
+++ b/dom/webidl/SVGFESpecularLightingElement.webidl
@@ -6,17 +6,23 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFESpecularLightingElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
+  [Constant]
   readonly attribute SVGAnimatedNumber surfaceScale;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularConstant;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularExponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthX;
+  [Constant]
   readonly attribute SVGAnimatedNumber kernelUnitLengthY;
 };
 
 SVGFESpecularLightingElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFESpotLightElement.webidl
+++ b/dom/webidl/SVGFESpotLightElement.webidl
@@ -6,17 +6,25 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFESpotLightElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber x;
+  [Constant]
   readonly attribute SVGAnimatedNumber y;
+  [Constant]
   readonly attribute SVGAnimatedNumber z;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtX;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtY;
+  [Constant]
   readonly attribute SVGAnimatedNumber pointsAtZ;
+  [Constant]
   readonly attribute SVGAnimatedNumber specularExponent;
+  [Constant]
   readonly attribute SVGAnimatedNumber limitingConeAngle;
 };
--- a/dom/webidl/SVGFETileElement.webidl
+++ b/dom/webidl/SVGFETileElement.webidl
@@ -6,12 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFETileElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedString in1;
 };
 
 SVGFETileElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFETurbulenceElement.webidl
+++ b/dom/webidl/SVGFETurbulenceElement.webidl
@@ -17,17 +17,23 @@ interface SVGFETurbulenceElement : SVGEl
   const unsigned short SVG_TURBULENCE_TYPE_FRACTALNOISE = 1;
   const unsigned short SVG_TURBULENCE_TYPE_TURBULENCE = 2;
 
   // Stitch Options
   const unsigned short SVG_STITCHTYPE_UNKNOWN = 0;
   const unsigned short SVG_STITCHTYPE_STITCH = 1;
   const unsigned short SVG_STITCHTYPE_NOSTITCH = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedNumber baseFrequencyX;
+  [Constant]
   readonly attribute SVGAnimatedNumber baseFrequencyY;
+  [Constant]
   readonly attribute SVGAnimatedInteger numOctaves;
+  [Constant]
   readonly attribute SVGAnimatedNumber seed;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration stitchTiles;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration type;
 };
 
 SVGFETurbulenceElement implements SVGFilterPrimitiveStandardAttributes;
--- a/dom/webidl/SVGFilterElement.webidl
+++ b/dom/webidl/SVGFilterElement.webidl
@@ -6,21 +6,27 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGFilterElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedEnumeration filterUnits;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration primitiveUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
 
   // ImageData apply(ImageData source);
 };
 
 SVGFilterElement implements SVGURIReference;
 SVGFilterElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl
+++ b/dom/webidl/SVGFilterPrimitiveStandardAttributes.webidl
@@ -7,14 +7,19 @@
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface SVGFilterPrimitiveStandardAttributes {
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
+  [Constant]
   readonly attribute SVGAnimatedString result;
 };
--- a/dom/webidl/SVGGradientElement.webidl
+++ b/dom/webidl/SVGGradientElement.webidl
@@ -13,15 +13,18 @@
 interface SVGGradientElement : SVGElement {
 
   // Spread Method Types
   const unsigned short SVG_SPREADMETHOD_UNKNOWN = 0;
   const unsigned short SVG_SPREADMETHOD_PAD = 1;
   const unsigned short SVG_SPREADMETHOD_REFLECT = 2;
   const unsigned short SVG_SPREADMETHOD_REPEAT = 3;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration gradientUnits;
+  [Constant]
   readonly attribute SVGAnimatedTransformList gradientTransform;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration spreadMethod;
 };
 
 SVGGradientElement implements SVGURIReference;
 SVGGradientElement implements SVGUnitTypes;
--- a/dom/webidl/SVGLinearGradientElement.webidl
+++ b/dom/webidl/SVGLinearGradientElement.webidl
@@ -6,13 +6,17 @@
  * The origin of this IDL file is
  * https://svgwg.org/svg2-draft/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGLinearGradientElement : SVGGradientElement {
+  [Constant]
   readonly attribute SVGAnimatedLength x1;
+  [Constant]
   readonly attribute SVGAnimatedLength y1;
+  [Constant]
   readonly attribute SVGAnimatedLength x2;
+  [Constant]
   readonly attribute SVGAnimatedLength y2;
 };
--- a/dom/webidl/SVGMarkerElement.webidl
+++ b/dom/webidl/SVGMarkerElement.webidl
@@ -17,22 +17,29 @@ interface SVGMarkerElement : SVGElement 
   const unsigned short SVG_MARKERUNITS_USERSPACEONUSE = 1;
   const unsigned short SVG_MARKERUNITS_STROKEWIDTH = 2;
 
   // Marker Orientation Types
   const unsigned short SVG_MARKER_ORIENT_UNKNOWN = 0;
   const unsigned short SVG_MARKER_ORIENT_AUTO = 1;
   const unsigned short SVG_MARKER_ORIENT_ANGLE = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength refX;
+  [Constant]
   readonly attribute SVGAnimatedLength refY;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration markerUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength markerWidth;
+  [Constant]
   readonly attribute SVGAnimatedLength markerHeight;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration orientType;
+  [Constant]
   readonly attribute SVGAnimatedAngle orientAngle;
 
   void setOrientToAuto();
   [Throws]
   void setOrientToAngle(SVGAngle angle);
 };
 
 SVGMarkerElement implements SVGFitToViewBox;
--- a/dom/webidl/SVGMaskElement.webidl
+++ b/dom/webidl/SVGMaskElement.webidl
@@ -11,18 +11,24 @@
  */
 
 interface SVGMaskElement : SVGElement {
 
   // Mask Types
   const unsigned short SVG_MASKTYPE_LUMINANCE = 0;
   const unsigned short SVG_MASKTYPE_ALPHA = 1;
 
+  [Constant]
   readonly attribute SVGAnimatedEnumeration maskUnits;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration maskContentUnits;
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
 };
 
 SVGMaskElement implements SVGUnitTypes;
 
--- a/dom/webidl/SVGPathElement.webidl
+++ b/dom/webidl/SVGPathElement.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 interface SVGPathElement : SVGGraphicsElement {
 
+  [Constant]
   readonly attribute SVGAnimatedNumber pathLength;
 
   float getTotalLength();
   [NewObject, Throws]
   SVGPoint getPointAtLength(float distance);
   unsigned long getPathSegAtLength(float distance);
   [NewObject]
   SVGPathSegClosePath createSVGPathSegClosePath();
--- a/dom/webidl/SVGPathSeg.webidl
+++ b/dom/webidl/SVGPathSeg.webidl
@@ -29,17 +29,19 @@ interface SVGPathSeg {
   const unsigned short PATHSEG_LINETO_HORIZONTAL_REL = 13;
   const unsigned short PATHSEG_LINETO_VERTICAL_ABS = 14;
   const unsigned short PATHSEG_LINETO_VERTICAL_REL = 15;
   const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_ABS = 16;
   const unsigned short PATHSEG_CURVETO_CUBIC_SMOOTH_REL = 17;
   const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS = 18;
   const unsigned short PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL = 19;
 
+  [Pure]
   readonly attribute unsigned short pathSegType;
+  [Pure]
   readonly attribute DOMString pathSegTypeAsLetter;
 };
 
 interface SVGPathSegClosePath : SVGPathSeg {
 };
 
 interface SVGPathSegMovetoAbs : SVGPathSeg {
   [SetterThrows]
--- a/dom/webidl/SVGRadialGradientElement.webidl
+++ b/dom/webidl/SVGRadialGradientElement.webidl
@@ -6,15 +6,20 @@
  * The origin of this IDL file is
  * https://svgwg.org/svg2-draft/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGRadialGradientElement : SVGGradientElement {
+  [Constant]
   readonly attribute SVGAnimatedLength cx;
+  [Constant]
   readonly attribute SVGAnimatedLength cy;
+  [Constant]
   readonly attribute SVGAnimatedLength r;
+  [Constant]
   readonly attribute SVGAnimatedLength fx;
+  [Constant]
   readonly attribute SVGAnimatedLength fy;
   // readonly attribute SVGAnimatedLength fr;
 };
--- a/dom/webidl/SVGSVGElement.webidl
+++ b/dom/webidl/SVGSVGElement.webidl
@@ -9,19 +9,23 @@
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGViewSpec;
 
 interface SVGSVGElement : SVGGraphicsElement {
 
+  [Constant]
   readonly attribute SVGAnimatedLength x;
+  [Constant]
   readonly attribute SVGAnimatedLength y;
+  [Constant]
   readonly attribute SVGAnimatedLength width;
+  [Constant]
   readonly attribute SVGAnimatedLength height;
   // readonly attribute SVGRect viewport;
   [Constant]
   readonly attribute float pixelUnitToMillimeterX;
   [Constant]
   readonly attribute float pixelUnitToMillimeterY;
   [Constant]
   readonly attribute float screenPixelToMillimeterX;
--- a/dom/webidl/SVGStopElement.webidl
+++ b/dom/webidl/SVGStopElement.webidl
@@ -6,11 +6,12 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGStopElement : SVGElement {
+  [Constant]
   readonly attribute SVGAnimatedNumber offset;
 };
 
--- a/dom/webidl/SVGTextContentElement.webidl
+++ b/dom/webidl/SVGTextContentElement.webidl
@@ -12,17 +12,19 @@
 
 interface SVGTextContentElement : SVGGraphicsElement {
 
   // lengthAdjust Types
   const unsigned short LENGTHADJUST_UNKNOWN = 0;
   const unsigned short LENGTHADJUST_SPACING = 1;
   const unsigned short LENGTHADJUST_SPACINGANDGLYPHS = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength textLength;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration lengthAdjust;
 
   long getNumberOfChars();
   float getComputedTextLength();
   [Throws]
   float getSubStringLength(unsigned long charnum, unsigned long nchars);
   [Throws]
   SVGPoint getStartPositionOfChar(unsigned long charnum);
--- a/dom/webidl/SVGTextPathElement.webidl
+++ b/dom/webidl/SVGTextPathElement.webidl
@@ -17,15 +17,18 @@ interface SVGTextPathElement : SVGTextCo
   const unsigned short TEXTPATH_METHODTYPE_ALIGN = 1;
   const unsigned short TEXTPATH_METHODTYPE_STRETCH = 2;
 
   // textPath Spacing Types
   const unsigned short TEXTPATH_SPACINGTYPE_UNKNOWN = 0;
   const unsigned short TEXTPATH_SPACINGTYPE_AUTO = 1;
   const unsigned short TEXTPATH_SPACINGTYPE_EXACT = 2;
 
+  [Constant]
   readonly attribute SVGAnimatedLength startOffset;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration method;
+  [Constant]
   readonly attribute SVGAnimatedEnumeration spacing;
 };
 
 SVGTextPathElement implements SVGURIReference;
 
--- a/dom/webidl/SVGTextPositioningElement.webidl
+++ b/dom/webidl/SVGTextPositioningElement.webidl
@@ -6,15 +6,20 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGTextPositioningElement : SVGTextContentElement {
+  [Constant]
   readonly attribute SVGAnimatedLengthList x;
+  [Constant]
   readonly attribute SVGAnimatedLengthList y;
+  [Constant]
   readonly attribute SVGAnimatedLengthList dx;
+  [Constant]
   readonly attribute SVGAnimatedLengthList dy;
+  [Constant]
   readonly attribute SVGAnimatedNumberList rotate;
 };
 
--- a/dom/webidl/SVGURIReference.webidl
+++ b/dom/webidl/SVGURIReference.webidl
@@ -7,11 +7,12 @@
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 [NoInterfaceObject]
 interface SVGURIReference {
+  [Constant]
   readonly attribute SVGAnimatedString href;
 };
 
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -51,17 +51,20 @@ ServiceWorkerContainer::DisconnectFromOw
 
 void
 ServiceWorkerContainer::RemoveReadyPromise()
 {
   nsCOMPtr<nsPIDOMWindow> window = GetOwner();
   if (window) {
     nsCOMPtr<nsIServiceWorkerManager> swm =
       mozilla::services::GetServiceWorkerManager();
-    MOZ_ASSERT(swm);
+    if (!swm) {
+      // If the browser is shutting down, we don't need to remove the promise.
+      return;
+    }
 
     swm->RemoveReadyPromise(window);
   }
 }
 
 JSObject*
 ServiceWorkerContainer::WrapObject(JSContext* aCx)
 {
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -64,16 +64,21 @@ public:
   WaitUntil(Promise& aPromise);
 
   already_AddRefed<Promise>
   GetPromise() const
   {
     nsRefPtr<Promise> p = mPromise;
     return p.forget();
   }
+
+  virtual ExtendableEvent* AsExtendableEvent() MOZ_OVERRIDE
+  {
+    return this;
+  }
 };
 
 class InstallEvent MOZ_FINAL : public ExtendableEvent
 {
   // FIXME(nsm): Bug 982787 will allow actually populating this.
   nsRefPtr<ServiceWorker> mActiveWorker;
   bool mActivateImmediately;
 
@@ -127,12 +132,17 @@ public:
     mActivateImmediately = true;
   };
 
   bool
   ActivateImmediately() const
   {
     return mActivateImmediately;
   }
+
+  InstallEvent* AsInstallEvent() MOZ_OVERRIDE
+  {
+    return this;
+  }
 };
 
 END_WORKERS_NAMESPACE
 #endif /* mozilla_dom_workers_serviceworkerevents_h__ */
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -101,36 +101,16 @@ ServiceWorkerRegistrationInfo::ServiceWo
 
 ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
 {
   if (IsControllingDocuments()) {
     NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
   }
 }
 
-class QueueFireUpdateFoundRunnable MOZ_FINAL : public nsRunnable
-{
-  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
-public:
-  explicit QueueFireUpdateFoundRunnable(ServiceWorkerRegistrationInfo* aReg)
-    : mRegistration(aReg)
-  {
-    MOZ_ASSERT(aReg);
-  }
-
-  NS_IMETHOD
-  Run()
-  {
-    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    swm->FireEventOnServiceWorkerRegistrations(mRegistration,
-                                               NS_LITERAL_STRING("updatefound"));
-    return NS_OK;
-  }
-};
-
 //////////////////////////
 // ServiceWorkerManager //
 //////////////////////////
 
 NS_IMPL_ADDREF(ServiceWorkerManager)
 NS_IMPL_RELEASE(ServiceWorkerManager)
 
 NS_INTERFACE_MAP_BEGIN(ServiceWorkerManager)
@@ -158,70 +138,118 @@ ServiceWorkerManager::CleanupServiceWork
                                                       void *aUnused)
 {
   aDomainInfo->mServiceWorkerRegistrationInfos.Clear();
   return PL_DHASH_NEXT;
 }
 
 class ServiceWorkerRegisterJob;
 
-class FinishInstallRunnable MOZ_FINAL : public nsRunnable
+class ContinueLifecycleTask : public nsISupports
+{
+  NS_DECL_ISUPPORTS
+
+protected:
+  virtual ~ContinueLifecycleTask()
+  { }
+
+public:
+  virtual void ContinueAfterWorkerEvent(bool aSuccess,
+                                        bool aActivateImmediately) = 0;
+};
+
+NS_IMPL_ISUPPORTS0(ContinueLifecycleTask);
+
+class ContinueInstallTask MOZ_FINAL : public ContinueLifecycleTask
 {
-  nsMainThreadPtrHandle<nsISupports> mJob;
+  nsRefPtr<ServiceWorkerRegisterJob> mJob;
+
+public:
+  explicit ContinueInstallTask(ServiceWorkerRegisterJob* aJob)
+    : mJob(aJob)
+  { }
+
+  void ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately) MOZ_OVERRIDE;
+};
+
+class ContinueActivateTask MOZ_FINAL : public ContinueLifecycleTask
+{
+  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+
+public:
+  explicit ContinueActivateTask(ServiceWorkerRegistrationInfo* aReg)
+    : mRegistration(aReg)
+  { }
+
+  void
+  ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately /* unused */) MOZ_OVERRIDE
+  {
+    mRegistration->FinishActivate(aSuccess);
+  }
+};
+
+class ContinueLifecycleRunnable MOZ_FINAL : public nsRunnable
+{
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mSuccess;
   bool mActivateImmediately;
 
 public:
-  explicit FinishInstallRunnable(const nsMainThreadPtrHandle<nsISupports>& aJob,
-                                 bool aSuccess,
-                                 bool aActivateImmediately)
-    : mJob(aJob)
+  ContinueLifecycleRunnable(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
+                            bool aSuccess,
+                            bool aActivateImmediately)
+    : mTask(aTask)
     , mSuccess(aSuccess)
     , mActivateImmediately(aActivateImmediately)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
 
   NS_IMETHOD
-  Run() MOZ_OVERRIDE;
+  Run() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    mTask->ContinueAfterWorkerEvent(mSuccess, mActivateImmediately);
+    return NS_OK;
+  }
 };
 
 /*
  * Fires 'install' event on the ServiceWorkerGlobalScope. Modifies busy count
  * since it fires the event. This is ok since there can't be nested
  * ServiceWorkers, so the parent thread -> worker thread requirement for
  * runnables is satisfied.
  */
-class InstallEventRunnable MOZ_FINAL : public WorkerRunnable
+class LifecycleEventWorkerRunnable MOZ_FINAL : public WorkerRunnable
 {
-  nsMainThreadPtrHandle<nsISupports> mJob;
-  nsCString mScope;
+  nsString mEventName;
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
 
 public:
-  InstallEventRunnable(WorkerPrivate* aWorkerPrivate,
-                       const nsMainThreadPtrHandle<nsISupports>& aJob,
-                       const nsCString& aScope)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-        mJob(aJob),
-        mScope(aScope)
+  LifecycleEventWorkerRunnable(WorkerPrivate* aWorkerPrivate,
+                               const nsString& aEventName,
+                               const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask)
+      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
+      , mEventName(aEventName)
+      , mTask(aTask)
   {
     AssertIsOnMainThread();
     MOZ_ASSERT(aWorkerPrivate);
   }
 
   bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
     MOZ_ASSERT(aWorkerPrivate);
-    return DispatchInstallEvent(aCx, aWorkerPrivate);
+    return DispatchLifecycleEvent(aCx, aWorkerPrivate);
   }
 
 private:
   bool
-  DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
+  DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
 
 };
 
 class ServiceWorkerUpdateFinishCallback
 {
 protected:
   virtual ~ServiceWorkerUpdateFinishCallback()
   { }
@@ -351,17 +379,17 @@ public:
 
     return true;
   }
 };
 
 class ServiceWorkerRegisterJob MOZ_FINAL : public ServiceWorkerJob,
                                            public nsIStreamLoaderObserver
 {
-  friend class FinishInstallRunnable;
+  friend class ContinueInstallTask;
 
   nsCString mScope;
   nsCString mScriptSpec;
   nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
   nsRefPtr<ServiceWorkerUpdateFinishCallback> mCallback;
 
   ~ServiceWorkerRegisterJob()
   { }
@@ -499,18 +527,17 @@ public:
   }
 
   // Public so our error handling code can use it.
   void
   Fail(const ErrorEventInit& aError)
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateFailed(aError);
-    mCallback = nullptr;
-    Done(NS_ERROR_DOM_JS_EXCEPTION);
+    FailCommon(NS_ERROR_DOM_JS_EXCEPTION);
   }
 
   // Public so our error handling code can continue with a successful worker.
   void
   ContinueInstall()
   {
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
@@ -526,39 +553,38 @@ public:
 
     swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                    WhichServiceWorker::INSTALLING_WORKER);
     mRegistration->mInstallingWorker = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec);
     mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
 
     Succeed();
 
-    nsRefPtr<QueueFireUpdateFoundRunnable> upr =
-      new QueueFireUpdateFoundRunnable(mRegistration);
+    nsCOMPtr<nsIRunnable> upr =
+      NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm,
+                                                                  &ServiceWorkerManager::FireUpdateFound,
+                                                                  mRegistration);
     NS_DispatchToMainThread(upr);
 
-    // XXXnsm this leads to double fetches right now, ideally we'll be able to
-    // use the persistent cache later.
-    nsRefPtr<ServiceWorkerJob> upcasted = this;
-    nsMainThreadPtrHandle<nsISupports> handle(
-        new nsMainThreadPtrHolder<nsISupports>(upcasted));
+    nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
+        new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueInstallTask(this)));
 
     nsRefPtr<ServiceWorker> serviceWorker;
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mInstallingWorker->ScriptSpec(),
                                mRegistration->mScope,
                                getter_AddRefs(serviceWorker));
 
     if (NS_WARN_IF(NS_FAILED(rv))) {
       ContinueAfterInstallEvent(false /* success */, false /* activate immediately */);
       return;
     }
 
-    nsRefPtr<InstallEventRunnable> r =
-      new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle, mRegistration->mScope);
+    nsRefPtr<LifecycleEventWorkerRunnable> r =
+      new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("install"), handle);
 
     AutoJSAPI jsapi;
     jsapi.Init();
     r->Dispatch(jsapi.cx());
   }
 
 private:
   void
@@ -629,26 +655,49 @@ private:
   void
   Succeed()
   {
     MOZ_ASSERT(mCallback);
     mCallback->UpdateSucceeded(mRegistration);
     mCallback = nullptr;
   }
 
+  void
+  FailCommon(nsresult aRv)
+  {
+    mCallback = nullptr;
+    MaybeRemoveRegistration();
+    // Ensures that the job can't do anything useful from this point on.
+    mRegistration = nullptr;
+    Done(aRv);
+  }
+
   // This MUST only be called when the job is still performing actions related
   // to registration or update. After the spec resolves the update promise, use
   // Done() with the failure code instead.
   void
-  Fail(nsresult rv)
+  Fail(nsresult aRv)
   {
     MOZ_ASSERT(mCallback);
-    mCallback->UpdateFailed(rv);
-    mCallback = nullptr;
-    Done(rv);
+    mCallback->UpdateFailed(aRv);
+    FailCommon(aRv);
+  }
+
+  void
+  MaybeRemoveRegistration()
+  {
+    MOZ_ASSERT(mRegistration);
+    nsRefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
+    if (!newest) {
+      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+      nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+        swm->GetDomainInfo(mRegistration->mScope);
+      MOZ_ASSERT(domainInfo);
+      domainInfo->RemoveRegistration(mRegistration);
+    }
   }
 
   void
   ContinueAfterInstallEvent(bool aSuccess, bool aActivateImmediately)
   {
     // By this point the callback should've been notified about success or fail
     // and nulled.
     MOZ_ASSERT(!mCallback);
@@ -661,16 +710,17 @@ private:
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
     // "If installFailed is true"
     if (!aSuccess) {
       mRegistration->mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
       mRegistration->mInstallingWorker = nullptr;
       swm->InvalidateServiceWorkerRegistrationWorker(mRegistration,
                                                      WhichServiceWorker::INSTALLING_WORKER);
+      MaybeRemoveRegistration();
       return Done(NS_ERROR_DOM_ABORT_ERR);
     }
 
     // "If registration's waiting worker is not null"
     if (mRegistration->mWaitingWorker) {
       // FIXME(nsm): Terminate
       mRegistration->mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
     }
@@ -700,16 +750,22 @@ ContinueUpdateRunnable::Run()
 {
   AssertIsOnMainThread();
   nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
   nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
   upjob->ContinueInstall();
   return NS_OK;
 }
 
+void
+ContinueInstallTask::ContinueAfterWorkerEvent(bool aSuccess, bool aActivateImmediately)
+{
+  mJob->ContinueAfterInstallEvent(aSuccess, aActivateImmediately);
+}
+
 // If we return an error code here, the ServiceWorkerContainer will
 // automatically reject the Promise.
 NS_IMETHODIMP
 ServiceWorkerManager::Register(nsIDOMWindow* aWindow,
                                const nsAString& aScope,
                                const nsAString& aScriptURL,
                                nsISupports** aPromise)
 {
@@ -848,82 +904,86 @@ ServiceWorkerManager::Register(nsIDOMWin
   nsRefPtr<ServiceWorkerRegisterJob> job =
     new ServiceWorkerRegisterJob(queue, cleanedScope, spec, cb);
   queue->Append(job);
 
   promise.forget(aPromise);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-FinishInstallRunnable::Run()
+/*
+ * Used to handle ExtendableEvent::waitUntil() and proceed with
+ * installation/activation.
+ */
+class LifecycleEventPromiseHandler MOZ_FINAL : public PromiseNativeHandler
 {
-  AssertIsOnMainThread();
-  nsRefPtr<ServiceWorkerJob> job = static_cast<ServiceWorkerJob*>(mJob.get());
-  nsRefPtr<ServiceWorkerRegisterJob> upjob = static_cast<ServiceWorkerRegisterJob*>(job.get());
-  MOZ_ASSERT(upjob);
-  upjob->ContinueAfterInstallEvent(mSuccess, mActivateImmediately);
-  return NS_OK;
-}
-
-/*
- * Used to handle InstallEvent::waitUntil() and proceed with installation.
- */
-class FinishInstallHandler MOZ_FINAL : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<nsISupports> mJob;
+  nsMainThreadPtrHandle<ContinueLifecycleTask> mTask;
   bool mActivateImmediately;
 
   virtual
-  ~FinishInstallHandler()
+  ~LifecycleEventPromiseHandler()
   { }
 
 public:
-  FinishInstallHandler(const nsMainThreadPtrHandle<nsISupports>& aJob,
-                       bool aActivateImmediately)
-    : mJob(aJob)
+  LifecycleEventPromiseHandler(const nsMainThreadPtrHandle<ContinueLifecycleTask>& aTask,
+                               bool aActivateImmediately)
+    : mTask(aTask)
     , mActivateImmediately(aActivateImmediately)
   {
     MOZ_ASSERT(!NS_IsMainThread());
   }
 
   void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
   {
     WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
     MOZ_ASSERT(workerPrivate);
     workerPrivate->AssertIsOnWorkerThread();
 
-    nsRefPtr<FinishInstallRunnable> r = new FinishInstallRunnable(mJob, true, mActivateImmediately);
+    nsRefPtr<ContinueLifecycleRunnable> r =
+      new ContinueLifecycleRunnable(mTask, true /* success */, mActivateImmediately);
     NS_DispatchToMainThread(r);
   }
 
   void
   RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
   {
-    nsRefPtr<FinishInstallRunnable> r = new FinishInstallRunnable(mJob, false, mActivateImmediately);
+    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(workerPrivate);
+    workerPrivate->AssertIsOnWorkerThread();
+
+    nsRefPtr<ContinueLifecycleRunnable> r =
+      new ContinueLifecycleRunnable(mTask, false /* success */, mActivateImmediately);
     NS_DispatchToMainThread(r);
   }
 };
 
 bool
-InstallEventRunnable::DispatchInstallEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
+LifecycleEventWorkerRunnable::DispatchLifecycleEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
 {
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-  InstallEventInit init;
-  init.mBubbles = false;
-  init.mCancelable = true;
+
+  nsRefPtr<ExtendableEvent> event;
+  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
 
-  // FIXME(nsm): Bug 982787 pass previous active worker.
-
-  // FIXME(nsm): Set error handler so we can grab handler errors.
-  nsRefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
-  nsRefPtr<InstallEvent> event =
-    InstallEvent::Constructor(target, NS_LITERAL_STRING("install"), init);
+  if (mEventName.EqualsASCII("install")) {
+    // FIXME(nsm): Bug 982787 pass previous active worker.
+    InstallEventInit init;
+    init.mBubbles = false;
+    init.mCancelable = true;
+    event = InstallEvent::Constructor(target, mEventName, init);
+  } else if (mEventName.EqualsASCII("activate")) {
+    ExtendableEventInit init;
+    init.mBubbles = false;
+    init.mCancelable = true;
+    event = ExtendableEvent::Constructor(target, mEventName, init);
+  } else {
+    MOZ_CRASH("Unexpected lifecycle event");
+  }
 
   event->SetTrusted(true);
 
   nsRefPtr<Promise> waitUntilPromise;
 
   ErrorResult result;
   result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
@@ -947,145 +1007,31 @@ InstallEventRunnable::DispatchInstallEve
     waitUntilPromise = Promise::Reject(sgo, aCx,
                                        JS::UndefinedHandleValue, result);
   }
 
   if (result.Failed()) {
     return false;
   }
 
-  nsRefPtr<FinishInstallHandler> handler =
-    new FinishInstallHandler(mJob, event->ActivateImmediately());
+  // activateimmediately is only relevant to "install" event.
+  bool activateImmediately = false;
+  InstallEvent* installEvent = event->AsInstallEvent();
+  if (installEvent) {
+    activateImmediately = installEvent->ActivateImmediately();
+    // FIXME(nsm): Set activeWorker to the correct thing.
+    // FIXME(nsm): Install error handler for any listener errors.
+  }
+
+  nsRefPtr<LifecycleEventPromiseHandler> handler =
+    new LifecycleEventPromiseHandler(mTask, activateImmediately);
   waitUntilPromise->AppendNativeHandler(handler);
   return true;
 }
 
-class FinishActivationRunnable MOZ_FINAL : public nsRunnable
-{
-  bool mSuccess;
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  FinishActivationRunnable(bool aSuccess,
-                           const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-    : mSuccess(aSuccess)
-    , mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  NS_IMETHODIMP
-  Run()
-  {
-    AssertIsOnMainThread();
-
-    mRegistration->FinishActivate(mSuccess);
-    return NS_OK;
-  }
-};
-
-class FinishActivateHandler MOZ_FINAL : public PromiseNativeHandler
-{
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  explicit FinishActivateHandler(const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-    : mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-  }
-
-  virtual
-  ~FinishActivateHandler()
-  { }
-
-  void
-  ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
-  {
-    nsRefPtr<FinishActivationRunnable> r = new FinishActivationRunnable(true /* success */, mRegistration);
-    NS_DispatchToMainThread(r);
-  }
-
-  void
-  RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) MOZ_OVERRIDE
-  {
-    nsRefPtr<FinishActivationRunnable> r = new FinishActivationRunnable(false /* success */, mRegistration);
-    NS_DispatchToMainThread(r);
-  }
-};
-
-class ActivateEventRunnable : public WorkerRunnable
-{
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
-
-public:
-  ActivateEventRunnable(WorkerPrivate* aWorkerPrivate,
-                        const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
-      : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount),
-        mRegistration(aRegistration)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-  }
-
-  bool
-  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate);
-    return DispatchActivateEvent(aCx, aWorkerPrivate);
-  }
-
-private:
-  bool
-  DispatchActivateEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
-  {
-    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
-    nsRefPtr<EventTarget> target = do_QueryObject(aWorkerPrivate->GlobalScope());
-
-    // FIXME(nsm): Set activeWorker to the correct thing.
-    EventInit init;
-    init.mBubbles = false;
-    init.mCancelable = true;
-    nsRefPtr<ExtendableEvent> event =
-      ExtendableEvent::Constructor(target, NS_LITERAL_STRING("activate"), init);
-
-    event->SetTrusted(true);
-
-    nsRefPtr<Promise> waitUntilPromise;
-
-    // FIXME(nsm): Install error handler for any listener errors.
-    ErrorResult result;
-    result = target->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
-    WidgetEvent* internalEvent = event->GetInternalNSEvent();
-    if (!result.Failed() && !internalEvent->mFlags.mExceptionHasBeenRisen) {
-      waitUntilPromise = event->GetPromise();
-      if (!waitUntilPromise) {
-        nsCOMPtr<nsIGlobalObject> global =
-          do_QueryObject(aWorkerPrivate->GlobalScope());
-        waitUntilPromise =
-          Promise::Resolve(global,
-                           aCx, JS::UndefinedHandleValue, result);
-      }
-    } else {
-      nsCOMPtr<nsIGlobalObject> global =
-        do_QueryObject(aWorkerPrivate->GlobalScope());
-      // Continue with a canceled install.
-      waitUntilPromise = Promise::Reject(global, aCx,
-                                         JS::UndefinedHandleValue, result);
-    }
-
-    if (result.Failed()) {
-      return false;
-    }
-
-    nsRefPtr<FinishActivateHandler> handler = new FinishActivateHandler(mRegistration);
-    waitUntilPromise->AppendNativeHandler(handler);
-    return true;
-  }
-};
-
 void
 ServiceWorkerRegistrationInfo::TryToActivate()
 {
   mWaitingToActivate = true;
   if (!IsControllingDocuments()) {
     Activate();
   }
 }
@@ -1133,21 +1079,21 @@ ServiceWorkerRegistrationInfo::Activate(
     swm->CreateServiceWorker(mActiveWorker->ScriptSpec(),
                              mScope,
                              getter_AddRefs(serviceWorker));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FinishActivate(false /* success */);
     return;
   }
 
-  nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
-    new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this));
+  nsMainThreadPtrHandle<ContinueLifecycleTask> handle(
+    new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this)));
 
-  nsRefPtr<ActivateEventRunnable> r =
-    new ActivateEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
+  nsRefPtr<LifecycleEventWorkerRunnable> r =
+    new LifecycleEventWorkerRunnable(serviceWorker->GetWorkerPrivate(), NS_LITERAL_STRING("activate"), handle);
 
   AutoJSAPI jsapi;
   jsapi.Init();
   r->Dispatch(jsapi.cx());
 }
 
 /*
  * Implements the async aspects of the getRegistrations algorithm.
@@ -1470,95 +1416,127 @@ ServiceWorkerManager::CheckReadyPromise(
       new ServiceWorkerRegistration(aWindow, scope);
     aPromise->MaybeResolve(swr);
     return true;
   }
 
   return false;
 }
 
+class ServiceWorkerUnregisterJob MOZ_FINAL : public ServiceWorkerJob
+{
+  nsRefPtr<ServiceWorkerRegistrationInfo> mRegistration;
+  const nsCString mScope;
+  nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
+
+  ~ServiceWorkerUnregisterJob()
+  { }
+
+public:
+  ServiceWorkerUnregisterJob(ServiceWorkerJobQueue* aQueue,
+                             const nsACString& aScope,
+                             nsIServiceWorkerUnregisterCallback* aCallback)
+    : ServiceWorkerJob(aQueue)
+    , mScope(aScope)
+    , mCallback(aCallback)
+  {
+    AssertIsOnMainThread();
+  }
+
+  void
+  Start() MOZ_OVERRIDE
+  {
+    AssertIsOnMainThread();
+    nsCOMPtr<nsIRunnable> r =
+      NS_NewRunnableMethod(this, &ServiceWorkerUnregisterJob::UnregisterAndDone);
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r)));
+  }
+
+private:
+  // You probably want UnregisterAndDone().
+  nsresult
+  Unregister()
+  {
+    AssertIsOnMainThread();
+
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+
+    nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+      swm->GetDomainInfo(mScope);
+    MOZ_ASSERT(domainInfo);
+
+    // "Let registration be the result of running [[Get Registration]]
+    // algorithm passing scope as the argument."
+    nsRefPtr<ServiceWorkerRegistrationInfo> registration;
+    if (!domainInfo->mServiceWorkerRegistrationInfos.Get(mScope,
+                                                         getter_AddRefs(registration))) {
+      // "If registration is null, then, resolve promise with false."
+      return mCallback->UnregisterSucceeded(false);
+    }
+
+    MOZ_ASSERT(registration);
+
+    // "Set registration's uninstalling flag."
+    registration->mPendingUninstall = true;
+    // "Resolve promise with true"
+    nsresult rv = mCallback->UnregisterSucceeded(true);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    // "If no service worker client is using registration..."
+    if (!registration->IsControllingDocuments()) {
+      // "If registration's uninstalling flag is set.."
+      if (!registration->mPendingUninstall) {
+        return NS_OK;
+      }
+
+      // "Invoke [[Clear Registration]]..."
+      registration->Clear();
+      domainInfo->RemoveRegistration(registration);
+    }
+
+    return NS_OK;
+  }
+
+  // The unregister job is done irrespective of success or failure of any sort.
+  void
+  UnregisterAndDone()
+  {
+    Done(Unregister());
+  }
+};
+
 NS_IMETHODIMP
 ServiceWorkerManager::Unregister(nsIServiceWorkerUnregisterCallback* aCallback,
                                  const nsAString& aScope)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aCallback);
 
+// This is not accessible by content, and callers should always ensure scope is
+// a correct URI, so this is wrapped in DEBUG
+#ifdef DEBUG
   nsCOMPtr<nsIURI> scopeURI;
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
-
-  /*
-   * Implements the async aspects of the unregister algorithm.
-   */
-  class UnregisterRunnable : public nsRunnable
-  {
-    nsCOMPtr<nsIServiceWorkerUnregisterCallback> mCallback;
-    nsCOMPtr<nsIURI> mScopeURI;
-
-  public:
-    UnregisterRunnable(nsIServiceWorkerUnregisterCallback* aCallback,
-                       nsIURI* aScopeURI)
-      : mCallback(aCallback), mScopeURI(aScopeURI)
-    {
-      AssertIsOnMainThread();
-    }
-
-    NS_IMETHODIMP
-    Run()
-    {
-      AssertIsOnMainThread();
-
-      nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-
-      nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
-        swm->GetDomainInfo(mScopeURI);
-      MOZ_ASSERT(domainInfo);
-
-      nsCString spec;
-      nsresult rv = mScopeURI->GetSpecIgnoringRef(spec);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return mCallback->UnregisterFailed();
-      }
+#endif
 
-      nsRefPtr<ServiceWorkerRegistrationInfo> registration;
-      if (!domainInfo->mServiceWorkerRegistrationInfos.Get(spec,
-                                                           getter_AddRefs(registration))) {
-        return mCallback->UnregisterSucceeded(false);
-      }
-
-      MOZ_ASSERT(registration);
-
-      registration->mPendingUninstall = true;
-      rv = mCallback->UnregisterSucceeded(true);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
+  NS_ConvertUTF16toUTF8 scope(aScope);
+  nsRefPtr<ServiceWorkerManager::ServiceWorkerDomainInfo> domainInfo =
+    GetDomainInfo(scope);
+  ServiceWorkerJobQueue* queue = domainInfo->GetOrCreateJobQueue(scope);
+  MOZ_ASSERT(queue);
 
-      // The "Wait until no document is using registration" can actually be
-      // handled by [[HandleDocumentUnload]] in Bug 1041340, so we simply check
-      // if the document is currently in use here.
-      if (!registration->IsControllingDocuments()) {
-        if (!registration->mPendingUninstall) {
-          return NS_OK;
-        }
-
-        registration->Clear();
-        domainInfo->RemoveRegistration(registration);
-      }
-
-      return NS_OK;
-    }
-  };
-
-  nsRefPtr<nsIRunnable> unregisterRunnable =
-    new UnregisterRunnable(aCallback, scopeURI);
-  return NS_DispatchToCurrentThread(unregisterRunnable);
+  nsRefPtr<ServiceWorkerUnregisterJob> job =
+    new ServiceWorkerUnregisterJob(queue, scope, aCallback);
+  queue->Append(job);
+  return NS_OK;
 }
 
 /* static */
 already_AddRefed<ServiceWorkerManager>
 ServiceWorkerManager::GetInstance()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
   nsRefPtr<ServiceWorkerManager> concrete = do_QueryObject(swm);
@@ -2242,18 +2220,18 @@ ServiceWorkerManager::Update(const nsASt
   }
 
   ServiceWorkerJobQueue* queue = domainInfo->GetOrCreateJobQueue(scope);
   MOZ_ASSERT(queue);
 
   nsRefPtr<ServiceWorkerUpdateFinishCallback> cb =
     new ServiceWorkerUpdateFinishCallback();
 
-  nsRefPtr<ServiceWorkerRegisterJob> job
-    = new ServiceWorkerRegisterJob(queue, registration, cb);
+  nsRefPtr<ServiceWorkerRegisterJob> job =
+    new ServiceWorkerRegisterJob(queue, registration, cb);
   queue->Append(job);
   return NS_OK;
 }
 
 namespace {
 
 class MOZ_STACK_CLASS FilterRegistrationData
 {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -270,17 +270,17 @@ class ServiceWorkerManager MOZ_FINAL : p
 {
   friend class ActivationRunnable;
   friend class ServiceWorkerRegistrationInfo;
   friend class ServiceWorkerRegisterJob;
   friend class GetReadyPromiseRunnable;
   friend class GetRegistrationsRunnable;
   friend class GetRegistrationRunnable;
   friend class QueueFireUpdateFoundRunnable;
-  friend class UnregisterRunnable;
+  friend class ServiceWorkerUnregisterJob;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
   {
@@ -457,16 +457,23 @@ private:
   QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
                                              const nsAString& aName);
 
   void
   FireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
                                         const nsAString& aName);
 
   void
+  FireUpdateFound(ServiceWorkerRegistrationInfo* aRegistration)
+  {
+    FireEventOnServiceWorkerRegistrations(aRegistration,
+                                          NS_LITERAL_STRING("updatefound"));
+  }
+
+  void
   FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
 
   void
   StorePendingReadyPromise(nsPIDOMWindow* aWindow, nsIURI* aURI, Promise* aPromise);
 
   void
   CheckPendingReadyPromises();
 
--- a/dom/workers/ServiceWorkerRegistration.cpp
+++ b/dom/workers/ServiceWorkerRegistration.cpp
@@ -159,32 +159,33 @@ ServiceWorkerRegistration::Unregister(Er
   nsCOMPtr<nsIDocument> document = GetOwner()->GetExtantDoc();
   if (!document) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   nsCOMPtr<nsIURI> scopeURI;
   nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
+  // "If the origin of scope is not client's origin..."
   nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), mScope, nullptr, baseURI);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIPrincipal> documentPrincipal = document->NodePrincipal();
   rv = documentPrincipal->CheckMayLoad(scopeURI, true /* report */,
                                        false /* allowIfInheritsPrinciple */);
   if (NS_FAILED(rv)) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   nsAutoCString uriSpec;
-  aRv = scopeURI->GetSpec(uriSpec);
+  aRv = scopeURI->GetSpecIgnoringRef(uriSpec);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   nsCOMPtr<nsIServiceWorkerManager> swm =
     do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRv.Throw(rv);
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -22,14 +22,13 @@ support-files =
 [test_unregister.html]
 skip-if = true # bug 1094375
 [test_installation_simple.html]
 skip-if = true # bug 1094375
 [test_get_serviced.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
-skip-if = true # bug 1124743
 [test_controller.html]
 [test_workerUpdate.html]
 skip-if = true # Enable after Bug 982726 postMessage is landed.
 [test_workerUnregister.html]
 skip-if = true # Enable after Bug 982726 postMessage is landed.
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -101,16 +101,20 @@
         ok(e.name === "NetworkError", "Should fail with NetworkError");
       });
   }
 
   function parseError() {
     var p = navigator.serviceWorker.register("parse_error_worker.js", { scope: "parse_error/" });
     return p.then(function(wr) {
       ok(false, "Registration should fail with parse error");
+      return navigator.serviceWorker.getRegistration("parse_error/").then(function(swr) {
+        // See https://github.com/slightlyoff/ServiceWorker/issues/547
+        is(swr, undefined, "A failed registration for a scope with no prior controllers should clear itself");
+      });
     }, function(e) {
     info("NSM " + e.name);
       ok(e instanceof Error, "Registration should fail with parse error");
     });
   }
 
   // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
 
--- a/dom/workers/test/serviceworkers/test_unregister.html
+++ b/dom/workers/test/serviceworkers/test_unregister.html
@@ -81,35 +81,19 @@
 
     return testPromise.then(function() {
       div.removeChild(ifr);
     });
   }
 
   function runTest() {
     simpleRegister()
-      .then(function(v) {
-        info("simpleRegister() promise resolved");
-      })
       .then(testControlled)
-      .then(function(v) {
-        info("testControlled() promise resolved");
-      }, function(e) {
-        info("testControlled() promise rejected " + e);
-      })
       .then(unregister)
-      .then(function(v) {
-        info("unregister() promise resolved");
-      })
       .then(testUncontrolled)
-      .then(function(v) {
-        info("testUncontrolled() promise resolved");
-      }, function(e) {
-        info("testUncontrolled() promise rejected " + e);
-      })
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -454,17 +454,17 @@ public:
   /**
    * Set AsyncTransactionTracker of RemoveTextureFromCompositableAsync() transaction.
    */
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) {}
 
   /**
    * This function waits until the buffer is no longer being used.
    */
-  virtual void WaitForBufferOwnership() {}
+  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) {}
 
   /**
    * Track how much of this texture is wasted.
    * For example we might allocate a 256x256 tile but only use 10x10.
    */
    void SetWaste(int aWasteArea) {
      mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
    }
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -83,23 +83,27 @@ GrallocTextureClientOGL::ToSurfaceDescri
 
 void
 GrallocTextureClientOGL::SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker)
 {
   mRemoveFromCompositableTracker = aTracker;
 }
 
 void
-GrallocTextureClientOGL::WaitForBufferOwnership()
+GrallocTextureClientOGL::WaitForBufferOwnership(bool aWaitReleaseFence)
 {
   if (mRemoveFromCompositableTracker) {
     mRemoveFromCompositableTracker->WaitComplete();
     mRemoveFromCompositableTracker = nullptr;
   }
 
+  if (!aWaitReleaseFence) {
+    return;
+  }
+
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
    if (mReleaseFenceHandle.IsValid()) {
      android::sp<Fence> fence = mReleaseFenceHandle.mFence;
 #if ANDROID_VERSION == 17
      fence->waitForever(1000, "GrallocTextureClientOGL::Lock");
      // 1000 is what Android uses. It is a warning timeout in ms.
      // This timeout was removed in ANDROID_VERSION 18.
 #else
@@ -117,26 +121,42 @@ GrallocTextureClientOGL::Lock(OpenMode a
   if (!IsValid() || !IsAllocated()) {
     return false;
   }
 
   if (mMappedBuffer) {
     return true;
   }
 
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+  WaitForBufferOwnership(false /* aWaitReleaseFence */);
+#else
   WaitForBufferOwnership();
+#endif
 
   uint32_t usage = 0;
   if (aMode & OpenMode::OPEN_READ) {
     usage |= GRALLOC_USAGE_SW_READ_OFTEN;
   }
   if (aMode & OpenMode::OPEN_WRITE) {
     usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
   }
-  int32_t rv = mGraphicBuffer->lock(usage, reinterpret_cast<void**>(&mMappedBuffer));
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 21
+  android::sp<Fence> fence = android::Fence::NO_FENCE;
+  if (mReleaseFenceHandle.IsValid()) {
+    fence = mReleaseFenceHandle.mFence;
+  }
+  mReleaseFenceHandle = FenceHandle();
+  int32_t rv = mGraphicBuffer->lockAsync(usage,
+                                         reinterpret_cast<void**>(&mMappedBuffer),
+                                         fence->dup());
+#else
+  int32_t rv = mGraphicBuffer->lock(usage,
+                                    reinterpret_cast<void**>(&mMappedBuffer));
+#endif
   if (rv) {
     mMappedBuffer = nullptr;
     NS_WARNING("Couldn't lock graphic buffer");
     return false;
   }
   return BufferTextureClient::Lock(aMode);
 }
 
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -53,17 +53,17 @@ public:
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
   virtual bool IsAllocated() const MOZ_OVERRIDE;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) MOZ_OVERRIDE;
 
-  virtual void WaitForBufferOwnership() MOZ_OVERRIDE;
+  virtual void WaitForBufferOwnership(bool aWaitReleaseFence = true) MOZ_OVERRIDE;
 
   void InitWith(MaybeMagicGrallocBufferHandle aDesc, gfx::IntSize aSize);
 
   void SetTextureFlags(TextureFlags aFlags) { AddFlags(aFlags); }
 
   gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   android::sp<android::GraphicBuffer> GetGraphicBuffer()
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -22,17 +22,29 @@ MacIOSurfaceTextureHostOGL::MacIOSurface
 bool
 MacIOSurfaceTextureHostOGL::Lock()
 {
   if (!mCompositor || !mSurface) {
     return false;
   }
 
   if (!mTextureSource) {
-    mTextureSource = new MacIOSurfaceTextureSourceOGL(mCompositor, mSurface);
+    GLuint textureHandle;
+    gl::GLContext* gl = mCompositor->gl();
+    gl->fGenTextures(1, &textureHandle);
+    gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+    gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+    mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
+
+    mTextureSource = new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+                                         gfx::IntSize(mSurface->GetDevicePixelWidth(),
+                                                      mSurface->GetDevicePixelHeight()),
+                                         mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8:
+                                                                gfx::SurfaceFormat::R8G8B8X8);
   }
   return true;
 }
 
 void
 MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
   CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -89,16 +89,16 @@ public:
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
 #ifdef MOZ_LAYERS_HAVE_LOG
   virtual const char* Name() MOZ_OVERRIDE { return "MacIOSurfaceTextureHostOGL"; }
 #endif
 
 protected:
   RefPtr<CompositorOGL> mCompositor;
-  RefPtr<MacIOSurfaceTextureSourceOGL> mTextureSource;
+  RefPtr<GLTextureSource> mTextureSource;
   RefPtr<MacIOSurface> mSurface;
 };
 
 }
 }
 
 #endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
--- a/gfx/thebes/gfxPangoFonts.cpp
+++ b/gfx/thebes/gfxPangoFonts.cpp
@@ -200,18 +200,18 @@ public:
     {
         cairo_font_face_set_user_data(mFontFace,
                                       &sFontEntryKey,
                                       nullptr,
                                       nullptr);
         cairo_font_face_destroy(mFontFace);
     }
 
-    virtual void ForgetHBFace();
-    virtual void ReleaseGrFace(gr_face* aFace);
+    virtual void ForgetHBFace() MOZ_OVERRIDE;
+    virtual void ReleaseGrFace(gr_face* aFace) MOZ_OVERRIDE;
 
 protected:
     virtual nsresult
     CopyFontTable(uint32_t aTableTag, FallibleTArray<uint8_t>& aBuffer) MOZ_OVERRIDE;
 
     void MaybeReleaseFTFace();
 
 private:
@@ -666,22 +666,22 @@ public:
 
 #ifdef USE_SKIA
     virtual mozilla::TemporaryRef<mozilla::gfx::GlyphRenderingOptions>
         GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams = nullptr) MOZ_OVERRIDE;
 #endif
 
     // return a cloned font resized and offset to simulate sub/superscript glyphs
     virtual already_AddRefed<gfxFont>
-    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
+    GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) MOZ_OVERRIDE;
 
 protected:
     virtual already_AddRefed<gfxFont> MakeScaledFont(gfxFontStyle *aFontStyle,
                                                      gfxFloat aFontScale);
-    virtual already_AddRefed<gfxFont> GetSmallCapsFont();
+    virtual already_AddRefed<gfxFont> GetSmallCapsFont() MOZ_OVERRIDE;
 
 private:
     gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry,
               const gfxFontStyle *aFontStyle);
 
     // key for locating a gfxFcFont corresponding to a cairo_scaled_font
     static cairo_user_data_key_t sGfxFontKey;
 };
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -164,17 +164,17 @@ public:
 /// this gets called to be large - it is meant for critical errors only.
 
 class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
 {
 public:
   explicit CrashStatsLogForwarder(const char* aKey);
   virtual void Log(const std::string& aString) MOZ_OVERRIDE;
 
-  virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy();
+  virtual std::vector<std::pair<int32_t,std::string> > StringsVectorCopy() MOZ_OVERRIDE;
 
   void SetCircularBufferSize(uint32_t aCapacity);
 
 private:
   // Helpers for the Log()
   bool UpdateStringsVector(const std::string& aString);
   void UpdateCrashReport();
 
--- a/js/public/Conversions.h
+++ b/js/public/Conversions.h
@@ -10,20 +10,238 @@
 #define js_Conversions_h
 
 #include "mozilla/Casting.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/TypeTraits.h"
 
 #include <math.h>
 
+#include "jspubtd.h"
+
+#include "js/RootingAPI.h"
+#include "js/Value.h"
+
+struct JSContext;
+
+namespace js {
+
+/* DO NOT CALL THIS. Use JS::ToBoolean. */
+extern JS_PUBLIC_API(bool)
+ToBooleanSlow(JS::HandleValue v);
+
+/* DO NOT CALL THIS.  Use JS::ToNumber. */
+extern JS_PUBLIC_API(bool)
+ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
+
+/* DO NOT CALL THIS. Use JS::ToInt32. */
+extern JS_PUBLIC_API(bool)
+ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint32. */
+extern JS_PUBLIC_API(bool)
+ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint16. */
+extern JS_PUBLIC_API(bool)
+ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToInt64. */
+extern JS_PUBLIC_API(bool)
+ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToUint64. */
+extern JS_PUBLIC_API(bool)
+ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out);
+
+/* DO NOT CALL THIS. Use JS::ToString. */
+extern JS_PUBLIC_API(JSString*)
+ToStringSlow(JSContext *cx, JS::HandleValue v);
+
+/* DO NOT CALL THIS. Use JS::ToObject. */
+extern JS_PUBLIC_API(JSObject*)
+ToObjectSlow(JSContext *cx, JS::HandleValue v, bool reportScanStack);
+
+} // namespace js
+
 namespace JS {
 
 namespace detail {
 
+#ifdef JS_DEBUG
+/*
+ * Assert that we're not doing GC on cx, that we're in a request as
+ * needed, and that the compartments for cx and v are correct.
+ * Also check that GC would be safe at this point.
+ */
+extern JS_PUBLIC_API(void)
+AssertArgumentsAreSane(JSContext *cx, HandleValue v);
+#else
+inline void AssertArgumentsAreSane(JSContext *cx, HandleValue v)
+{}
+#endif /* JS_DEBUG */
+
+} // namespace detail
+
+/*
+ * ES6 draft 20141224, 7.1.1, second algorithm.
+ *
+ * Most users shouldn't call this -- use JS::ToBoolean, ToNumber, or ToString
+ * instead.  This will typically only be called from custom convert hooks that
+ * wish to fall back to the ES6 default conversion behavior shared by most
+ * objects in JS, codified as OrdinaryToPrimitive.
+ */
+extern JS_PUBLIC_API(bool)
+OrdinaryToPrimitive(JSContext *cx, HandleObject obj, JSType type, MutableHandleValue vp);
+
+/* ES6 draft 20141224, 7.1.2. */
+MOZ_ALWAYS_INLINE bool
+ToBoolean(HandleValue v)
+{
+    if (v.isBoolean())
+        return v.toBoolean();
+    if (v.isInt32())
+        return v.toInt32() != 0;
+    if (v.isNullOrUndefined())
+        return false;
+    if (v.isDouble()) {
+        double d = v.toDouble();
+        return !mozilla::IsNaN(d) && d != 0;
+    }
+    if (v.isSymbol())
+        return true;
+
+    /* The slow path handles strings and objects. */
+    return js::ToBooleanSlow(v);
+}
+
+/* ES6 draft 20141224, 7.1.3. */
+MOZ_ALWAYS_INLINE bool
+ToNumber(JSContext *cx, HandleValue v, double *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isNumber()) {
+        *out = v.toNumber();
+        return true;
+    }
+    return js::ToNumberSlow(cx, v, out);
+}
+
+/* ES6 draft 20141224, ToInteger (specialized for doubles). */
+inline double
+ToInteger(double d)
+{
+    if (d == 0)
+        return d;
+
+    if (!mozilla::IsFinite(d)) {
+        if (mozilla::IsNaN(d))
+            return 0;
+        return d;
+    }
+
+    return d < 0 ? ceil(d) : floor(d);
+}
+
+/* ES6 draft 20141224, 7.1.5. */
+MOZ_ALWAYS_INLINE bool
+ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = v.toInt32();
+        return true;
+    }
+    return js::ToInt32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.6. */
+MOZ_ALWAYS_INLINE bool
+ToUint32(JSContext *cx, HandleValue v, uint32_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint32_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint32Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.8. */
+MOZ_ALWAYS_INLINE bool
+ToUint16(JSContext *cx, HandleValue v, uint16_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint16_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint16Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToInt32, except in its
+ * producing an int64_t.
+ */
+MOZ_ALWAYS_INLINE bool
+ToInt64(JSContext *cx, HandleValue v, int64_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = int64_t(v.toInt32());
+        return true;
+    }
+    return js::ToInt64Slow(cx, v, out);
+}
+
+/*
+ * Non-standard, with behavior similar to that of ToUint32, except in its
+ * producing a uint64_t.
+ */
+MOZ_ALWAYS_INLINE bool
+ToUint64(JSContext *cx, HandleValue v, uint64_t *out)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isInt32()) {
+        *out = uint64_t(v.toInt32());
+        return true;
+    }
+    return js::ToUint64Slow(cx, v, out);
+}
+
+/* ES6 draft 20141224, 7.1.12. */
+MOZ_ALWAYS_INLINE JSString*
+ToString(JSContext *cx, HandleValue v)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isString())
+        return v.toString();
+    return js::ToStringSlow(cx, v);
+}
+
+/* ES6 draft 20141224, 7.1.13. */
+inline JSObject *
+ToObject(JSContext *cx, HandleValue v)
+{
+    detail::AssertArgumentsAreSane(cx, v);
+
+    if (v.isObject())
+        return &v.toObject();
+    return js::ToObjectSlow(cx, v, false);
+}
+
+namespace detail {
+
 /*
  * Convert a double value to ResultType (an unsigned integral type) using
  * ECMAScript-style semantics (that is, in like manner to how ECMAScript's
  * ToInt32 converts to int32_t).
  *
  *   If d is infinite or NaN, return 0.
  *   Otherwise compute d2 = sign(d) * floor(abs(d)), and return the ResultType
  *   value congruent to d2 mod 2**(bit width of ResultType).
@@ -273,27 +491,11 @@ ToInt64(double d)
 
 /* WEBIDL 4.2.11 */
 inline uint64_t
 ToUint64(double d)
 {
     return detail::ToUintWidth<uint64_t>(d);
 }
 
-/* ES5 9.4 ToInteger (specialized for doubles). */
-inline double
-ToInteger(double d)
-{
-    if (d == 0)
-        return d;
-
-    if (!mozilla::IsFinite(d)) {
-        if (mozilla::IsNaN(d))
-            return 0;
-        return d;
-    }
-
-    return d < 0 ? ceil(d) : floor(d);
-}
-
 } // namespace JS
 
 #endif /* js_Conversions_h */
--- a/js/src/builtin/Intl.cpp
+++ b/js/src/builtin/Intl.cpp
@@ -664,19 +664,25 @@ js::intl_Collator(JSContext *cx, unsigne
     // intl_Collator is an intrinsic for self-hosted JavaScript, so it cannot
     // be used with "new", but it still has to be treated as a constructor.
     return Collator(cx, args, true);
 }
 
 static void
 collator_finalize(FreeOp *fop, JSObject *obj)
 {
-    UCollator *coll = static_cast<UCollator*>(obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT).toPrivate());
-    if (coll)
-        ucol_close(coll);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UCOLLATOR_SLOT);
+    if (!slot.isUndefined()) {
+        if (UCollator *coll = static_cast<UCollator*>(slot.toPrivate()))
+            ucol_close(coll);
+    }
 }
 
 static JSObject *
 InitCollatorClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &Collator, cx->names().Collator, 0));
     if (!ctor)
         return nullptr;
@@ -1151,20 +1157,25 @@ js::intl_NumberFormat(JSContext *cx, uns
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return NumberFormat(cx, args, true);
 }
 
 static void
 numberFormat_finalize(FreeOp *fop, JSObject *obj)
 {
-    UNumberFormat *nf =
-        static_cast<UNumberFormat*>(obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT).toPrivate());
-    if (nf)
-        unum_close(nf);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UNUMBER_FORMAT_SLOT);
+    if (!slot.isUndefined()) {
+        if (UNumberFormat *nf = static_cast<UNumberFormat*>(slot.toPrivate()))
+            unum_close(nf);
+    }
 }
 
 static JSObject *
 InitNumberFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &NumberFormat, cx->names().NumberFormat, 0));
     if (!ctor)
         return nullptr;
@@ -1605,19 +1616,25 @@ js::intl_DateTimeFormat(JSContext *cx, u
     // cannot be used with "new", but it still has to be treated as a
     // constructor.
     return DateTimeFormat(cx, args, true);
 }
 
 static void
 dateTimeFormat_finalize(FreeOp *fop, JSObject *obj)
 {
-    UDateFormat *df = static_cast<UDateFormat*>(obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT).toPrivate());
-    if (df)
-        udat_close(df);
+    // This is-undefined check shouldn't be necessary, but for internal
+    // brokenness in object allocation code.  For the moment, hack around it by
+    // explicitly guarding against the possibility of the reserved slot not
+    // containing a private.  See bug 949220.
+    const Value &slot = obj->as<NativeObject>().getReservedSlot(UDATE_FORMAT_SLOT);
+    if (!slot.isUndefined()) {
+        if (UDateFormat *df = static_cast<UDateFormat*>(slot.toPrivate()))
+            udat_close(df);
+    }
 }
 
 static JSObject *
 InitDateTimeFormatClass(JSContext *cx, HandleObject Intl, Handle<GlobalObject*> global)
 {
     RootedFunction ctor(cx, global->createConstructor(cx, &DateTimeFormat, cx->names().DateTimeFormat, 0));
     if (!ctor)
         return nullptr;
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -2,16 +2,17 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #ifndef frontend_FullParseHandler_h
 #define frontend_FullParseHandler_h
 
+#include "mozilla/Attributes.h"
 #include "mozilla/PodOperations.h"
 
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 
 namespace js {
 namespace frontend {
 
@@ -537,18 +538,40 @@ class FullParseHandler
 
     ParseNode *newLexicalScope(ObjectBox *blockBox) {
         return new_<LexicalScopeNode>(blockBox, pos());
     }
     void setLexicalScopeBody(ParseNode *block, ParseNode *body) {
         block->pn_expr = body;
     }
 
-    bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) {
-        return pn->isKind(kind) && !pn->isInParens();
+    ParseNode *newAssignment(ParseNodeKind kind, ParseNode *lhs, ParseNode *rhs,
+                             ParseContext<FullParseHandler> *pc, JSOp op)
+    {
+        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    }
+
+    bool isUnparenthesizedYieldExpression(ParseNode *node) {
+        return node->isKind(PNK_YIELD) && !node->isInParens();
+    }
+
+    bool isUnparenthesizedCommaExpression(ParseNode *node) {
+        return node->isKind(PNK_COMMA) && !node->isInParens();
+    }
+
+    bool isUnparenthesizedAssignment(Node node) {
+        if (node->isKind(PNK_ASSIGN) && !node->isInParens()) {
+            // PNK_ASSIGN is also (mis)used for things like |var name = expr;|.
+            // But this method is only called on actual expressions, so we can
+            // just assert the node's op is the one used for plain assignment.
+            MOZ_ASSERT(node->isOp(JSOP_NOP));
+            return true;
+        }
+
+        return false;
     }
 
     inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op);
 
     void setBeginPosition(ParseNode *pn, ParseNode *oth) {
         setBeginPosition(pn, oth->pn_pos.begin);
     }
     void setBeginPosition(ParseNode *pn, uint32_t begin) {
@@ -575,43 +598,44 @@ class FullParseHandler
         return new_<ListNode>(kind, op, pos());
     }
 
     /* New list with one initial child node. kid must be non-null. */
     ParseNode *newList(ParseNodeKind kind, ParseNode *kid, JSOp op = JSOP_NOP) {
         return new_<ListNode>(kind, op, kid);
     }
 
-    void addList(ParseNode *pn, ParseNode *kid) {
-        pn->append(kid);
+
+    ParseNode *newCommaExpressionList(ParseNode *kid) {
+        return newList(PNK_COMMA, kid, JSOP_NOP);
     }
 
-    bool isUnparenthesizedYield(ParseNode *pn) {
-        return pn->isKind(PNK_YIELD) && !pn->isInParens();
+    void addList(ParseNode *list, ParseNode *kid) {
+        list->append(kid);
     }
 
     void setOp(ParseNode *pn, JSOp op) {
         pn->setOp(op);
     }
     void setBlockId(ParseNode *pn, unsigned blockid) {
         pn->pn_blockid = blockid;
     }
     void setFlag(ParseNode *pn, unsigned flag) {
         pn->pn_dflags |= flag;
     }
     void setListFlag(ParseNode *pn, unsigned flag) {
         MOZ_ASSERT(pn->isArity(PN_LIST));
         pn->pn_xflags |= flag;
     }
-    ParseNode *setInParens(ParseNode *pn) {
+    MOZ_WARN_UNUSED_RESULT ParseNode *parenthesize(ParseNode *pn) {
         pn->setInParens(true);
         return pn;
     }
-    ParseNode *setLikelyIIFE(ParseNode *pn) {
-        return setInParens(pn);
+    MOZ_WARN_UNUSED_RESULT ParseNode *setLikelyIIFE(ParseNode *pn) {
+        return parenthesize(pn);
     }
     void setPrologue(ParseNode *pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode *pn) {
         return pn->isConstant();
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2879,20 +2879,19 @@ Parser<ParseHandler>::condition()
 {
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node pn = exprInParens();
     if (!pn)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
-    if (handler.isOperationWithoutParens(pn, PNK_ASSIGN) &&
-        !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
-    {
-        return null();
+    if (handler.isUnparenthesizedAssignment(pn)) {
+        if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+            return null();
     }
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
 {
@@ -5912,21 +5911,21 @@ Parser<ParseHandler>::expr(InvokedPredic
     Node pn = assignExpr(invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (matched) {
-        Node seq = handler.newList(PNK_COMMA, pn);
+        Node seq = handler.newCommaExpressionList(pn);
         if (!seq)
             return null();
         while (true) {
-            if (handler.isUnparenthesizedYield(pn)) {
+            if (handler.isUnparenthesizedYieldExpression(pn)) {
                 report(ParseError, false, pn, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
                 return null();
             }
 
             pn = assignExpr();
             if (!pn)
                 return null();
             handler.addList(seq, pn);
@@ -6291,17 +6290,17 @@ Parser<ParseHandler>::assignExpr(Invoked
     AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
     if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
         return null();
 
     Node rhs = assignExpr();
     if (!rhs)
         return null();
 
-    return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    return handler.newAssignment(kind, lhs, rhs, pc, op);
 }
 
 static const char incop_name_str[][10] = {"increment", "decrement"};
 
 template <>
 bool
 Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
 {
@@ -7275,20 +7274,19 @@ Parser<ParseHandler>::comprehensionIf(Ge
 
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
     Node cond = assignExpr();
     if (!cond)
         return null();
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
 
     /* Check for (a = b) and warn about possible (a == b) mistype. */
-    if (handler.isOperationWithoutParens(cond, PNK_ASSIGN) &&
-        !report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
-    {
-        return null();
+    if (handler.isUnparenthesizedAssignment(cond)) {
+        if (!report(ParseExtraWarning, false, null(), JSMSG_EQUAL_AS_ASSIGN))
+            return null();
     }
 
     Node then = comprehensionTail(comprehensionKind);
     if (!then)
         return null();
 
     return handler.newIfStatement(begin, cond, then, null());
 }
@@ -7318,17 +7316,17 @@ Parser<ParseHandler>::comprehensionTail(
 
     if (comprehensionKind == NotGenerator)
         return handler.newUnary(PNK_ARRAYPUSH, JSOP_ARRAYPUSH, begin, bodyExpr);
 
     MOZ_ASSERT(comprehensionKind == StarGenerator);
     Node yieldExpr = newYieldExpression(begin, bodyExpr);
     if (!yieldExpr)
         return null();
-    handler.setInParens(yieldExpr);
+    yieldExpr = handler.parenthesize(yieldExpr);
 
     return handler.newExprStatement(yieldExpr, pos().end);
 }
 
 // Parse an ES6 generator or array comprehension, starting at the first 'for'.
 // The caller is responsible for matching the ending TOK_RP or TOK_RB.
 template <typename ParseHandler>
 typename ParseHandler::Node
@@ -7441,17 +7439,17 @@ Parser<ParseHandler>::argumentList(Node 
         if (!argNode)
             return false;
         if (spread) {
             argNode = handler.newUnary(PNK_SPREAD, JSOP_NOP, begin, argNode);
             if (!argNode)
                 return false;
         }
 
-        if (handler.isOperationWithoutParens(argNode, PNK_YIELD)) {
+        if (handler.isUnparenthesizedYieldExpression(argNode)) {
             TokenKind tt;
             if (!tokenStream.peekToken(&tt))
                 return false;
             if (tt == TOK_COMMA) {
                 report(ParseError, false, argNode, JSMSG_BAD_GENERATOR_SYNTAX, js_yield_str);
                 return false;
             }
         }
@@ -8274,17 +8272,17 @@ Parser<ParseHandler>::parenExprOrGenerat
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
-        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+        if (handler.isUnparenthesizedCommaExpression(pn)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
@@ -8292,22 +8290,21 @@ Parser<ParseHandler>::parenExprOrGenerat
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt != TOK_RP) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         handler.setEndPosition(pn, pos().end);
-        handler.setInParens(pn);
-        return pn;
+        return handler.parenthesize(pn);
     }
 #endif /* JS_HAS_GENERATOR_EXPRS */
 
-    pn = handler.setInParens(pn);
+    pn = handler.parenthesize(pn);
 
     MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_IN_PAREN);
 
     return pn;
 }
 
 // Legacy generator comprehensions can sometimes appear without parentheses.
 // For example:
@@ -8353,17 +8350,17 @@ Parser<ParseHandler>::exprInParens()
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
     if (matched) {
         if (pc->lastYieldOffset != startYieldOffset) {
             reportWithOffset(ParseError, false, pc->lastYieldOffset,
                              JSMSG_BAD_GENEXP_BODY, js_yield_str);
             return null();
         }
-        if (handler.isOperationWithoutParens(pn, PNK_COMMA)) {
+        if (handler.isUnparenthesizedCommaExpression(pn)) {
             report(ParseError, false, null(),
                    JSMSG_BAD_GENERATOR_SYNTAX, js_generator_str);
             return null();
         }
         pn = legacyGeneratorExpr(pn);
         if (!pn)
             return null();
         handler.setBeginPosition(pn, begin);
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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/. */
 
 #ifndef frontend_SyntaxParseHandler_h
 #define frontend_SyntaxParseHandler_h
 
+#include "mozilla/Attributes.h"
+
 #include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 
 namespace js {
 namespace frontend {
 
 template <typename ParseHandler>
 class Parser;
@@ -34,22 +36,71 @@ class SyntaxParseHandler
     TokenStream &tokenStream;
 
   public:
     enum Node {
         NodeFailure = 0,
         NodeGeneric,
         NodeName,
         NodeGetProp,
-        NodeString,
         NodeStringExprStatement,
-        NodeLValue
+        NodeLValue,
+
+        // In rare cases a parenthesized |node| doesn't have the same semantics
+        // as |node|.  Each such node has a special Node value, and we use a
+        // different Node value to represent the parenthesized form.  See also
+        // isUnparenthesized*(Node), newExprStatement(Node, uint32_t),
+        // parenthesize(Node), and meaningMightChangeIfParenthesized(Node).
+
+        // The directive prologue at the start of a FunctionBody or ScriptBody
+        // is the longest sequence (possibly empty) of string literal
+        // expression statements at the start of a function.  Thus we need this
+        // to treat |"use strict";| as a possible Use Strict Directive and
+        // |("use strict");| as a useless statement.
+        NodeUnparenthesizedString,
+
+        // Legacy generator expressions of the form |(expr for (...))| and
+        // array comprehensions of the form |[expr for (...)]|) don't permit
+        // |expr| to be a comma expression.  Thus we need this to treat
+        // |(a(), b for (x in []))| as a syntax error and
+        // |((a(), b) for (x in []))| as a generator that calls |a| and then
+        // yields |b| each time it's resumed.
+        NodeUnparenthesizedCommaExpr,
+
+        // Yield expressions currently (but not in ES6 -- a SpiderMonkey bug to
+        // fix) must generally be parenthesized.  (See the uses of
+        // isUnparenthesizedYieldExpression in Parser.cpp for the rare
+        // exceptions.)  Thus we need this to treat |yield 1, 2;| as a syntax
+        // error and |(yield 1), 2;| as a comma expression that will yield 1,
+        // then evaluate to 2.
+        NodeUnparenthesizedYieldExpr,
+
+        // Assignment expressions in condition contexts could be typos for
+        // equality checks.  (Think |if (x = y)| versus |if (x == y)|.)  Thus
+        // we need this to treat |if (x = y)| as a possible typo and
+        // |if ((x = y))| as a deliberate assignment within a condition.
+        //
+        // (Technically this isn't needed, as these are *only* extraWarnings
+        // warnings, and parsing with that option disables syntax parsing.  But
+        // it seems best to be consistent, and perhaps the syntax parser will
+        // eventually enforce extraWarnings and will require this then.)
+        NodeUnparenthesizedAssignment
     };
     typedef Definition::Kind DefinitionNode;
 
+  private:
+    static bool meaningMightChangeIfParenthesized(Node node) {
+        return node == NodeUnparenthesizedString ||
+               node == NodeUnparenthesizedCommaExpr ||
+               node == NodeUnparenthesizedYieldExpr ||
+               node == NodeUnparenthesizedAssignment;
+    }
+
+
+  public:
     SyntaxParseHandler(ExclusiveContext *cx, LifoAlloc &alloc,
                        TokenStream &tokenStream, bool foldConstants,
                        Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
       : lastAtom(nullptr),
         tokenStream(tokenStream)
     {}
 
     static Node null() { return NodeFailure; }
@@ -64,24 +115,24 @@ class SyntaxParseHandler
     Node newComputedName(Node expr, uint32_t start, uint32_t end) {
         return NodeName;
     }
 
     DefinitionNode newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
         return Definition::PLACEHOLDER;
     }
 
-    Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeString; }
+    Node newIdentifier(JSAtom *atom, const TokenPos &pos) { return NodeName; }
     Node newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) { return NodeGeneric; }
     Node newBooleanLiteral(bool cond, const TokenPos &pos) { return NodeGeneric; }
 
     Node newStringLiteral(JSAtom *atom, const TokenPos &pos) {
         lastAtom = atom;
         lastStringPos = pos;
-        return NodeString;
+        return NodeUnparenthesizedString;
     }
 
     Node newTemplateStringLiteral(JSAtom *atom, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newCallSiteObject(uint32_t begin, unsigned blockidGen) {
         return NodeGeneric;
@@ -130,28 +181,28 @@ class SyntaxParseHandler
     bool addElision(Node literal, const TokenPos &pos) { return true; }
     bool addSpreadElement(Node literal, uint32_t begin, Node inner) { return true; }
     bool addArrayElement(Node literal, Node element) { return true; }
 
     Node newObjectLiteral(uint32_t begin) { return NodeGeneric; }
     bool addPrototypeMutation(Node literal, uint32_t begin, Node expr) { return true; }
     bool addPropertyDefinition(Node literal, Node name, Node expr, bool isShorthand = false) { return true; }
     bool addMethodDefinition(Node literal, Node name, Node fn, JSOp op) { return true; }
-    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
+    Node newYieldExpression(uint32_t begin, Node value, Node gen) { return NodeUnparenthesizedYieldExpr; }
     Node newYieldStarExpression(uint32_t begin, Node value, Node gen) { return NodeGeneric; }
 
     // Statements
 
     Node newStatementList(unsigned blockid, const TokenPos &pos) { return NodeGeneric; }
     void addStatementToList(Node list, Node stmt, ParseContext<SyntaxParseHandler> *pc) {}
     bool prependInitialYield(Node stmtList, Node gen) { return true; }
     Node newEmptyStatement(const TokenPos &pos) { return NodeGeneric; }
 
     Node newExprStatement(Node expr, uint32_t end) {
-        return expr == NodeString ? NodeStringExprStatement : NodeGeneric;
+        return expr == NodeUnparenthesizedString ? NodeStringExprStatement : NodeGeneric;
     }
 
     Node newIfStatement(uint32_t begin, Node cond, Node then, Node else_) { return NodeGeneric; }
     Node newDoWhileStatement(Node body, Node cond, const TokenPos &pos) { return NodeGeneric; }
     Node newWhileStatement(uint32_t begin, Node cond, Node body) { return NodeGeneric; }
     Node newSwitchStatement(uint32_t begin, Node discriminant, Node caseList) { return NodeGeneric; }
     Node newCaseOrDefault(uint32_t begin, Node expr, Node body) { return NodeGeneric; }
     Node newContinueStatement(PropertyName *label, const TokenPos &pos) { return NodeGeneric; }
@@ -190,23 +241,16 @@ class SyntaxParseHandler
 
     Node newForHead(ParseNodeKind kind, Node decls, Node lhs, Node rhs, const TokenPos &pos) {
         return NodeGeneric;
     }
 
     Node newLexicalScope(ObjectBox *blockbox) { return NodeGeneric; }
     void setLexicalScopeBody(Node block, Node body) {}
 
-    bool isOperationWithoutParens(Node pn, ParseNodeKind kind) {
-        // It is OK to return false here, callers should only use this method
-        // for reporting strict option warnings and parsing code which the
-        // syntax parser does not handle.
-        return false;
-    }
-
     bool finishInitializerAssignment(Node pn, Node init, JSOp op) { return true; }
 
     void setBeginPosition(Node pn, Node oth) {}
     void setBeginPosition(Node pn, uint32_t begin) {}
 
     void setEndPosition(Node pn, Node oth) {}
     void setEndPosition(Node pn, uint32_t end) {}
 
@@ -217,29 +261,58 @@ class SyntaxParseHandler
     }
 
     Node newList(ParseNodeKind kind, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
     Node newList(ParseNodeKind kind, Node kid, JSOp op = JSOP_NOP) {
         return NodeGeneric;
     }
-    void addList(Node pn, Node kid) {}
-    bool isUnparenthesizedYield(Node pn) { return false; }
+
+    Node newCommaExpressionList(Node kid) {
+        return NodeUnparenthesizedCommaExpr;
+    }
+
+    void addList(Node list, Node kid) {
+        MOZ_ASSERT(list == NodeGeneric || list == NodeUnparenthesizedCommaExpr);
+    }
+
+    Node newAssignment(ParseNodeKind kind, Node lhs, Node rhs,
+                       ParseContext<SyntaxParseHandler> *pc, JSOp op)
+    {
+        if (kind == PNK_ASSIGN)
+            return NodeUnparenthesizedAssignment;
+        return newBinaryOrAppend(kind, lhs, rhs, pc, op);
+    }
+
+    bool isUnparenthesizedYieldExpression(Node node) {
+        return node == NodeUnparenthesizedYieldExpr;
+    }
+
+    bool isUnparenthesizedCommaExpression(Node node) {
+        return node == NodeUnparenthesizedCommaExpr;
+    }
+
+    bool isUnparenthesizedAssignment(Node node) {
+        return node == NodeUnparenthesizedAssignment;
+    }
 
     void setOp(Node pn, JSOp op) {}
     void setBlockId(Node pn, unsigned blockid) {}
     void setFlag(Node pn, unsigned flag) {}
     void setListFlag(Node pn, unsigned flag) {}
-    Node setInParens(Node pn) {
-        // String literals enclosed by parentheses are ignored during
-        // strict mode parsing.
-        return (pn == NodeString) ? NodeGeneric : pn;
+    MOZ_WARN_UNUSED_RESULT Node parenthesize(Node node) {
+        if (meaningMightChangeIfParenthesized(node))
+            return NodeGeneric;
+
+        // In all other cases, the parenthesized form of |node| is equivalent
+        // to the unparenthesized form: return |node| unchanged.
+        return node;
     }
-    Node setLikelyIIFE(Node pn) {
+    MOZ_WARN_UNUSED_RESULT Node setLikelyIIFE(Node pn) {
         return pn; // Remain in syntax-parse mode.
     }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
     PropertyName *isName(Node pn) {
         return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
     }
--- a/js/src/gc/Verifier.cpp
+++ b/js/src/gc/Verifier.cpp
@@ -214,17 +214,16 @@ gc::GCRuntime::startVerifyPreBarriers()
     trc->term = trc->edgeptr + size;
 
     if (!trc->nodemap.init())
         goto oom;
 
     /* Create the root node. */
     trc->curnode = MakeNode(trc, nullptr, JSGCTraceKind(0));
 
-    /* We want MarkRuntime to save the roots to gcSavedRoots. */
     incrementalState = MARK_ROOTS;
 
     /* Make all the roots be edges emanating from the root node. */
     markRuntime(trc);
 
     VerifyNode *node;
     node = trc->curnode;
     if (trc->edgeptr == trc->term)
--- a/js/src/jit-test/tests/basic/hypot-approx.js
+++ b/js/src/jit-test/tests/basic/hypot-approx.js
@@ -16,10 +16,19 @@ assertNear(Math.hypot(1e-3, 1e-3, 1e-3),
 
 assertNear(Math.hypot(1e300, 1e300), 1.4142135623730952e+300);
 assertNear(Math.hypot(1e100, 1e200, 1e300), 1e300);
 
 assertNear(Math.hypot(1e3, 1e-3), 1000.0000000005);
 assertNear(Math.hypot(1e-300, 1e300), 1e300);
 assertNear(Math.hypot(1e3, 1e-3, 1e3, 1e-3), 1414.2135623738021555);
 
-for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05)
+assertNear(Math.hypot(1e1, 1e2, 1e3), Math.sqrt(1e2 + 1e4 + 1e6));
+assertNear(Math.hypot(1e1, 1e2, 1e3, 1e4), Math.sqrt(1e2 + 1e4 + 1e6 + 1e8));
+
+for (var i = 1, j = 2; i < 2; i += 0.05, j += 0.05)
     assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j));
+
+for (var i = 1, j = 2, k = 3; i < 2; i += 0.05, j += 0.05, k += 0.05)
+    assertNear(Math.hypot(i, j, k), Math.sqrt(i * i + j * j + k * k));
+
+for (var i = 1, j = 2, k = 3, l = 4; i < 2; i += 0.05, j += 0.05, k += 0.05, l += 0.5)
+    assertNear(Math.hypot(i, j, k, l), Math.sqrt(i * i + j * j + k * k + l * l));
--- a/js/src/jit-test/tests/basic/hypot-exact.js
+++ b/js/src/jit-test/tests/basic/hypot-exact.js
@@ -23,35 +23,52 @@ for (var inf of [Infinity, -Infinity]) {
     assertEq(Math.hypot(0, 0, inf), Infinity);
 
     assertEq(Math.hypot(inf, NaN), Infinity);
     assertEq(Math.hypot(NaN, inf), Infinity);
 
     assertEq(Math.hypot(inf, NaN, NaN), Infinity);
     assertEq(Math.hypot(NaN, inf, NaN), Infinity);
     assertEq(Math.hypot(NaN, NaN, inf), Infinity);
+
+    assertEq(Math.hypot(inf, NaN, NaN, NaN), Infinity);
+    assertEq(Math.hypot(NaN, inf, NaN, NaN), Infinity);
+    assertEq(Math.hypot(NaN, NaN, inf, NaN), Infinity);
+    assertEq(Math.hypot(NaN, NaN, NaN, inf), Infinity);
 }
 
 // If no argument is +∞ or −∞, and any argument is NaN, the result is NaN.
 assertEq(Math.hypot(NaN), NaN);
 
 assertEq(Math.hypot(NaN, 0), NaN);
 assertEq(Math.hypot(0, NaN), NaN);
 
 assertEq(Math.hypot(NaN, NaN), NaN);
 
 assertEq(Math.hypot(NaN, 0, 0), NaN);
 assertEq(Math.hypot(0, NaN, 0), NaN);
 assertEq(Math.hypot(0, 0, NaN), NaN);
 
+assertEq(Math.hypot(NaN, 0, 0, 0), NaN);
+assertEq(Math.hypot(0, NaN, 0, 0), NaN);
+assertEq(Math.hypot(0, 0, NaN, 0), NaN);
+assertEq(Math.hypot(0, 0, 0, NaN), NaN);
+
 assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, NaN), NaN);
+assertEq(Math.hypot(Number.MAX_VALUE, Number.MIN_VALUE, Number.MIN_VALUE, NaN), NaN);
 
 // If all arguments are either +0 or -0, the result is +0.
 assertEq(Math.hypot(-0, -0), +0);
 assertEq(Math.hypot(+0, -0), +0);
 
 assertEq(Math.hypot(-0, -0, -0), +0);
 assertEq(Math.hypot(+0, -0, -0), +0);
 assertEq(Math.hypot(-0, +0, -0), +0);
 assertEq(Math.hypot(+0, +0, -0), +0);
 
+assertEq(Math.hypot(-0, -0, -0, -0), +0);
+assertEq(Math.hypot(+0, -0, -0, -0), +0);
+assertEq(Math.hypot(-0, -0, +0, -0), +0);
+assertEq(Math.hypot(+0, +0, +0, -0), +0);
+assertEq(Math.hypot(-0, -0, -0, +0), +0);
+
 // The length property of the hypot function is 2.
 assertEq(Math.hypot.length, 2);
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1057,38 +1057,91 @@ function rtrunc_to_int32_object(i) {
 var uceFault_trunc_to_int32_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_trunc_to_int32_string'));
 function rtrunc_to_int32_string(i) {
     var x = (i + "0") | 0;
     if (uceFault_trunc_to_int32_string(i) || uceFault_trunc_to_int32_string(i))
         assertEq(x, (i + "0") | 0);
     return i;
 }
 
-var uceFault_hypot_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number'));
-function rhypot_number(i) {
+var uceFault_hypot_number_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_2args'));
+function rhypot_number_2args(i) {
     var x = Math.hypot(i, i + 1);
-    if (uceFault_hypot_number(i) || uceFault_hypot_number(i))
+    if (uceFault_hypot_number_2args(i) || uceFault_hypot_number_2args(i))
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
     return i;
 }
 
-var uceFault_hypot_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object'));
-function rhypot_object(i) {
+var uceFault_hypot_number_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_3args'));
+function rhypot_number_3args(i) {
+    var x = Math.hypot(i, i + 1, i + 2);
+    if (uceFault_hypot_number_3args(i) || uceFault_hypot_number_3args(i))
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    return i;
+}
+
+var uceFault_hypot_number_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_number_4args'));
+function rhypot_number_4args(i) {
+    var x = Math.hypot(i, i + 1, i + 2, i + 3);
+    if (uceFault_hypot_number_4args(i) || uceFault_hypot_number_4args(i))
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    return i;
+}
+
+var uceFault_hypot_object_2args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_2args'));
+function rhypot_object_2args(i) {
     var t0 = i;
     var t1 = i + 1;
     var o0 = { valueOf: function () { return t0; } };
     var o1 = { valueOf: function () { return t1; } };
     var x = Math.hypot(o0, o1);
     t0 = 1000;
     t1 = 2000;
-    if (uceFault_hypot_object(i) || uceFault_hypot_object(i) )
+    if (uceFault_hypot_object_2args(i) || uceFault_hypot_object_2args(i) )
         assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1)));
     return i;
 }
 
+var uceFault_hypot_object_3args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_3args'));
+function rhypot_object_3args(i) {
+    var t0 = i;
+    var t1 = i + 1;
+    var t2 = i + 2;
+    var o0 = { valueOf: function () { return t0; } };
+    var o1 = { valueOf: function () { return t1; } };
+    var o2 = { valueOf: function () { return t2; } };
+    var x = Math.hypot(o0, o1, o2);
+    t0 = 1000;
+    t1 = 2000;
+    t2 = 3000;
+    if (uceFault_hypot_object_3args(i) || uceFault_hypot_object_3args(i) )
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2)));
+    return i;
+}
+
+var uceFault_hypot_object_4args = eval(uneval(uceFault).replace('uceFault', 'uceFault_hypot_object_4args'));
+function rhypot_object_4args(i) {
+    var t0 = i;
+    var t1 = i + 1;
+    var t2 = i + 2;
+    var t3 = i + 3;
+    var o0 = { valueOf: function () { return t0; } };
+    var o1 = { valueOf: function () { return t1; } };
+    var o2 = { valueOf: function () { return t2; } };
+    var o3 = { valueOf: function () { return t3; } };
+    var x = Math.hypot(o0, o1, o2, o3);
+    t0 = 1000;
+    t1 = 2000;
+    t2 = 3000;
+    t3 = 4000;
+    if (uceFault_hypot_object_4args(i) || uceFault_hypot_object_4args(i) )
+        assertEq(x, Math.sqrt(i * i + (i + 1) * (i + 1) + (i + 2) * (i + 2) + (i + 3) * (i + 3)));
+    return i;
+}
+
 var uceFault_sin_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_sin_number'));
 function rsin_number(i) {
     var x = Math.sin(i);
     if (uceFault_sin_number(i) || uceFault_sin_number(i))
         assertEq(x, Math.sin(i));
     return i;
 }
 
@@ -1219,18 +1272,22 @@ for (i = 0; i < 100; i++) {
     rtypeof(i);
     rtodouble_value(i);
     rtodouble_number(i);
     rtofloat32_number(i);
     rtofloat32_object(i);
     rtrunc_to_int32_number(i);
     rtrunc_to_int32_object(i);
     rtrunc_to_int32_string(i);
-    rhypot_number(i);
-    rhypot_object(i);
+    rhypot_number_2args(i);
+    rhypot_number_3args(i);
+    rhypot_number_4args(i);
+    rhypot_object_2args(i);
+    rhypot_object_3args(i);
+    rhypot_object_4args(i);
     rsin_number(i);
     rsin_object(i);
     rlog_number(i);
     rlog_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3903,24 +3903,35 @@ CodeGenerator::visitAtan2D(LAtan2D *lir)
 
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitHypot(LHypot *lir)
 {
     Register temp = ToRegister(lir->temp());
-    FloatRegister x = ToFloatRegister(lir->x());
-    FloatRegister y = ToFloatRegister(lir->y());
-
-    masm.setupUnalignedABICall(2, temp);
-    masm.passABIArg(x, MoveOp::DOUBLE);
-    masm.passABIArg(y, MoveOp::DOUBLE);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
-
+    uint32_t numArgs = lir->numArgs();
+    masm.setupUnalignedABICall(numArgs, temp);
+
+    for (uint32_t i = 0 ; i < numArgs; ++i)
+        masm.passABIArg(ToFloatRegister(lir->getOperand(i)), MoveOp::DOUBLE);
+
+    switch(numArgs) {
+      case 2:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, ecmaHypot), MoveOp::DOUBLE);
+        break;
+      case 3:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot3), MoveOp::DOUBLE);
+        break;
+      case 4:
+        masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, hypot4), MoveOp::DOUBLE);
+        break;
+      default:
+        MOZ_CRASH("Unexpected number of arguments to hypot function.");
+    }
     MOZ_ASSERT(ToFloatRegister(lir->output()) == ReturnDoubleReg);
 }
 
 void
 CodeGenerator::visitNewArray(LNewArray *lir)
 {
     Register objReg = ToRegister(lir->output());
     Register tempReg = ToRegister(lir->temp());
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -631,17 +631,24 @@ enum ABIFunctionType
     // double f(int, double)
     Args_Double_IntDouble = Args_Double_None |
         (ArgType_Double << (ArgType_Shift * 1)) |
         (ArgType_General << (ArgType_Shift * 2)),
 
     // int f(int, double)
     Args_Int_IntDouble = Args_General0 |
         (ArgType_Double << (ArgType_Shift * 1)) |
-        (ArgType_General << (ArgType_Shift * 2))
+        (ArgType_General << (ArgType_Shift * 2)),
+
+    // double f(double, double, double)
+    Args_Double_DoubleDoubleDouble = Args_Double_DoubleDouble | (ArgType_Double << (ArgType_Shift * 3)),
+
+    // double f(double, double, double, double)
+    Args_Double_DoubleDoubleDoubleDouble = Args_Double_DoubleDoubleDouble | (ArgType_Double << (ArgType_Shift * 4))
+
 };
 
 enum class BarrierKind : uint32_t {
     // No barrier is needed.
     NoBarrier,
 
     // The barrier only has to check the value's type tag is in the TypeSet.
     // Specific object types don't have to be checked.
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -2960,26 +2960,50 @@ class LAtan2D : public LCallInstructionH
         return getTemp(0);
     }
 
     const LDefinition *output() {
         return getDef(0);
     }
 };
 
-class LHypot : public LCallInstructionHelper<1, 2, 1>
-{
+class LHypot : public LCallInstructionHelper<1, 4, 1>
+{
+    uint32_t numOperands_;
   public:
     LIR_HEADER(Hypot)
-    LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp) {
+    LHypot(const LAllocation &x, const LAllocation &y, const LDefinition &temp)
+      : numOperands_(2)
+    {
         setOperand(0, x);
         setOperand(1, y);
         setTemp(0, temp);
     }
 
+    LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LDefinition &temp)
+      : numOperands_(3)
+    {
+        setOperand(0, x);
+        setOperand(1, y);
+        setOperand(2, z);
+        setTemp(0, temp);
+    }
+
+    LHypot(const LAllocation &x, const LAllocation &y, const LAllocation &z, const LAllocation &w, const LDefinition &temp)
+      : numOperands_(4)
+    {
+        setOperand(0, x);
+        setOperand(1, y);
+        setOperand(2, z);
+        setOperand(3, w);
+        setTemp(0, temp);
+    }
+
+    uint32_t numArgs() const { return numOperands_; }
+
     const LAllocation *x() {
         return getOperand(0);
     }
 
     const LAllocation *y() {
         return getOperand(1);
     }
 
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1301,23 +1301,44 @@ LIRGenerator::visitAtan2(MAtan2 *ins)
     LAtan2D *lir = new(alloc()) LAtan2D(useRegisterAtStart(y), useRegisterAtStart(x),
                                         tempFixed(CallTempReg0));
     defineReturn(lir, ins);
 }
 
 void
 LIRGenerator::visitHypot(MHypot *ins)
 {
-    MDefinition *x = ins->x();
-    MOZ_ASSERT(x->type() == MIRType_Double);
-
-    MDefinition *y = ins->y();
-    MOZ_ASSERT(y->type() == MIRType_Double);
-
-    LHypot *lir = new(alloc()) LHypot(useRegisterAtStart(x), useRegisterAtStart(y), tempFixed(CallTempReg0));
+    LHypot *lir = nullptr;
+    uint32_t length = ins->numOperands();
+    for (uint32_t i = 0; i < length; ++i)
+        MOZ_ASSERT(ins->getOperand(i)->type() == MIRType_Double);
+
+    switch(length) {
+      case 2:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  tempFixed(CallTempReg0));
+        break;
+      case 3:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  useRegisterAtStart(ins->getOperand(2)),
+                                  tempFixed(CallTempReg0));
+        break;
+      case 4:
+        lir = new(alloc()) LHypot(useRegisterAtStart(ins->getOperand(0)),
+                                  useRegisterAtStart(ins->getOperand(1)),
+                                  useRegisterAtStart(ins->getOperand(2)),
+                                  useRegisterAtStart(ins->getOperand(3)),
+                                  tempFixed(CallTempReg0));
+        break;
+      default:
+        MOZ_CRASH("Unexpected number of arguments to LHypot.");
+    }
+
     defineReturn(lir, ins);
 }
 
 void
 LIRGenerator::visitPow(MPow *ins)
 {
     MDefinition *input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -997,31 +997,40 @@ IonBuilder::inlineMathAtan2(CallInfo &ca
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathHypot(CallInfo &callInfo)
 {
     if (callInfo.constructing())
         return InliningStatus_NotInlined;
 
-    if (callInfo.argc() != 2)
+    uint32_t argc = callInfo.argc();
+    if (argc < 2 || argc > 4)
         return InliningStatus_NotInlined;
 
     if (getInlineReturnType() != MIRType_Double)
         return InliningStatus_NotInlined;
 
-    MIRType argType0 = callInfo.getArg(0)->type();
-    MIRType argType1 = callInfo.getArg(1)->type();
-
-    if (!IsNumberType(argType0) || !IsNumberType(argType1))
+    MDefinitionVector vector(alloc());
+    if (!vector.reserve(argc))
         return InliningStatus_NotInlined;
 
+    for (uint32_t i = 0; i < argc; ++i) {
+        MDefinition * arg = callInfo.getArg(i);
+        if (!IsNumberType(arg->type()))
+            return InliningStatus_NotInlined;
+        vector.infallibleAppend(arg);
+    }
+
     callInfo.setImplicitlyUsedUnchecked();
-
-    MHypot *hypot = MHypot::New(alloc(), callInfo.getArg(0), callInfo.getArg(1));
+    MHypot *hypot = MHypot::New(alloc(), vector);
+
+    if (!hypot)
+        return InliningStatus_NotInlined;
+
     current->add(hypot);
     current->push(hypot);
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineMathPow(CallInfo &callInfo)
 {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2226,16 +2226,28 @@ MMathFunction::trySpecializeFloat32(Temp
             ConvertDefinitionToDouble<0>(alloc, input(), this);
         return;
     }
 
     setResultType(MIRType_Float32);
     setPolicyType(MIRType_Float32);
 }
 
+MHypot *MHypot::New(TempAllocator &alloc, const MDefinitionVector & vector)
+{
+    uint32_t length = vector.length();
+    MHypot * hypot = new(alloc) MHypot;
+    if (!hypot->init(alloc, length))
+        return nullptr;
+
+    for (uint32_t i = 0; i < length; ++i)
+        hypot->initOperand(i, vector[i]);
+    return hypot;
+}
+
 bool
 MAdd::fallible() const
 {
     // the add is fallible if range analysis does not say that it is finite, AND
     // either the truncation analysis shows that there are non-truncated uses.
     if (truncateKind() >= IndirectTruncate)
         return false;
     if (range() && range()->hasInt32Bounds())
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4789,18 +4789,18 @@ class MTruncateToInt32
     void computeRange(TempAllocator &alloc) MOZ_OVERRIDE;
     TruncateKind operandTruncateKind(size_t index) const MOZ_OVERRIDE;
 # ifdef DEBUG
     bool isConsistentFloat32Use(MUse *use) const MOZ_OVERRIDE {
         return true;
     }
 #endif
 
-    bool writeRecoverData(CompactBufferWriter &writer) const;
-    bool canRecoverOnBailout() const {
+    bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
+    bool canRecoverOnBailout() const MOZ_OVERRIDE {
         return input()->type() < MIRType_Symbol;
     }
 
     ALLOW_CLONE(MTruncateToInt32)
 };
 
 // Converts any type to a string
 class MToString :
@@ -5502,39 +5502,28 @@ class MAtan2
         return true;
     }
 
     ALLOW_CLONE(MAtan2)
 };
 
 // Inline implementation of Math.hypot().
 class MHypot
-  : public MBinaryInstruction,
-    public MixPolicy<DoublePolicy<0>, DoublePolicy<1> >::Data
-{
-    MHypot(MDefinition *y, MDefinition *x)
-      : MBinaryInstruction(x, y)
+  : public MVariadicInstruction,
+    public AllDoublePolicy::Data
+{
+    MHypot()
     {
         setResultType(MIRType_Double);
         setMovable();
     }
 
   public:
     INSTRUCTION_HEADER(Hypot)
-    static MHypot *New(TempAllocator &alloc, MDefinition *x, MDefinition *y) {
-        return new(alloc) MHypot(y, x);
-    }
-
-    MDefinition *x() const {
-        return getOperand(0);
-    }
-
-    MDefinition *y() const {
-        return getOperand(1);
-    }
+    static MHypot *New(TempAllocator &alloc, const MDefinitionVector &vector);
 
     bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
         return congruentIfOperandsEqual(ins);
     }
 
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::None();
     }
@@ -5543,17 +5532,23 @@ class MHypot
         return true;
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
     bool canRecoverOnBailout() const MOZ_OVERRIDE {
         return true;
     }
 
-    ALLOW_CLONE(MHypot)
+    bool canClone() const {
+        return true;
+    }
+
+    MInstruction *clone(TempAllocator &alloc, const MDefinitionVector &inputs) const {
+       return MHypot::New(alloc, inputs);
+    }
 };
 
 // Inline implementation of Math.pow().
 class MPow
   : public MBinaryInstruction,
     public PowPolicy::Data
 {
     MPow(MDefinition *input, MDefinition *power, MIRType powerType)
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -869,33 +869,34 @@ RAtan2::recover(JSContext *cx, SnapshotI
     return true;
 }
 
 bool
 MHypot::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Hypot));
+    writer.writeUnsigned(uint32_t(numOperands()));
     return true;
 }
 
 RHypot::RHypot(CompactBufferReader &reader)
+    : numOperands_(reader.readUnsigned())
 { }
 
 bool
 RHypot::recover(JSContext *cx, SnapshotIterator &iter) const
 {
     JS::AutoValueVector vec(cx);
 
-    // currently, only 2 args can be saved in MIR
-    if (!vec.reserve(2))
+    if (!vec.reserve(numOperands_))
         return false;
 
-    vec.infallibleAppend(iter.read());
-    vec.infallibleAppend(iter.read());
+    for (uint32_t i = 0 ; i < numOperands_ ; ++i)
+       vec.infallibleAppend(iter.read());
 
     RootedValue result(cx);
 
     if(!js::math_hypot_handle(cx, vec, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -472,21 +472,24 @@ class RAtan2 MOZ_FINAL : public RInstruc
         return 2;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
 class RHypot MOZ_FINAL : public RInstruction
 {
+   private:
+     uint32_t numOperands_;
+
    public:
      RINSTRUCTION_HEADER_(Hypot)
 
      virtual uint32_t numOperands() const {
-         return 2;
+         return numOperands_;
      }
 
      bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
 class RMathFunction MOZ_FINAL : public RInstruction
 {
   private:
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -90,16 +90,36 @@ ArithPolicy::adjustInputs(TempAllocator 
         if (!replace->typePolicy()->adjustInputs(alloc, replace))
             return false;
     }
 
     return true;
 }
 
 bool
+AllDoublePolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
+{
+    for (size_t i = 0, e = ins->numOperands(); i < e; i++) {
+        MDefinition *in = ins->getOperand(i);
+        if (in->type() == MIRType_Double)
+            continue;
+
+        MInstruction *replace = MToDouble::New(alloc, in);
+
+        ins->block()->insertBefore(ins, replace);
+        ins->replaceOperand(i, replace);
+
+        if (!replace->typePolicy()->adjustInputs(alloc, replace))
+            return false;
+    }
+
+    return true;
+}
+
+bool
 ComparePolicy::adjustInputs(TempAllocator &alloc, MInstruction *def)
 {
     MOZ_ASSERT(def->isCompare());
     MCompare *compare = def->toCompare();
 
     // Convert Float32 operands to doubles
     for (size_t i = 0; i < 2; i++) {
         MDefinition *in = def->getOperand(i);
@@ -1020,16 +1040,17 @@ FilterTypeSetPolicy::adjustInputs(TempAl
     _(FilterTypeSetPolicy)                      \
     _(InstanceOfPolicy)                         \
     _(PowPolicy)                                \
     _(StoreTypedArrayElementStaticPolicy)       \
     _(StoreTypedArrayHolePolicy)                \
     _(StoreTypedArrayPolicy)                    \
     _(StoreUnboxedObjectOrNullPolicy)           \
     _(TestPolicy)                               \
+    _(AllDoublePolicy)                          \
     _(ToDoublePolicy)                           \
     _(ToInt32Policy)                            \
     _(ToStringPolicy)                           \
     _(TypeBarrierPolicy)
 
 #define TEMPLATE_TYPE_POLICY_LIST(_)                                    \
     _(BoxExceptPolicy<0, MIRType_String>)                               \
     _(BoxPolicy<0>)                                                     \
--- a/js/src/jit/TypePolicy.h
+++ b/js/src/jit/TypePolicy.h
@@ -88,16 +88,23 @@ class BoxInputsPolicy MOZ_FINAL : public
 
 class ArithPolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
+class AllDoublePolicy MOZ_FINAL : public TypePolicy
+{
+  public:
+    EMPTY_DATA_;
+    bool adjustInputs(TempAllocator &alloc, MInstruction *def);
+};
+
 class BitwisePolicy MOZ_FINAL : public TypePolicy
 {
   public:
     SPECIALIZATION_DATA_;
     virtual bool adjustInputs(TempAllocator &alloc, MInstruction *def) MOZ_OVERRIDE;
 };
 
 class ComparePolicy MOZ_FINAL : public TypePolicy
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4155,16 +4155,18 @@ AssertValidABIFunctionType(uint32_t pass
       case Args_Int_Double:
       case Args_Float32_Float32:
       case Args_Double_Double:
       case Args_Double_Int:
       case Args_Double_DoubleInt:
       case Args_Double_DoubleDouble:
       case Args_Double_IntDouble:
       case Args_Int_IntDouble:
+      case Args_Double_DoubleDoubleDouble:
+      case Args_Double_DoubleDoubleDoubleDouble:
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 }
 #endif
 
 void
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -1445,16 +1445,25 @@ Simulator::getFpArgs(double *x, double *
     } else {
         *x = get_double_from_register_pair(0);
         *y = get_double_from_register_pair(2);
         *z = get_register(2);
     }
 }
 
 void
+Simulator::getFpFromStack(int32_t *stack, double *x)
+{
+    MOZ_ASSERT(stack && x);
+    char buffer[2 * sizeof(stack[0])];
+    memcpy(buffer, stack, 2 * sizeof(stack[0]));
+    memcpy(x, buffer, 2 * sizeof(stack[0]));
+}
+
+void
 Simulator::setCallResultDouble(double result)
 {
     // The return value is either in r0/r1 or d0.
     if (UseHardFpABI()) {
         char buffer[2 * sizeof(vfp_registers_[0])];
         memcpy(buffer, &result, sizeof(buffer));
         // Copy result to d0.
         memcpy(vfp_registers_, buffer, sizeof(buffer));
@@ -2073,16 +2082,19 @@ typedef double (*Prototype_Double_Int)(i
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef float (*Prototype_Float32_Float32)(float arg0);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 
+typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
+typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1,
+                                                            double arg2, double arg3);
 
 // Fill the volatile registers with scratch values.
 //
 // Some of the ABI calls assume that the float registers are not scratched, even
 // though the ABI defines them as volatile - a performance optimization. These
 // are all calls passing operands in integer registers, so for now the simulator
 // does not scratch any float registers for these calls. Should try to narrow it
 // further in future.
@@ -2294,16 +2306,41 @@ Simulator::softwareInterrupt(SimInstruct
             else
                 dval0 = get_double_from_register_pair(2);
             Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external);
             int32_t result = target(ival, dval0);
             scratchVolatileRegisters(/* scratchFloat = true */);
             set_register(r0, result);
             break;
           }
+          case Args_Double_DoubleDoubleDouble: {
+            double dval0, dval1, dval2;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the last argument is on stack
+            getFpFromStack(stack_pointer, &dval2);
+            Prototype_Double_DoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultDouble(dresult);
+            break;
+         }
+         case Args_Double_DoubleDoubleDoubleDouble: {
+            double dval0, dval1, dval2, dval3;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the two last arguments are on stack
+            getFpFromStack(stack_pointer, &dval2);
+            getFpFromStack(stack_pointer + 2, &dval3);
+            Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2, dval3);
+            scratchVolatileRegisters(/* scratchFloat = true */);
+            setCallResultDouble(dresult);
+            break;
+          }
           default:
             MOZ_CRASH("call");
         }
 
         if (single_stepping_)
             single_step_callback_(single_step_callback_arg_, this, nullptr);
 
         set_register(lr, saved_lr);
--- a/js/src/jit/arm/Simulator-arm.h
+++ b/js/src/jit/arm/Simulator-arm.h
@@ -284,16 +284,17 @@ class Simulator
     bool skipCalleeSavedRegsCheck;
 
     // Runtime call support.
     static void *RedirectNativeFunction(void *nativeFunction, ABIFunctionType type);
 
   private:
     // Handle arguments and return value for runtime FP functions.
     void getFpArgs(double *x, double *y, int32_t *z);
+    void getFpFromStack(int32_t *stack, double *x1);
     void setCallResultDouble(double result);
     void setCallResultFloat(float result);
     void setCallResult(int64_t res);
     void scratchVolatileRegisters(bool scratchFloat = true);
 
     template<class ReturnType, int register_size>
     ReturnType getFromVFPRegister(int reg_index);
 
--- a/js/src/jit/mips/Assembler-mips.cpp
+++ b/js/src/jit/mips/Assembler-mips.cpp
@@ -964,26 +964,26 @@ BufferOffset
 Assembler::as_clz(Register rd, Register rs)
 {
     return writeInst(InstReg(op_special2, rs, rd, rd, ff_clz).encode());
 }
 
 BufferOffset
 Assembler::as_ins(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
-    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32);
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(pos + size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ins).encode());
 }
 
 BufferOffset
 Assembler::as_ext(Register rt, Register rs, uint16_t pos, uint16_t size)
 {
-    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size >= 32);
+    MOZ_ASSERT(pos < 32 && size != 0 && size <= 32 && pos + size != 0 && pos + size <= 32);
     Register rd;
     rd = Register::FromCode(size - 1);
     return writeInst(InstReg(op_special3, rs, rt, rd, pos, ff_ext).encode());
 }
 
 // FP instructions
 BufferOffset
 Assembler::as_ld(FloatRegister fd, Register base, int32_t off)
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -3467,16 +3467,18 @@ AssertValidABIFunctionType(uint32_t pass
       case Args_Int_Double:
       case Args_Float32_Float32:
       case Args_Double_Double:
       case Args_Double_Int:
       case Args_Double_DoubleInt:
       case Args_Double_DoubleDouble:
       case Args_Double_IntDouble:
       case Args_Int_IntDouble:
+      case Args_Double_DoubleDoubleDouble:
+      case Args_Double_DoubleDoubleDoubleDouble:
         break;
       default:
         MOZ_CRASH("Unexpected type");
     }
 }
 #endif
 
 void
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -227,17 +227,17 @@ class MacroAssemblerMIPS : public Assemb
     void ma_b(Register lhs, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Register lhs, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Register lhs, ImmPtr imm, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind);
     }
     void ma_b(Register lhs, ImmGCPtr imm, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         MOZ_ASSERT(lhs != ScratchRegister);
         ma_li(ScratchRegister, imm);
-        ma_b(lhs, Imm32(uint32_t(imm.value)), l, c, jumpKind);
+        ma_b(lhs, ScratchRegister, l, c, jumpKind);
     }
     void ma_b(Register lhs, Address addr, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Address addr, Imm32 imm, Label *l, Condition c, JumpKind jumpKind = LongJump);
     void ma_b(Address addr, Register rhs, Label *l, Condition c, JumpKind jumpKind = LongJump) {
         MOZ_ASSERT(rhs != ScratchRegister);
         ma_lw(ScratchRegister, addr);
         ma_b(ScratchRegister, rhs, l, c, jumpKind);
     }
--- a/js/src/jit/mips/Simulator-mips.cpp
+++ b/js/src/jit/mips/Simulator-mips.cpp
@@ -1546,16 +1546,24 @@ void
 Simulator::getFpArgs(double *x, double *y, int32_t *z)
 {
     *x = getFpuRegisterDouble(12);
     *y = getFpuRegisterDouble(14);
     *z = getRegister(a2);
 }
 
 void
+Simulator::getFpFromStack(int32_t *stack, double *x)
+{
+    MOZ_ASSERT(stack);
+    MOZ_ASSERT(x);
+    memcpy(x, stack, sizeof(double));
+}
+
+void
 Simulator::setCallResultDouble(double result)
 {
     setFpuRegisterDouble(f0, result);
 }
 
 void
 Simulator::setCallResultFloat(float result)
 {
@@ -1852,16 +1860,20 @@ typedef double (*Prototype_Double_Int)(i
 typedef int32_t (*Prototype_Int_Double)(double arg0);
 typedef float (*Prototype_Float32_Float32)(float arg0);
 
 typedef double (*Prototype_DoubleInt)(double arg0, int32_t arg1);
 typedef double (*Prototype_Double_IntDouble)(int32_t arg0, double arg1);
 typedef double (*Prototype_Double_DoubleDouble)(double arg0, double arg1);
 typedef int32_t (*Prototype_Int_IntDouble)(int32_t arg0, double arg1);
 
+typedef double (*Prototype_Double_DoubleDoubleDouble)(double arg0, double arg1, double arg2);
+typedef double (*Prototype_Double_DoubleDoubleDoubleDouble)(double arg0, double arg1,
+                                                            double arg2, double arg3);
+
 // Software interrupt instructions are used by the simulator to call into C++.
 void
 Simulator::softwareInterrupt(SimInstruction *instr)
 {
     int32_t func = instr->functionFieldRaw();
     uint32_t code = (func == ff_break) ? instr->bits(25, 6) : -1;
 
     // We first check if we met a call_rt_redirected.
@@ -2017,16 +2029,39 @@ Simulator::softwareInterrupt(SimInstruct
           case Args_Int_IntDouble: {
             int32_t ival = getRegister(a0);
             double dval0 = getDoubleFromRegisterPair(a2);
             Prototype_Int_IntDouble target = reinterpret_cast<Prototype_Int_IntDouble>(external);
             int32_t result = target(ival, dval0);
             setRegister(v0, result);
             break;
           }
+          case Args_Double_DoubleDoubleDouble: {
+            double dval0, dval1, dval2;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the last argument is on stack
+            getFpFromStack(stack_pointer + 4, &dval2);
+            Prototype_Double_DoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2);
+            setCallResultDouble(dresult);
+            break;
+         }
+         case Args_Double_DoubleDoubleDoubleDouble: {
+            double dval0, dval1, dval2, dval3;
+            int32_t ival;
+            getFpArgs(&dval0, &dval1, &ival);
+            // the two last arguments are on stack
+            getFpFromStack(stack_pointer + 4, &dval2);
+            getFpFromStack(stack_pointer + 6, &dval3);
+            Prototype_Double_DoubleDoubleDoubleDouble target = reinterpret_cast<Prototype_Double_DoubleDoubleDoubleDouble>(external);
+            double dresult = target(dval0, dval1, dval2, dval3);
+            setCallResultDouble(dresult);
+            break;
+          }
           default:
             MOZ_CRASH("call");
         }
 
         setRegister(ra, saved_ra);
         set_pc(getRegister(ra));
 #endif
     } else if (func == ff_break && code <= kMaxStopCode) {
--- a/js/src/jit/mips/Simulator-mips.h
+++ b/js/src/jit/mips/Simulator-mips.h
@@ -292,16 +292,17 @@ class Simulator {
     };
     int16_t exceptions[kNumExceptions];
 
     // Exceptions.
     void signalExceptions();
 
     // Handle arguments and return value for runtime FP functions.
     void getFpArgs(double *x, double *y, int32_t *z);
+    void getFpFromStack(int32_t *stack, double *x);
 
     void setCallResultDouble(double result);
     void setCallResultFloat(float result);
     void setCallResult(int64_t res);
 
     void callInternal(uint8_t *entry);
 
     // Architecture state.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -447,28 +447,16 @@ JS_ValueToSource(JSContext *cx, HandleVa
 }
 
 JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t *ip)
 {
     return mozilla::NumberIsInt32(d, ip);
 }
 
-JS_PUBLIC_API(int32_t)
-JS_DoubleToInt32(double d)
-{
-    return ToInt32(d);
-}
-
-JS_PUBLIC_API(uint32_t)
-JS_DoubleToUint32(double d)
-{
-    return ToUint32(d);
-}
-
 JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
     return TypeOfValue(value);
 }
@@ -5937,25 +5925,25 @@ AutoGCRooter::AutoGCRooter(ContextFriend
   : down(cx->autoGCRooters),
     tag_(tag),
     stackTop(&cx->autoGCRooters)
 {
     MOZ_ASSERT(this != *stackTop);
     *stackTop = this;
 }
 
-#ifdef DEBUG
+#ifdef JS_DEBUG
 JS_PUBLIC_API(void)
-JS::AssertArgumentsAreSane(JSContext *cx, HandleValue value)
+JS::detail::AssertArgumentsAreSane(JSContext *cx, HandleValue value)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, value);
 }
-#endif /* DEBUG */
+#endif /* JS_DEBUG */
 
 JS_PUBLIC_API(void *)
 JS_EncodeScript(JSContext *cx, HandleScript scriptArg, uint32_t *lengthp)
 {
     XDREncoder encoder(cx);
     RootedScript script(cx, scriptArg);
     if (!encoder.codeScript(&script))
         return nullptr;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -56,30 +56,16 @@ class JS_PUBLIC_API(AutoCheckRequestDept
 
 #else
 
 # define CHECK_REQUEST(cx) \
     ((void) 0)
 
 #endif /* JS_DEBUG */
 
-#ifdef JS_DEBUG
-/*
- * Assert that we're not doing GC on cx, that we're in a request as
- * needed, and that the compartments for cx and v are correct.
- * Also check that GC would be safe at this point.
- */
-JS_PUBLIC_API(void)
-AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v);
-#else
-inline void AssertArgumentsAreSane(JSContext *cx, JS::HandleValue v) {
-    /* Do nothing */
-}
-#endif /* JS_DEBUG */
-
 /* AutoValueArray roots an internal fixed-size array of Values. */
 template <size_t N>
 class AutoValueArray : public AutoGCRooter
 {
     const size_t length_;
     Value elements_[N];
 
   public:
@@ -1105,207 +1091,19 @@ extern JS_PUBLIC_API(JSFunction *)
 JS_ValueToFunction(JSContext *cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_ValueToConstructor(JSContext *cx, JS::HandleValue v);
 
 extern JS_PUBLIC_API(JSString *)
 JS_ValueToSource(JSContext *cx, JS::Handle<JS::Value> v);
 
-namespace js {
-/*
- * DO NOT CALL THIS.  Use JS::ToNumber
- */
-extern JS_PUBLIC_API(bool)
-ToNumberSlow(JSContext *cx, JS::Value v, double *dp);
-
-/*
- * DO NOT CALL THIS. Use JS::ToBoolean
- */
-extern JS_PUBLIC_API(bool)
-ToBooleanSlow(JS::HandleValue v);
-
-/*
- * DO NOT CALL THIS. Use JS::ToString
- */
-extern JS_PUBLIC_API(JSString*)
-ToStringSlow(JSContext *cx, JS::HandleValue v);
-
-/*
- * DO NOT CALL THIS. Use JS::ToObject.
- */
-extern JS_PUBLIC_API(JSObject*)
-ToObjectSlow(JSContext *cx, JS::HandleValue vp, bool reportScanStack);
-
-} /* namespace js */
-
-namespace JS {
-
-/* ES5 9.3 ToNumber. */
-MOZ_ALWAYS_INLINE bool
-ToNumber(JSContext *cx, HandleValue v, double *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isNumber()) {
-        *out = v.toNumber();
-        return true;
-    }
-    return js::ToNumberSlow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToBoolean(HandleValue v)
-{
-    if (v.isBoolean())
-        return v.toBoolean();
-    if (v.isInt32())
-        return v.toInt32() != 0;
-    if (v.isNullOrUndefined())
-        return false;
-    if (v.isDouble()) {
-        double d = v.toDouble();
-        return !mozilla::IsNaN(d) && d != 0;
-    }
-    if (v.isSymbol())
-        return true;
-
-    /* The slow path handles strings and objects. */
-    return js::ToBooleanSlow(v);
-}
-
-MOZ_ALWAYS_INLINE JSString*
-ToString(JSContext *cx, HandleValue v)
-{
-    if (v.isString())
-        return v.toString();
-    return js::ToStringSlow(cx, v);
-}
-
-/* ES5 9.9 ToObject. */
-MOZ_ALWAYS_INLINE JSObject*
-ToObject(JSContext *cx, HandleValue vp)
-{
-    if (vp.isObject())
-        return &vp.toObject();
-    return js::ToObjectSlow(cx, vp, false);
-}
-
-/*
- * Implements ES6 draft rev 28 (2014 Oct 14) 7.1.1, second algorithm.
- *
- * Most users should not call this -- use JS::ToNumber, ToBoolean, or ToString
- * instead. This should only be called from custom convert hooks. It implements
- * the default conversion behavior shared by most objects in JS, so it's useful
- * as a fallback.
- */
-extern JS_PUBLIC_API(bool)
-OrdinaryToPrimitive(JSContext *cx, JS::HandleObject obj, JSType type,
-                    JS::MutableHandleValue vp);
-
-} /* namespace JS */
-
 extern JS_PUBLIC_API(bool)
 JS_DoubleIsInt32(double d, int32_t *ip);
 
-extern JS_PUBLIC_API(int32_t)
-JS_DoubleToInt32(double d);
-
-extern JS_PUBLIC_API(uint32_t)
-JS_DoubleToUint32(double d);
-
-
-namespace js {
-/* DO NOT CALL THIS. Use JS::ToUint16. */
-extern JS_PUBLIC_API(bool)
-ToUint16Slow(JSContext *cx, JS::HandleValue v, uint16_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToInt32. */
-extern JS_PUBLIC_API(bool)
-ToInt32Slow(JSContext *cx, JS::HandleValue v, int32_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToUint32. */
-extern JS_PUBLIC_API(bool)
-ToUint32Slow(JSContext *cx, JS::HandleValue v, uint32_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToInt64. */
-extern JS_PUBLIC_API(bool)
-ToInt64Slow(JSContext *cx, JS::HandleValue v, int64_t *out);
-
-/* DO NOT CALL THIS. Use JS::ToUint64. */
-extern JS_PUBLIC_API(bool)
-ToUint64Slow(JSContext *cx, JS::HandleValue v, uint64_t *out);
-} /* namespace js */
-
-namespace JS {
-
-MOZ_ALWAYS_INLINE bool
-ToUint16(JSContext *cx, JS::HandleValue v, uint16_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = uint16_t(v.toInt32());
-        return true;
-    }
-    return js::ToUint16Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToInt32(JSContext *cx, JS::HandleValue v, int32_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = v.toInt32();
-        return true;
-    }
-    return js::ToInt32Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToUint32(JSContext *cx, JS::HandleValue v, uint32_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = uint32_t(v.toInt32());
-        return true;
-    }
-    return js::ToUint32Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToInt64(JSContext *cx, JS::HandleValue v, int64_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        *out = int64_t(v.toInt32());
-        return true;
-    }
-    return js::ToInt64Slow(cx, v, out);
-}
-
-MOZ_ALWAYS_INLINE bool
-ToUint64(JSContext *cx, JS::HandleValue v, uint64_t *out)
-{
-    AssertArgumentsAreSane(cx, v);
-
-    if (v.isInt32()) {
-        /* Account for sign extension of negatives into the longer 64bit space. */
-        *out = uint64_t(int64_t(v.toInt32()));
-        return true;
-    }
-    return js::ToUint64Slow(cx, v, out);
-}
-
-
-} /* namespace JS */
-
 extern JS_PUBLIC_API(JSType)
 JS_TypeOfValue(JSContext *cx, JS::Handle<JS::Value> v);
 
 extern JS_PUBLIC_API(bool)
 JS_StrictlyEqual(JSContext *cx, jsval v1, jsval v2, bool *equal);
 
 extern JS_PUBLIC_API(bool)
 JS_LooselyEqual(JSContext *cx, JS::Handle<JS::Value> v1, JS::Handle<JS::Value> v2, bool *equal);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -1373,16 +1373,60 @@ js::ecmaHypot(double x, double y)
      */
     if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) {
         return mozilla::PositiveInfinity<double>();
     }
 #endif
     return hypot(x, y);
 }
 
+static inline
+void
+hypot_step(double &scale, double &sumsq, double x)
+{
+    double xabs = mozilla::Abs(x);
+    if (scale < xabs) {
+        sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
+        scale = xabs;
+    } else if (scale != 0) {
+        sumsq += (xabs / scale) * (xabs / scale);
+    }
+}
+
+double
+js::hypot4(double x, double y, double z, double w)
+{
+    /* Check for infinity or NaNs so that we can return immediatelly.
+     * Does not need to be WIN_XP specific as ecmaHypot
+     */
+    if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y) ||
+            mozilla::IsInfinite(z) || mozilla::IsInfinite(w))
+        return mozilla::PositiveInfinity<double>();
+
+    if (mozilla::IsNaN(x) || mozilla::IsNaN(y) || mozilla::IsNaN(z) ||
+            mozilla::IsNaN(w))
+        return GenericNaN();
+
+    double scale = 0;
+    double sumsq = 1;
+
+    hypot_step(scale, sumsq, x);
+    hypot_step(scale, sumsq, y);
+    hypot_step(scale, sumsq, z);
+    hypot_step(scale, sumsq, w);
+
+    return scale * sqrt(sumsq);
+}
+
+double
+js::hypot3(double x, double y, double z)
+{
+    return hypot4(x, y, z, 0.0);
+}
+
 bool
 js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return math_hypot_handle(cx, args, args.rval());
 }
 
 bool
@@ -1413,24 +1457,17 @@ js::math_hypot_handle(JSContext *cx, Han
         if (!ToNumber(cx, args[i], &x))
             return false;
 
         isInfinite |= mozilla::IsInfinite(x);
         isNaN |= mozilla::IsNaN(x);
         if (isInfinite || isNaN)
             continue;
 
-        double xabs = mozilla::Abs(x);
-
-        if (scale < xabs) {
-            sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
-            scale = xabs;
-        } else if (scale != 0) {
-            sumsq += (xabs / scale) * (xabs / scale);
-        }
+        hypot_step(scale, sumsq, x);
     }
 
     double result = isInfinite ? PositiveInfinity<double>() :
                     isNaN ? GenericNaN() :
                     scale * sqrt(sumsq);
     res.setNumber(result);
     return true;
 }
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -240,16 +240,22 @@ extern bool
 math_asinh(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern bool
 math_atanh(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
 ecmaHypot(double x, double y);
 
+extern double
+hypot3(double x, double y, double z);
+
+extern double
+hypot4(double x, double y, double z, double w);
+
 extern bool
 math_hypot(JSContext *cx, unsigned argc, Value *vp);
 
 extern bool
 math_hypot_handle(JSContext *cx, HandleValueArray args, MutableHandleValue res);
 
 extern bool
 math_trunc(JSContext *cx, unsigned argc, Value *vp);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3555,17 +3555,17 @@ js::PrimitiveToObject(JSContext *cx, con
 /*
  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
  * already be an object, use ToObject. reportCantConvert controls how null and
  * undefined errors are reported.
  *
  * Callers must handle the already-object case.
  */
 JSObject *
-js::ToObjectSlow(JSContext *cx, HandleValue val, bool reportScanStack)
+js::ToObjectSlow(JSContext *cx, JS::HandleValue val, bool reportScanStack)
 {
     MOZ_ASSERT(!val.isMagic());
     MOZ_ASSERT(!val.isObject());
 
     if (val.isNullOrUndefined()) {
         if (reportScanStack) {
             js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, val, NullPtr());
         } else {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -15,16 +15,17 @@
  * values, called slots.  The map/slot pointer pair is GC'ed, while the map
  * is reference counted and the slot vector is malloc'ed.
  */
 
 #include "mozilla/MemoryReporting.h"
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
+#include "js/Conversions.h"
 #include "js/GCAPI.h"
 #include "js/HeapAPI.h"
 #include "vm/Shape.h"
 #include "vm/String.h"
 #include "vm/Xdr.h"
 
 namespace JS {
 struct ClassInfo;
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -16,16 +16,18 @@
 #else
 #include <sys/wait.h>
 #include <unistd.h>
 #endif
 
 // For JSFunctionSpecWithHelp
 #include "jsfriendapi.h"
 
+#include "js/Conversions.h"
+
 using namespace JS;
 
 static bool
 os_getenv(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() < 1) {
         JS_ReportError(cx, "os.getenv requires 1 argument");
--- a/js/src/tests/ecma_6/Comprehensions/error-messages.js
+++ b/js/src/tests/ecma_6/Comprehensions/error-messages.js
@@ -26,72 +26,74 @@ const GENERIC              = error("(for
 const BAD_GENERATOR_SYNTAX = error("(for (x of []) x, 1)").message;
 const MISSING_SEMI         = error("yield 1").message;
 const MISSING_PAREN        = error("(yield 1)").message;
 const PAREN_PAREN          = error("(foo").message;
 const FOR_OF_PAREN         = error("(for (x of y, z) w)").message;
 
 const cases = [
 // Expressions involving yield without a value, not currently implemented.  Many
-// of these errors would need to be updated.
-//{ expr: "yield",        top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield" },
-//{ expr: "1, yield",     top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield at end of list" },
-//{ expr: "yield, 1",     top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN,  desc: "simple yield in list" },
-//{ expr: "(yield)",      top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield, parenthesized" },
-//{ expr: "(1, yield)",   top: TOP_YIELD, fun: null,              gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized" },
-//{ expr: "(yield, 1)",   top: TOP_YIELD, fun: YIELD_PAREN, gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized" },
-//{ expr: "((((yield))))",   top: TOP_YIELD, fun: null, gen: GENEXP_YIELD, desc: "deeply nested yield" },
-//{ expr: "(for (x of []) yield)",           top: TOP_YIELD, fun: GENERIC,      gen: GENERIC,      desc: "simple yield in genexp" },
-//{ expr: "(for (x of []) yield, 1)",        top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list in genexp" },
-//{ expr: "(for (x of []) 1, yield)",        top: TOP_YIELD, fun: GENERIC,      gen: GENERIC,      desc: "simple yield at end of list in genexp" },
-//{ expr: "(for (x of []) (yield))",         top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (yield))",      top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield, parenthesized in list in genexp" },
-//{ expr: "(for (x of []) (1, yield))",      top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (2, yield))",   top: TOP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, desc: "simple yield at end of list, parenthesized in list in genexp" },
-//{ expr: "(for (x of []) (yield, 1))",      top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized in genexp" },
-//{ expr: "(for (x of []) 1, (yield, 2))",   top: TOP_YIELD, fun: YIELD_PAREN,  gen: YIELD_PAREN,  desc: "simple yield in list, parenthesized in list in genexp" },
+// of these errors would need to be updated.  (Note: line numbers below might be
+// mere placeholders, not actually the expected correct behavior -- check before
+// blindly uncommenting.)
+//{ expr: "yield",        top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield" },
+//{ expr: "1, yield",     top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list" },
+//{ expr: "yield, 1",     top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2],  desc: "simple yield in list" },
+//{ expr: "(yield)",      top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized" },
+//{ expr: "(1, yield)",   top: [TOP_YIELD, 777], fun: null,              gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized" },
+//{ expr: "(yield, 1)",   top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized" },
+//{ expr: "((((yield))))",   top: [TOP_YIELD, 777], fun: null, gen: [GENEXP_YIELD, 2], desc: "deeply nested yield" },
+//{ expr: "(for (x of []) yield)",           top: [TOP_YIELD, 777], fun: [GENERIC, 777],      gen: [GENERIC, 777],      desc: "simple yield in genexp" },
+//{ expr: "(for (x of []) yield, 1)",        top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list in genexp" },
+//{ expr: "(for (x of []) 1, yield)",        top: [TOP_YIELD, 777], fun: [GENERIC, 777],      gen: [GENERIC, 777],      desc: "simple yield at end of list in genexp" },
+//{ expr: "(for (x of []) (yield))",         top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (yield))",      top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield, parenthesized in list in genexp" },
+//{ expr: "(for (x of []) (1, yield))",      top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (2, yield))",   top: [TOP_YIELD, 777], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], desc: "simple yield at end of list, parenthesized in list in genexp" },
+//{ expr: "(for (x of []) (yield, 1))",      top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in genexp" },
+//{ expr: "(for (x of []) 1, (yield, 2))",   top: [TOP_YIELD, 777], fun: [YIELD_PAREN, 2],  gen: [YIELD_PAREN, 2],  desc: "simple yield in list, parenthesized in list in genexp" },
 //{ expr: "(for (x of []) (function*() { yield }))",           top: null, fun: null, gen: null, desc: "legal yield in nested function" },
 
   // yield expressions
-  { expr: "yield 1",      top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg" },
-  { expr: "1, yield 2",   top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg at end of list" },
-  { expr: "yield 1, 2",   top: MISSING_SEMI, fun: MISSING_SEMI, gen: null, genexp: FOR_OF_PAREN, desc: "yield w/ arg in list" },
-  { expr: "(yield 1)",    top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized" },
-  { expr: "(1, yield 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized" },
-  { expr: "(yield 1, 2)", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized" },
+  { expr: "yield 1",      top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg" },
+  { expr: "1, yield 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: null, genexp: [FOR_OF_PAREN, 1], desc: "yield w/ arg at end of list" },
+  { expr: "yield 1, 2",   top: [MISSING_SEMI, 2], fun: [MISSING_SEMI, 2], gen: [YIELD_PAREN, 3], genexp: [FOR_OF_PAREN, 3], desc: "yield w/ arg in list" },
+  { expr: "(yield 1)",    top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized" },
+  { expr: "(1, yield 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized" },
+  { expr: "(yield 1, 2)", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: [YIELD_PAREN, 3], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized" },
 
   // deeply nested yield expressions
-  { expr: "((((yield 1))))", top: MISSING_PAREN, fun: MISSING_PAREN, gen: null, genexp: GENEXP_YIELD, desc: "deeply nested yield w/ arg" },
+  { expr: "((((yield 1))))", top: [MISSING_PAREN, 2], fun: [MISSING_PAREN, 2], gen: null, genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield w/ arg" },
 
   // arguments
   { expr: "arguments",    top: null, fun: null, gen: null, genexp: null, desc: "arguments in list" },
-  { expr: "1, arguments", top: null, fun: null, gen: null, genexp: FOR_OF_PAREN, desc: "arguments in list" },
+  { expr: "1, arguments", top: null, fun: null, gen: null, genexp: [FOR_OF_PAREN, 1], desc: "arguments in list" },
 
   // yield in generator expressions
-  { expr: "(for (x of []) yield 1)",         top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in genexp" },
-  { expr: "(for (x of []) yield 1, 2)",      top: GENEXP_YIELD, fun: GENEXP_YIELD,  gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg in list in genexp" },
-  { expr: "(for (x of []) 1, yield 2)",      top: PAREN_PAREN, fun: PAREN_PAREN,  gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list in genexp" },
-  { expr: "(for (x of []) (yield 1))",       top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (yield 2))",    top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg, parenthesized in list in genexp" },
-  { expr: "(for (x of []) (1, yield 2))",    top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "yield w/ arg at end of list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (2, yield 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg at end of list, parenthesized in list in genexp" },
-  { expr: "(for (x of []) (yield 1, 2))",    top: YIELD_PAREN, fun: YIELD_PAREN, gen: YIELD_PAREN, genexp: YIELD_PAREN, desc: "yield w/ arg in list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (yield 2, 3))", top: PAREN_PAREN, fun: PAREN_PAREN, gen: PAREN_PAREN, genexp: PAREN_PAREN, desc: "yield w/ arg in list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) yield 1)",         top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in genexp" },
+  { expr: "(for (x of []) yield 1, 2)",      top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2],  gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg in list in genexp" },
+  { expr: "(for (x of []) 1, yield 2)",      top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1],  gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list in genexp" },
+  { expr: "(for (x of []) (yield 1))",       top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (yield 2))",    top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg, parenthesized in list in genexp" },
+  { expr: "(for (x of []) (1, yield 2))",    top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "yield w/ arg at end of list, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (2, yield 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg at end of list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) (yield 1, 2))",    top: [YIELD_PAREN, 2], fun: [YIELD_PAREN, 2], gen: [YIELD_PAREN, 2], genexp: [YIELD_PAREN, 2], desc: "yield w/ arg in list, parenthesized in genexp" },
+  { expr: "(for (x of []) 1, (yield 2, 3))", top: [PAREN_PAREN, 1], fun: [PAREN_PAREN, 1], gen: [PAREN_PAREN, 1], genexp: [PAREN_PAREN, 1], desc: "yield w/ arg in list, parenthesized in list in genexp" },
 
   // deeply nested yield in generator expressions
-  { expr: "(for (x of []) (((1, yield 2))))",               top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in genexp" },
-  { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: GENEXP_YIELD, fun: GENEXP_YIELD, gen: GENEXP_YIELD, genexp: GENEXP_YIELD, desc: "deeply nested yield in multiple genexps" },
+  { expr: "(for (x of []) (((1, yield 2))))",               top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in genexp" },
+  { expr: "(for (y of []) (for (x of []) ((1, yield 2))))", top: [GENEXP_YIELD, 2], fun: [GENEXP_YIELD, 2], gen: [GENEXP_YIELD, 2], genexp: [GENEXP_YIELD, 2], desc: "deeply nested yield in multiple genexps" },
 
   // arguments in generator expressions
   { expr: "(for (x of []) arguments)",         top: null, fun: null, gen: null, genexp: null, desc: "simple arguments in genexp" },
-  { expr: "(for (x of []) 1, arguments)",      top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list in genexp" },
+  { expr: "(for (x of []) 1, arguments)",      top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list in genexp" },
   { expr: "(for (x of []) (arguments))",       top: null, fun: null, gen: null, genexp: null, desc: "arguments, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (arguments))",    top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments, parenthesized in list in genexp" },
+  { expr: "(for (x of []) 1, (arguments))",    top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments, parenthesized in list in genexp" },
   { expr: "(for (x of []) (1, arguments))",    top: null, fun: null, gen: null, genexp: null, desc: "arguments in list, parenthesized in genexp" },
-  { expr: "(for (x of []) 1, (2, arguments))", top: BAD_GENERATOR_SYNTAX, fun: BAD_GENERATOR_SYNTAX, gen: BAD_GENERATOR_SYNTAX, genexp: BAD_GENERATOR_SYNTAX, desc: "arguments in list, parenthesized in list in genexp" },
+  { expr: "(for (x of []) 1, (2, arguments))", top: [BAD_GENERATOR_SYNTAX, 1], fun: [BAD_GENERATOR_SYNTAX, 1], gen: [BAD_GENERATOR_SYNTAX, 1], genexp: [BAD_GENERATOR_SYNTAX, 1], desc: "arguments in list, parenthesized in list in genexp" },
 
   // deeply nested arguments in generator expressions
   { expr: "(for (x of []) (((1, arguments))))",               top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in genexp" },
   { expr: "(for (y of []) (for (x of []) ((1, arguments))))", top: null, fun: null, gen: null, genexp: null, desc: "deeply nested arguments in multiple genexps" },
 
   // legal yield/arguments in nested function
   { expr: "(for (x of []) (function*() { yield 1 }))",         top: null, fun: null, gen: null, genexp: null, desc: "legal yield in nested function" },
   { expr: "(for (x of []) (function() { arguments }))",       top: null, fun: null, gen: null, genexp: null, desc: "legal arguments in nested function" },
@@ -105,25 +107,24 @@ test();
 function splitKeyword(str) {
   return str.
 //         replace(/[)] yield/, ')\nyield\n').
          replace(/yield ([0-9])/, '\nyield $1\n').
          replace(/yield([^ ]|$)/, '\nyield\n$1').
          replace(/arguments/, '\narguments\n');
 }
 
-function expectError1(err, ctx, msg) {
+function expectError1(err, ctx, msg, lineNumber) {
   reportCompare('object', typeof err,     'exn for: ' + msg);
   reportCompare(ctx,      err.message,    'exn message for: ' + msg);
-  if (ctx !== BAD_GENERATOR_SYNTAX && ctx != PAREN_PAREN && ctx != FOR_OF_PAREN)
-      reportCompare(2,    err.lineNumber, 'exn token for: ' + msg);
+  reportCompare(lineNumber,    err.lineNumber, 'exn token for: ' + msg);
 }
 
-function expectError(expr, wrapCtx, expect, msg) {
-  expectError1(error(wrapCtx(expr)), expect, msg);
+function expectError(expr, wrapCtx, [expect, lineNumber], msg) {
+  expectError1(error(wrapCtx(expr)), expect, msg, lineNumber);
 }
 
 function expectSuccess(err, msg) {
   reportCompare(null, err, 'parse: ' + msg);
 }
 
 function atTop(str) { return str }
 function inFun(str) { return '(function(){' + str + '})' }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Expressions/delete-name-parenthesized-early-error-strict-mode.js
@@ -0,0 +1,76 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1111101;
+var summary =
+  "delete (foo), delete ((foo)), and so on are strict mode early errors";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function checkSyntaxError(code)
+{
+  function helper(maker)
+  {
+    var earlyError = false;
+    try
+    {
+      var f = maker(code);
+
+      var error = "no early error, created a function with code <" + code + ">";
+      try
+      {
+        f();
+        error += ", and the function can be called without error";
+      }
+      catch (e)
+      {
+        error +=", and calling the function throws " + e;
+      }
+
+      throw new Error(error);
+    }
+    catch (e)
+    {
+      assertEq(e instanceof SyntaxError, true,
+               "expected syntax error, got " + e);
+    }
+  }
+
+  helper(Function);
+  helper(eval);
+}
+
+checkSyntaxError("function f() { 'use strict'; delete escape; } f();");
+checkSyntaxError("function f() { 'use strict'; delete escape; }");
+checkSyntaxError("function f() { 'use strict'; delete (escape); } f();");
+checkSyntaxError("function f() { 'use strict'; delete (escape); }");
+checkSyntaxError("function f() { 'use strict'; delete ((escape)); } f();");
+checkSyntaxError("function f() { 'use strict'; delete ((escape)); }");
+
+// Meanwhile, non-strict all of these should work
+
+function checkFine(code)
+{
+  Function(code);
+  (1, eval)(code); // indirect, to be consistent w/above
+}
+
+checkFine("function f() { delete escape; } f();");
+checkFine("function f() { delete escape; }");
+checkFine("function f() { delete (escape); } f();");
+checkFine("function f() { delete (escape); }");
+checkFine("function f() { delete ((escape)); } f();");
+checkFine("function f() { delete ((escape)); }");
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_6/Generators/syntax.js
+++ b/js/src/tests/ecma_6/Generators/syntax.js
@@ -29,17 +29,17 @@ function* g() { (yield 3) + (yield 4); }
 function* g() { yield; }
 function* g() { yield }
 function* g() {
     yield
 }
 function* g() { (yield) }
 function* g() { [yield] }
 function* g() { {yield} }
-function* g() { yield, yield }
+function* g() { (yield), (yield) }
 function* g() { yield; yield }
 function* g() { (yield) ? yield : yield }
 function* g() {
     (yield)
     ? yield
     : yield
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -491,32 +491,16 @@ class PerThreadData : public PerThreadDa
      * thread is associated.  This is private because accessing the
      * fields of this runtime can provoke race conditions, so the
      * intention is that access will be mediated through safe
      * functions like |runtimeFromMainThread| and |associatedWith()| below.
      */
     JSRuntime *runtime_;
 
   public:
-    /*
-     * We save all conservative scanned roots in this vector so that
-     * conservative scanning can be "replayed" deterministically. In DEBUG mode,
-     * this allows us to run a non-incremental GC after every incremental GC to
-     * ensure that no objects were missed.
-     */
-#ifdef DEBUG
-    struct SavedGCRoot {
-        void *thing;
-        JSGCTraceKind kind;
-
-        SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
-    };
-    js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
-#endif
-
 #ifdef JS_TRACE_LOGGING
     TraceLoggerThread   *traceLogger;
 #endif
 
     /* Pointer to the current AutoFlushICache. */
     js::jit::AutoFlushICache *autoFlushICache_;
 
   public:
--- a/media/gmp-clearkey/0.1/clearkey.info
+++ b/media/gmp-clearkey/0.1/clearkey.info
@@ -1,4 +1,4 @@
 Name: clearkey
 Description: ClearKey decrypt-only GMP plugin
 Version: 0.1
-APIs: eme-decrypt-v3[org.w3.clearkey]
+APIs: eme-decrypt-v4[org.w3.clearkey]
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -688,19 +688,67 @@ int NrSocket::read(void* buf, size_t max
     ABORT(R_EOD);
 
   *len = (size_t)status;  // Guaranteed to be > 0
   _status = 0;
 abort:
   return(_status);
 }
 
+NS_IMPL_ISUPPORTS(NrSocketIpcProxy, nsIUDPSocketInternal)
+
+nsresult
+NrSocketIpcProxy::Init(const nsRefPtr<NrSocketIpc>& socket)
+{
+  nsresult rv;
+  sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) {
+    MOZ_ASSERT(false, "Failed to get STS thread");
+    return rv;
+  }
+
+  socket_ = socket;
+  return NS_OK;
+}
+
+NrSocketIpcProxy::~NrSocketIpcProxy()
+{
+  // Send our ref to STS to be released
+  RUN_ON_THREAD(sts_thread_,
+                mozilla::WrapRelease(socket_.forget()),
+                NS_DISPATCH_NORMAL);
+}
+
+// IUDPSocketInternal interfaces
+// callback while error happened in UDP socket operation
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerError(const nsACString &message,
+                                                  const nsACString &filename,
+                                                  uint32_t line_number) {
+  return socket_->CallListenerError(message, filename, line_number);
+}
+
+// callback while receiving UDP packet
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerReceivedData(const nsACString &host,
+                                                         uint16_t port,
+                                                         const uint8_t *data,
+                                                         uint32_t data_length) {
+  return socket_->CallListenerReceivedData(host, port, data, data_length);
+}
+
+// callback while UDP socket is opened
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerOpened() {
+  return socket_->CallListenerOpened();
+}
+
+// callback while UDP socket is closed
+NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() {
+  return socket_->CallListenerClosed();
+}
+
 // NrSocketIpc Implementation
-NS_IMPL_ISUPPORTS(NrSocketIpc, nsIUDPSocketInternal)
-
 NrSocketIpc::NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread)
     : err_(false),
       state_(NR_INIT),
       main_thread_(main_thread),
       monitor_("NrSocketIpc") {
 }
 
 // IUDPSocketInternal interfaces
@@ -1017,17 +1065,25 @@ void NrSocketIpc::create_m(const nsACStr
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
     mon.NotifyAll();
     return;
   }
 
   socket_child_ = new nsMainThreadPtrHolder<nsIUDPSocketChild>(socketChild);
   socket_child_->SetFilterName(nsCString("stun"));
 
-  if (NS_FAILED(socket_child_->Bind(this, host, port,
+  nsRefPtr<NrSocketIpcProxy> proxy(new NrSocketIpcProxy);
+  rv = proxy->Init(this);
+  if (NS_FAILED(rv)) {
+    err_ = true;
+    mon.NotifyAll();
+    return;
+  }
+
+  if (NS_FAILED(socket_child_->Bind(proxy, host, port,
                                     /* reuse = */ false,
                                     /* loopback = */ false))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
     mon.NotifyAll();
     return;
   }
 }
@@ -1102,17 +1158,17 @@ static nr_socket_vtbl nr_socket_local_vt
   nr_socket_local_getaddr,
   nr_socket_local_connect,
   nr_socket_local_write,
   nr_socket_local_read,
   nr_socket_local_close
 };
 
 int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
-  NrSocketBase *sock = nullptr;
+  RefPtr<NrSocketBase> sock;
 
   // create IPC bridge for content process
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     sock = new NrSocket();
   } else {
     nsCOMPtr<nsIThread> main_thread;
     NS_GetMainThread(getter_AddRefs(main_thread));
     sock = new NrSocketIpc(main_thread.get());
@@ -1124,25 +1180,26 @@ int nr_socket_local_create(nr_transport_
   if (r)
     ABORT(r);
 
   r = nr_socket_create_int(static_cast<void *>(sock),
                            sock->vtbl(), sockp);
   if (r)
     ABORT(r);
 
-  // Add a reference so that we can delete it in destroy()
-  sock->AddRef();
-
   _status = 0;
 
+  {
+    // We will release this reference in destroy(), not exactly the normal
+    // ownership model, but it is what it is.
+    NrSocketBase* dummy = sock.forget().take();
+    (void)dummy;
+  }
+
 abort:
-  if (_status) {
-    delete sock;
-  }
   return _status;
 }
 
 
 static int nr_socket_local_destroy(void **objp) {
   if(!objp || !*objp)
     return 0;
 
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -183,30 +183,38 @@ struct nr_udp_message {
   PRNetAddr from;
   nsAutoPtr<DataBuffer> data;
 
 private:
   ~nr_udp_message() {}
   DISALLOW_COPY_ASSIGN(nr_udp_message);
 };
 
-class NrSocketIpc : public NrSocketBase,
-                    public nsIUDPSocketInternal {
+class NrSocketIpc : public NrSocketBase {
 public:
 
   enum NrSocketIpcState {
     NR_INIT,
     NR_CONNECTING,
     NR_CONNECTED,
     NR_CLOSING,
     NR_CLOSED,
   };
 
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIUDPSOCKETINTERNAL
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrSocketIpc)
+
+  NS_IMETHODIMP CallListenerError(const nsACString &message,
+                                  const nsACString &filename,
+                                  uint32_t line_number);
+  NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
+                                         uint16_t port,
+                                         const uint8_t *data,
+                                         uint32_t data_length);
+  NS_IMETHODIMP CallListenerOpened();
+  NS_IMETHODIMP CallListenerClosed();
 
   explicit NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread);
 
   // Implementations of the NrSocketBase APIs
   virtual int create(nr_transport_addr *addr) MOZ_OVERRIDE;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) MOZ_OVERRIDE;
   virtual int recvfrom(void * buf, size_t maxlen,
@@ -235,16 +243,32 @@ private:
   std::queue<RefPtr<nr_udp_message> > received_msgs_;
 
   nsMainThreadPtrHandle<nsIUDPSocketChild> socket_child_;
   nsCOMPtr<nsIEventTarget> sts_thread_;
   const nsCOMPtr<nsIEventTarget> main_thread_;
   ReentrantMonitor monitor_;
 };
 
+// The socket child holds onto one of these, which just passes callbacks
+// through and makes sure the ref to the NrSocketIpc is released on STS.
+class NrSocketIpcProxy : public nsIUDPSocketInternal {
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIUDPSOCKETINTERNAL
+
+  nsresult Init(const nsRefPtr<NrSocketIpc>& socket);
+
+private:
+  virtual ~NrSocketIpcProxy();
+
+  nsRefPtr<NrSocketIpc> socket_;
+  nsCOMPtr<nsIEventTarget> sts_thread_;
+};
+
 int nr_netaddr_to_transport_addr(const net::NetAddr *netaddr,
                                  nr_transport_addr *addr,
                                  int protocol);
 int nr_praddr_to_transport_addr(const PRNetAddr *praddr,
                                 nr_transport_addr *addr,
                                 int protocol, int keep);
 int nr_transport_addr_get_addrstring_and_port(nr_transport_addr *addr,
                                               nsACString *host, int32_t *port);
--- a/media/mtransport/runnable_utils.h
+++ b/media/mtransport/runnable_utils.h
@@ -91,11 +91,30 @@ RUN_ON_THREAD(nsIEventTarget *thread, de
       MOZ_ASSERT(NS_SUCCEEDED(rv));               \
       MOZ_ASSERT(on);                             \
     }                                           \
   } while(0)
 #else
 #define ASSERT_ON_THREAD(t)
 #endif
 
+template <class T>
+class DispatchedRelease : public detail::runnable_args_base<detail::NoResult> {
+public:
+  explicit DispatchedRelease(already_AddRefed<T>& ref) : ref_(ref) {}
+
+  NS_IMETHOD Run() {
+    ref_ = nullptr;
+    return NS_OK;
+  }
+private:
+  nsRefPtr<T> ref_;
+};
+
+template <typename T>
+DispatchedRelease<T>* WrapRelease(already_AddRefed<T>&& ref)
+{
+  return new DispatchedRelease<T>(ref);
+}
+
 } /* namespace mozilla */
 
 #endif
--- a/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_server_ctx.c
@@ -104,16 +104,17 @@ static int nr_stun_server_client_create(
       ABORT(R_NO_MEMORY);
 
     if(!(clnt->username=r_strdup(user)))
       ABORT(R_NO_MEMORY);
 
     if(r=r_data_copy(&clnt->password,pass))
       ABORT(r);
 
+    r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-SERVER(%s)/CLIENT(%s): Adding client for %s",ctx->label, client_label, user);
     clnt->stun_server_cb=cb;
     clnt->cb_arg=cb_arg;
 
     *clntp = clnt;
     _status=0;
  abort:
     if(_status){
       nr_stun_server_destroy_client(clnt);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -208,16 +208,17 @@ PeerConnectionImpl* PeerConnectionImpl::
 NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::
 OnProxyAvailable(nsICancelable *request,
                  nsIChannel *aChannel,
                  nsIProxyInfo *proxyinfo,
                  nsresult result) {
   CSFLogInfo(logTag, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
 
   if (NS_SUCCEEDED(result) && proxyinfo) {
+    CSFLogInfo(logTag, "%s: Had proxyinfo", __FUNCTION__);
     nsresult rv;
     nsCString httpsProxyHost;
     int32_t httpsProxyPort;
 
     rv = proxyinfo->GetHost(httpsProxyHost);
     if (NS_FAILED(rv)) {
       CSFLogError(logTag, "%s: Failed to get proxy server host", __FUNCTION__);
       return rv;
@@ -238,17 +239,17 @@ OnProxyAvailable(nsICancelable *request,
       CSFLogError(logTag, "%s: Failed to set proxy server (ICE ctx unavailable)",
           __FUNCTION__);
     }
   }
 
   if (result != NS_ERROR_ABORT) {
     // NS_ERROR_ABORT means that the PeerConnectionMedia is no longer waiting
     pcm_->mProxyResolveCompleted = true;
-    pcm_->GatherIfReady();
+    pcm_->FlushIceCtxOperationQueueIfReady();
   }
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback)
 
 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
@@ -256,17 +257,16 @@ PeerConnectionMedia::PeerConnectionMedia
       mParentHandle(parent->GetHandle()),
       mParentName(parent->GetName()),
       mAllowIceLoopback(false),
       mIceCtx(nullptr),
       mDNSResolver(new mozilla::NrIceResolver()),
       mUuidGen(MakeUnique<PCUuidGenerator>()),
       mMainThread(mParent->GetMainThread()),
       mSTSThread(mParent->GetSTSThread()),
-      mTransportsUpdated(false),
       mProxyResolveCompleted(false) {
   nsresult rv;
 
   nsCOMPtr<nsIProtocolProxyService> pps =
     do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     CSFLogError(logTag, "%s: Failed to get proxy service: %d", __FUNCTION__, (int)rv);
     return;
@@ -395,17 +395,16 @@ PeerConnectionMedia::UpdateTransports(co
                                pwd,
                                candidates),
                   NS_DISPATCH_NORMAL);
   }
 
 
   // TODO(bug 1017888): Need to deal properly with renegotatiation.
   // For now just start gathering.
-  mTransportsUpdated = true;
   GatherIfReady();
 }
 
 nsresult PeerConnectionMedia::UpdateMediaPipelines(
     const mozilla::JsepSession& session) {
   size_t numPairs = session.GetNegotiatedTrackPairCount();
   mozilla::MediaPipelineFactory factory(this);
   const mozilla::JsepTrackPair* pair;
@@ -462,26 +461,27 @@ PeerConnectionMedia::StartIceChecks(cons
     } else {
       CSFLogDebug(logTag, "Transport %u has %u components",
                           static_cast<unsigned>(i),
                           static_cast<unsigned>(transport->mComponents));
       numComponentsByLevel.push_back(transport->mComponents);
     }
   }
 
-  RUN_ON_THREAD(GetSTSThread(),
-                WrapRunnable(
-                  RefPtr<PeerConnectionMedia>(this),
-                  &PeerConnectionMedia::StartIceChecks_s,
-                  session.IsIceControlling(),
-                  session.RemoteIsIceLite(),
-                  // Copy, just in case API changes to return a ref
-                  std::vector<std::string>(session.GetIceOptions()),
-                  numComponentsByLevel),
-                NS_DISPATCH_NORMAL);
+  nsRefPtr<nsIRunnable> runnable(
+      WrapRunnable(
+        RefPtr<PeerConnectionMedia>(this),
+        &PeerConnectionMedia::StartIceChecks_s,
+        session.IsIceControlling(),
+        session.RemoteIsIceLite(),
+        // Copy, just in case API changes to return a ref
+        std::vector<std::string>(session.GetIceOptions()),
+        numComponentsByLevel));
+
+  PerformOrEnqueueIceCtxOperation(runnable);
 }
 
 void
 PeerConnectionMedia::StartIceChecks_s(
     bool aIsControlling,
     bool aIsIceLite,
     const std::vector<std::string>& aIceOptionsList,
     const std::vector<size_t>& aComponentCountByLevel) {
@@ -552,26 +552,52 @@ PeerConnectionMedia::AddIceCandidate_s(c
   if (NS_FAILED(rv)) {
     CSFLogError(logTag, "Couldn't process ICE candidate at level %u",
                 aMLine);
     return;
   }
 }
 
 void
+PeerConnectionMedia::FlushIceCtxOperationQueueIfReady()
+{
+  ASSERT_ON_THREAD(mMainThread);
+
+  if (IsIceCtxReady()) {
+    for (auto i = mQueuedIceCtxOperations.begin();
+         i != mQueuedIceCtxOperations.end();
+         ++i) {
+      GetSTSThread()->Dispatch(*i, NS_DISPATCH_NORMAL);
+    }
+    mQueuedIceCtxOperations.clear();
+  }
+}
+
+void
+PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(
+    const nsRefPtr<nsIRunnable>& runnable)
+{
+  ASSERT_ON_THREAD(mMainThread);
+
+  if (IsIceCtxReady()) {
+    GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
+  } else {
+    mQueuedIceCtxOperations.push_back(runnable);
+  }
+}
+
+void
 PeerConnectionMedia::GatherIfReady() {
   ASSERT_ON_THREAD(mMainThread);
 
-  if (mTransportsUpdated && mProxyResolveCompleted) {
-    RUN_ON_THREAD(GetSTSThread(),
-        WrapRunnable(
-          RefPtr<PeerConnectionMedia>(this),
-          &PeerConnectionMedia::EnsureIceGathering_s),
-        NS_DISPATCH_NORMAL);
-  }
+  nsRefPtr<nsIRunnable> runnable(WrapRunnable(
+        RefPtr<PeerConnectionMedia>(this),
+        &PeerConnectionMedia::EnsureIceGathering_s));
+
+  PerformOrEnqueueIceCtxOperation(runnable);
 }
 
 void
 PeerConnectionMedia::EnsureIceGathering_s() {
   if (mIceCtx->gathering_state() == NrIceCtx::ICE_CTX_GATHER_INIT) {
     if (mProxyServer) {
       mIceCtx->SetProxyServer(*mProxyServer);
     }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -461,16 +461,18 @@ class PeerConnectionMedia : public sigsl
 
   // Manage ICE transports.
   void UpdateIceMediaStream_s(size_t aMLine, size_t aComponentCount,
                               bool aHasAttrs,
                               const std::string& aUfrag,
                               const std::string& aPassword,
                               const std::vector<std::string>& aCandidateList);
   void GatherIfReady();
+  void FlushIceCtxOperationQueueIfReady();
+  void PerformOrEnqueueIceCtxOperation(const nsRefPtr<nsIRunnable>& runnable);
   void EnsureIceGathering_s();
   void StartIceChecks_s(bool aIsControlling,
                         bool aIsIceLite,
                         const std::vector<std::string>& aIceOptionsList,
                         const std::vector<size_t>& aComponentCountByLevel);
 
   // Process a trickle ICE candidate.
   void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
@@ -492,16 +494,19 @@ class PeerConnectionMedia : public sigsl
   void IceGatheringStateChange_m(mozilla::NrIceCtx* ctx,
                                  mozilla::NrIceCtx::GatheringState state);
   void IceConnectionStateChange_m(mozilla::NrIceCtx* ctx,
                                   mozilla::NrIceCtx::ConnectionState state);
   void OnCandidateFound_m(const std::string &candidate, uint16_t aMLine);
   void EndOfLocalCandidates_m(const std::string& aDefaultAddr,
                               uint16_t aDefaultPort,
                               uint16_t aMLine);
+  bool IsIceCtxReady() const {
+    return mProxyResolveCompleted;
+  }
 
 
   // The parent PC
   PeerConnectionImpl *mParent;
   // and a loose handle on it for event driven stuff
   std::string mParentHandle;
   std::string mParentName;
 
@@ -534,18 +539,21 @@ class PeerConnectionMedia : public sigsl
   mozilla::UniquePtr<PCUuidGenerator> mUuidGen;
 
   // The main thread.
   nsCOMPtr<nsIThread> mMainThread;
 
   // The STS thread.
   nsCOMPtr<nsIEventTarget> mSTSThread;
 
-  // Used to track when transports are updated and are ready to start gathering
-  bool mTransportsUpdated;
+  // Used whenever we need to dispatch a runnable to STS to tweak something
+  // on our ICE ctx, but are not ready to do so at the moment (eg; we are
+  // waiting to get a callback with our http proxy config before we start
+  // gathering or start checking)
+  std::vector<nsRefPtr<nsIRunnable>> mQueuedIceCtxOperations;
 
   // Used to cancel any ongoing proxy request.
   nsCOMPtr<nsICancelable> mProxyRequest;
 
   // Used to track the state of the request.
   bool mProxyResolveCompleted;
 
   // Used to store the result of the request.
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -154,16 +154,22 @@ static DllBlockInfo sWindowsDllBlocklist
   { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP },
 
   // Crashes with RoboForm2Go written against old SDK, bug 988311
   { "rf-firefox-22.dll", ALL_VERSIONS },
 
   // Crashes with DesktopTemperature, bug 1046382
   { "dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP },
 
+  // Startup crashes with Lenovo Onekey Theater, bug 1123778
+  { "activedetect32.dll", UNVERSIONED },
+  { "activedetect64.dll", UNVERSIONED },
+  { "windowsapihookdll32.dll", UNVERSIONED },
+  { "windowsapihookdll64.dll", UNVERSIONED },
+
   { nullptr, 0 }
 };
 
 #ifndef STATUS_DLL_NOT_FOUND
 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
 #endif
 
 // define this for very verbose dll load debug spew
--- a/netwerk/cache2/CacheFile.cpp
+++ b/netwerk/cache2/CacheFile.cpp
@@ -607,18 +607,19 @@ CacheFile::OnMetadataWritten(nsresult aR
   LOG(("CacheFile::OnMetadataWritten() [this=%p, rv=0x%08x]", this, aResult));
 
   MOZ_ASSERT(mWritingMetadata);
   mWritingMetadata = false;
 
   MOZ_ASSERT(!mMemoryOnly);
   MOZ_ASSERT(!mOpeningFile);
 
-  if (NS_FAILED(aResult)) {
+  if (NS_WARN_IF(NS_FAILED(aResult))) {
     // TODO close streams with an error ???
+    SetError(aResult);
   }
 
   if (mOutput || mInputs.Length() || mChunks.Count())
     return NS_OK;
 
   if (IsDirty())
     WriteMetadataIfNeededLocked();
 
--- a/netwerk/cache2/CacheFileIOManager.cpp
+++ b/netwerk/cache2/CacheFileIOManager.cpp
@@ -701,16 +701,20 @@ public:
     nsresult rv;
 
     MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-background");
     if (mHandle->IsClosed()) {
       rv = NS_ERROR_NOT_INITIALIZED;
     } else {
       rv = CacheFileIOManager::gInstance->WriteInternal(
           mHandle, mOffset, mBuf, mCount, mValidate);
+      if (NS_FAILED(rv) && !mCallback) {
+        // No listener is going to handle the error, doom the file
+        CacheFileIOManager::gInstance->DoomFileInternal(mHandle);
+      }
     }
     MOZ_EVENT_TRACER_DONE(static_cast<nsIRunnable*>(this), "net::cache::write-background");
 
     MOZ_EVENT_TRACER_EXEC(static_cast<nsIRunnable*>(this), "net::cache::write-result");
     if (mCallback) {
       mCallback->OnDataWritten(mHandle, mBuf, rv);
     } else {
       free(const_cast<char *>(mBuf));
--- a/security/pkix/test/gtest/pkixbuild_tests.cpp
+++ b/security/pkix/test/gtest/pkixbuild_tests.cpp
@@ -137,17 +137,17 @@ private:
   }
 
   Result IsChainValid(const DERArray&, Time) override
   {
     return Success;
   }
 
   Result VerifySignedData(const SignedDataWithSignature& signedData,
-                          Input subjectPublicKeyInfo)
+                          Input subjectPublicKeyInfo) override
   {
     return TestVerifySignedData(signedData, subjectPublicKeyInfo);
   }
 
   Result DigestBuf(Input item, /*out*/ uint8_t *digestBuf, size_t digestBufLen)
                    override
   {
     ADD_FAILURE();
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-common/async.js");
 Cu.import("resource://testing-common/services/common/utils.js");
+Cu.import("resource://testing-common/PlacesTestUtils.jsm");
 
 let provider = {
   getFile: function(prop, persistent) {
     persistent.value = true;
     switch (prop) {
       case "ExtPrefDL":
         return [Services.dirsvc.get("CurProcD", Ci.nsIFile)];
       default:
--- a/services/sync/tests/unit/test_history_engine.js
+++ b/services/sync/tests/unit/test_history_engine.js
@@ -1,30 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
 Cu.import("resource://services-sync/constants.js");
 Cu.import("resource://services-sync/engines/history.js");
 Cu.import("resource://services-sync/engines.js");
 Cu.import("resource://services-sync/identity.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/service.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://testing-common/services/sync/utils.js");
 
 Service.engineManager.clear();
 
+add_test(function test_setup() {
+  PlacesTestUtils.clearHistory().then(run_next_test);
+});
+
 add_test(function test_processIncoming_mobile_history_batched() {
   _("SyncEngine._processIncoming works on history engine.");
 
   let FAKE_DOWNLOAD_LIMIT = 100;
 
   Svc.Prefs.set("client.type", "mobile");
-  PlacesUtils.history.removeAllPages();
   Service.engineManager.register(HistoryEngine);
 
   // A collection that logs each GET
   let collection = new ServerCollection();
   collection.get_log = [];
   collection._get = collection.get;
   collection.get = function (options) {
     this.get_log.push(options);
@@ -125,20 +127,21 @@ add_test(function test_processIncoming_m
       do_check_eq(collection.get_log[j].limit, undefined);
       if (i < Math.floor((234 - 50) / MOBILE_BATCH_SIZE))
         do_check_eq(collection.get_log[j].ids.length, MOBILE_BATCH_SIZE);
       else
         do_check_eq(collection.get_log[j].ids.length, 234 % MOBILE_BATCH_SIZE);
     }
 
   } finally {
-    PlacesUtils.history.removeAllPages();
-    server.stop(do_test_finished);
-    Svc.Prefs.resetBranch("");
-    Service.recordManager.clearCache();
+    PlacesTestUtils.clearHistory().then(() => {
+      server.stop(do_test_finished);
+      Svc.Prefs.resetBranch("");
+      Service.recordManager.clearCache();
+    });
   }
 });
 
 function run_test() {
   generateNewKeys(Service.collectionKeys);
 
   run_next_test();
 }
--- a/services/sync/tests/unit/test_history_store.js
+++ b/services/sync/tests/unit/test_history_store.js
@@ -57,17 +57,17 @@ function onNextTitleChanged(callback) {
 
 // Ensure exceptions from inside callbacks leads to test failures while
 // we still clean up properly.
 function ensureThrows(func) {
   return function() {
     try {
       func.apply(this, arguments);
     } catch (ex) {
-      PlacesUtils.history.removeAllPages();
+      PlacesTestUtils.clearHistory();
       do_throw(ex);
     }
   };
 }
 
 let store = new HistoryEngine(Service)._store;
 function applyEnsureNoFailures(records) {
   do_check_eq(store.applyIncomingBatch(records).length, 0);
@@ -295,11 +295,10 @@ add_test(function test_remove() {
   do_check_eq(queryres.length, 0);
   queryres = queryHistoryVisits(tburi);
   do_check_eq(queryres.length, 0);
   run_next_test();
 });
 
 add_test(function cleanup() {
   _("Clean up.");
-  PlacesUtils.history.removeAllPages();
-  run_next_test();
+  PlacesTestUtils.clearHistory().then(run_next_test);
 });
--- a/services/sync/tests/unit/test_history_tracker.js
+++ b/services/sync/tests/unit/test_history_tracker.js
@@ -194,11 +194,10 @@ add_test(function test_stop_tracking_twi
   Utils.nextTick(function() {
     do_check_empty(tracker.changedIDs);
     run_next_test();
   });
 });
 
 add_test(function cleanup() {
    _("Clean up.");
-  PlacesUtils.history.removeAllPages();
-  run_next_test();
+  PlacesTestUtils.clearHistory().then(run_next_test);
 });
--- a/toolkit/components/places/History.cpp
+++ b/toolkit/components/places/History.cpp
@@ -907,34 +907,50 @@ public:
     if (mHistory->IsShuttingDown()) {
       // If we were already shutting down, we cannot insert the URIs.
       return NS_OK;
     }
 
     mozStorageTransaction transaction(mDBConn, false,
                                       mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
-    VisitData* lastPlace = nullptr;
+    VisitData* lastFetchedPlace = nullptr;
     for (nsTArray<VisitData>::size_type i = 0; i < mPlaces.Length(); i++) {
       VisitData& place = mPlaces.ElementAt(i);
       VisitData& referrer = mReferrers.ElementAt(i);
 
+      // Fetching from the database can overwrite this information, so save it
+      // apart.
+      bool typed = place.typed;
+      bool hidden = place.hidden;
+
       // We can avoid a database lookup if it's the same place as the last
       // visit we added.
-      bool known = lastPlace && lastPlace->IsSamePlaceAs(place);
+      bool known = lastFetchedPlace && lastFetchedPlace->IsSamePlaceAs(place);
       if (!known) {
         nsresult rv = mHistory->FetchPageInfo(place, &known);
         if (NS_FAILED(rv)) {
           if (!!mCallback) {
             nsCOMPtr<nsIRunnable> event =
               new NotifyPlaceInfoCallback(mCallback, place, true, rv);
             return NS_DispatchToMainThread(event);
           }
           return NS_OK;
         }
+        lastFetchedPlace = &mPlaces.ElementAt(i);
+      }
+
+      // If any transition is typed, ensure the page is marked as typed.
+      if (typed != lastFetchedPlace->typed) {
+        place.typed = true;
+      }
+
+      // If any transition is visible, ensure the page is marked as visible.
+      if (hidden != lastFetchedPlace->hidden) {
+        place.hidden = false;
       }
 
       FetchReferrerInfo(referrer, place);
 
       nsresult rv = DoDatabaseInserts(known, place, referrer);
       if (!!mCallback) {
         nsCOMPtr<nsIRunnable> event =
           new NotifyPlaceInfoCallback(mCallback, place, true, rv);
@@ -948,18 +964,16 @@ public:
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Notify about title change if needed.
       if ((!known && !place.title.IsVoid()) || place.titleChanged) {
         event = new NotifyTitleObservers(place.spec, place.title, place.guid);
         rv = NS_DispatchToMainThread(event);
         NS_ENSURE_SUCCESS(rv, rv);
       }
-
-      lastPlace = &mPlaces.ElementAt(i);
     }
 
     nsresult rv = transaction.Commit();
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
   }
 private:
@@ -2286,34 +2300,25 @@ History::FetchPageInfo(VisitData& _place
     _place.title = title;
   }
   // Otherwise, just indicate if the title has changed.
   else {
     _place.titleChanged = !(_place.title.Equals(title) ||
                             (_place.title.IsEmpty() && title.IsVoid()));
   }
 
-  if (_place.hidden) {
-    // If this transition was hidden, it is possible that others were not.
-    // Any one visible transition makes this location visible. If database
-    // has location as visible, reflect that in our data structure.
-    int32_t hidden;
-    rv = stmt->GetInt32(3, &hidden);
-    NS_ENSURE_SUCCESS(rv, rv);
-    _place.hidden = !!hidden;
-  }
-
-  if (!_place.typed) {
-    // If this transition wasn't typed, others might have been. If database
-    // has location as typed, reflect that in our data structure.
-    int32_t typed;
-    rv = stmt->GetInt32(4, &typed);
-    NS_ENSURE_SUCCESS(rv, rv);
-    _place.typed = !!typed;
-  }
+  int32_t hidden;
+  rv = stmt->GetInt32(3, &hidden);
+  NS_ENSURE_SUCCESS(rv, rv);
+  _place.hidden = !!hidden;
+
+  int32_t typed;
+  rv = stmt->GetInt32(4, &typed);
+  NS_ENSURE_SUCCESS(rv, rv);
+  _place.typed = !!typed;
 
   rv = stmt->GetInt32(5, &_place.frecency);
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 MOZ_DEFINE_MALLOC_SIZE_OF(HistoryMallocSizeOf)
 
--- a/toolkit/components/places/tests/unit/test_async_history_api.js
+++ b/toolkit/components/places/tests/unit/test_async_history_api.js
@@ -19,67 +19,61 @@ const RECENT_EVENT_THRESHOLD = 15 * 60 *
  *
  * @param [optional] aTransitionType
  *        The transition type of the visit.  Defaults to TRANSITION_LINK if not
  *        provided.
  * @param [optional] aVisitTime
  *        The time of the visit.  Defaults to now if not provided.
  */
 function VisitInfo(aTransitionType,
-                   aVisitTime)
-{
+                   aVisitTime) {
   this.transitionType =
     aTransitionType === undefined ? TRANSITION_LINK : aTransitionType;
   this.visitDate = aVisitTime || Date.now() * 1000;
 }
 
 function promiseUpdatePlaces(aPlaces) {
-  let deferred = Promise.defer();
-  PlacesUtils.asyncHistory.updatePlaces(aPlaces, {
-    _errors: [],
-    _results: [],
-    handleError: function handleError(aResultCode, aPlace) {
-      this._errors.push({ resultCode: aResultCode, info: aPlace});
-    },
-    handleResult: function handleResult(aPlace) {
-      this._results.push(aPlace);
-    },
-    handleCompletion: function handleCompletion() {
-      deferred.resolve({ errors: this._errors, results: this._results });
-    }
+  return new Promise((resolve, reject) => {
+    PlacesUtils.asyncHistory.updatePlaces(aPlaces, {
+      _errors: [],
+      _results: [],
+      handleError(aResultCode, aPlace) {
+        this._errors.push({ resultCode: aResultCode, info: aPlace});
+      },
+      handleResult(aPlace) {
+        this._results.push(aPlace);
+      },
+      handleCompletion() {
+        resolve({ errors: this._errors, results: this._results });
+      }
+    });
   });
-
-  return deferred.promise;
 }
 
 /**
  * Listens for a title change notification, and calls aCallback when it gets it.
  *
  * @param aURI
  *        The URI of the page we expect a notification for.
  * @param aExpectedTitle
  *        The expected title of the URI we expect a notification for.
  * @param aCallback
  *        The method to call when we have gotten the proper notification about
  *        the title changing.
  */
 function TitleChangedObserver(aURI,
                               aExpectedTitle,
-                              aCallback)
-{
+                              aCallback) {
   this.uri = aURI;
   this.expectedTitle = aExpectedTitle;
   this.callback = aCallback;
 }
 TitleChangedObserver.prototype = {
   __proto__: NavHistoryObserver.prototype,
-  onTitleChanged: function(aURI,
-                           aTitle,
-                           aGUID)
-  {
+  onTitleChanged(aURI, aTitle, aGUID) {
     do_log_info("onTitleChanged(" + aURI.spec + ", " + aTitle + ", " + aGUID + ")");
     if (!this.uri.equals(aURI)) {
       return;
     }
     do_check_eq(aTitle, this.expectedTitle);
     do_check_guid_for_uri(aURI, aGUID);
     this.callback();
   },
@@ -143,24 +137,22 @@ function do_check_title_for_uri(aURI,
   do_check_true(stmt.executeStep(), stack);
   do_check_eq(stmt.row.title, aTitle, stack);
   stmt.finalize();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Functions
 
-function test_interface_exists()
-{
+add_task(function* test_interface_exists() {
   let history = Cc["@mozilla.org/browser/history;1"].getService(Ci.nsISupports);
   do_check_true(history instanceof Ci.mozIAsyncHistory);
-}
+});
 
-function test_invalid_uri_throws()
-{
+add_task(function* test_invalid_uri_throws() {
   // First, test passing in nothing.
   let place = {
     visits: [
       new VisitInfo(),
     ],
   };
   try {
     yield promiseUpdatePlaces(place);
@@ -183,20 +175,19 @@ function test_invalid_uri_throws()
     try {
       yield promiseUpdatePlaces(place);
       do_throw("Should have thrown!");
     }
     catch (e) {
       do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
     }
   }
-}
+});
 
-function test_invalid_places_throws()
-{
+add_task(function* test_invalid_places_throws() {
   // First, test passing in nothing.
   try {
     PlacesUtils.asyncHistory.updatePlaces();
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
@@ -214,20 +205,19 @@ function test_invalid_places_throws()
     try {
       yield promiseUpdatePlaces(value);
       do_throw("Should have thrown!");
     }
     catch (e) {
       do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
     }
   }
-}
+});
 
-function test_invalid_guid_throws()
-{
+add_task(function* test_invalid_guid_throws() {
   // First check invalid length guid.
   let place = {
     guid: "BAD_GUID",
     uri: NetUtil.newURI(TEST_DOMAIN + "test_invalid_guid_throws"),
     visits: [
       new VisitInfo(),
     ],
   };
@@ -244,20 +234,19 @@ function test_invalid_guid_throws()
   do_check_eq(place.guid.length, 12);
   try {
     yield promiseUpdatePlaces(place);
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
   }
-}
+});
 
-function test_no_visits_throws()
-{
+add_task(function* test_no_visits_throws() {
   const TEST_URI =
     NetUtil.newURI(TEST_DOMAIN + "test_no_id_or_guid_no_visits_throws");
   const TEST_GUID = "_RANDOMGUID_";
   const TEST_PLACEID = 2;
 
   let log_test_conditions = function(aPlace) {
     let str = "Testing place with " +
       (aPlace.uri ? "uri" : "no uri") + ", " +
@@ -285,56 +274,53 @@ function test_no_visits_throws()
           do_throw("Should have thrown!");
         }
         catch (e) {
           do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
         }
       }
     }
   }
-}
+});
 
-function test_add_visit_no_date_throws()
-{
+add_task(function* test_add_visit_no_date_throws() {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_date_throws"),
     visits: [
       new VisitInfo(),
     ],
   };
   delete place.visits[0].visitDate;
   try {
     yield promiseUpdatePlaces(place);
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
   }
-}
+});
 
-function test_add_visit_no_transitionType_throws()
-{
+add_task(function* test_add_visit_no_transitionType_throws() {
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN + "test_add_visit_no_transitionType_throws"),
     visits: [
       new VisitInfo(),
     ],
   };
   delete place.visits[0].transitionType;
   try {
     yield promiseUpdatePlaces(place);
     do_throw("Should have thrown!");
   }
   catch (e) {
     do_check_eq(e.result, Cr.NS_ERROR_INVALID_ARG);
   }
-}
+});
 
-function test_add_visit_invalid_transitionType_throws()
-{
+add_task(function* test_add_visit_invalid_transitionType_throws() {
   // First, test something that has a transition type lower than the first one.
   let place = {
     uri: NetUtil.newURI(TEST_DOMAIN +
                         "test_add_visit_invalid_transitionType_throws"),
     visits: [