Merge m-c to b2g-inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 27 Jan 2015 16:19:48 -0800
changeset 226100 4a09a5431c88456fbf19fa6ca722598ad5123247
parent 226099 68cd3912adbbea2133d6ce4e26e0302c05eeb5c2 (current diff)
parent 226089 b2b10231606bb89fb75d11596abc49a9b6eecbc2 (diff)
child 226101 51e05062748c9217fb5f18f7bdcd60abf560a7b9
push id28186
push usercbook@mozilla.com
push dateWed, 28 Jan 2015 13:16:00 +0000
treeherdermozilla-central@af476081c7fc [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge 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() {