Merge m-c to b2g-inbound a=merge despite the CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Wed, 01 Oct 2014 17:09:34 -0700
changeset 231625 f4ab8e8b595b886c35fcd6087a40aafb3136ec70
parent 231624 f3ddcc2343254dd6a89e18096303ba48eff0928d (current diff)
parent 231492 2399d1ae89e9675702b0e36acf7f471b769a54cc (diff)
child 231626 a0b82c9542069f902161756bc7f3b00cdb090883
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone35.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 despite the CLOSED TREE
mobile/android/base/widget/TabRow.java
mobile/android/modules/FireflyApp.jsm
testing/web-platform/meta/html-imports/fetching/already-in-import-map.html.ini
--- a/browser/base/content/test/general/browser_urlHighlight.js
+++ b/browser/base/content/test/general/browser_urlHighlight.js
@@ -14,21 +14,17 @@ function testVal(aExpected) {
     let pos = value.indexOf(range);
     result += value.substring(0, pos) + "<" + range + ">";
     value = value.substring(pos + range.length);
   }
   result += value;
   is(result, aExpected);
 }
 
-add_task(function* () {
-  return new Promise(resolve => Services.search.init(resolve));
-});
-
-add_task(function* () {
+function test() {
   const prefname = "browser.urlbar.formatting.enabled";
 
   registerCleanupFunction(function () {
     Services.prefs.clearUserPref(prefname);
     URLBarSetURI();
   });
 
   Services.prefs.setBoolPref(prefname, true);
@@ -108,9 +104,9 @@ add_task(function* () {
   testVal("foo9://mozilla.org/");
   testVal("foo+://mozilla.org/");
   testVal("foo.://mozilla.org/");
   testVal("foo-://mozilla.org/");
 
   Services.prefs.setBoolPref(prefname, false);
 
   testVal("https://mozilla.org");
-});
+}
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -50,8 +50,9 @@ skip-if = e10s # Bug 915547 (social prov
 [browser_social_multiprovider.js]
 [browser_social_multiworker.js]
 [browser_social_perwindowPB.js]
 [browser_social_sidebar.js]
 [browser_social_status.js]
 skip-if = e10s # Bug 915547 (social providers don't install)
 [browser_social_window.js]
 [browser_social_workercrash.js]
+skip-if = !crashreporter
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -180,35 +180,21 @@
         <body><![CDATA[
           // This method must not modify the given URL such that calling
           // nsIURIFixup::createFixupURI with the result will produce a different URI.
           return this._mayTrimURLs ? trimURL(aURL) : aURL;
         ]]></body>
       </method>
 
       <field name="_formattingEnabled">true</field>
-      <field name="_searchServiceInitialized">false</field>
       <method name="formatValue">
         <body><![CDATA[
           if (!this._formattingEnabled || this.focused)
             return;
 
-          // Initialize the search service asynchronously if that hasn't
-          // happened yet. We will need it to highlight search terms later.
-          if (!this._searchServiceInitialized) {
-            Services.search.init(() => {
-              if (this.formatValue) {
-                this._searchServiceInitialized = true;
-                this.formatValue();
-              }
-            });
-
-            return;
-          }
-
           let controller = this.editor.selectionController;
           let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
           selection.removeAllRanges();
 
           let textNode = this.editor.rootElement.firstChild;
           let value = textNode.textContent;
 
           let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
@@ -233,39 +219,30 @@
                 baseDomain = IDNService.convertACEtoUTF8(baseDomain);
               }
             } catch (e) {}
           }
           if (baseDomain != domain) {
             subDomain = domain.slice(0, -baseDomain.length);
           }
 
-          function addSelectionRange(start, end) {
+          let rangeLength = preDomain.length + subDomain.length;
+          if (rangeLength) {
             let range = document.createRange();
-            range.setStart(textNode, start);
-            range.setEnd(textNode, end);
+            range.setStart(textNode, 0);
+            range.setEnd(textNode, rangeLength);
             selection.addRange(range);
           }
 
-          let rangeLength = preDomain.length + subDomain.length;
-          if (rangeLength) {
-            addSelectionRange(0, rangeLength);
-          }
-
-          let result = Services.search.parseSubmissionURL(value);
           let startRest = preDomain.length + domain.length;
-
-          // Format search terms in the URL, if any.
-          if (result.termsOffset > -1 && result.termsLength) {
-            addSelectionRange(startRest, result.termsOffset);
-            startRest = result.termsOffset + result.termsLength;
-          }
-
           if (startRest < value.length) {
-            addSelectionRange(startRest, value.length);
+            let range = document.createRange();
+            range.setStart(textNode, startRest);
+            range.setEnd(textNode, value.length);
+            selection.addRange(range);
           }
         ]]></body>
       </method>
 
       <method name="_clearFormatting">
         <body><![CDATA[
           if (!this._formattingEnabled)
             return;
@@ -645,17 +622,16 @@
                 break;
               case "autoFill":
                 this.completeDefaultIndex = this._prefs.getBoolPref(aData);
                 break;
               case "delay":
                 this.timeout = this._prefs.getIntPref(aData);
                 break;
               case "formatting.enabled":
-                this._clearFormatting();
                 this._formattingEnabled = this._prefs.getBoolPref(aData);
                 break;
               case "trimURLs":
                 this._mayTrimURLs = this._prefs.getBoolPref(aData);
                 break;
               case "unifiedcomplete":
                 let useUnifiedComplete = false;
                 try {
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -63,25 +63,16 @@ loop.conversation = (function(mozL10n) {
 
     _handleDeclineBlock: function(e) {
       this.props.model.trigger("declineAndBlock");
       /* Prevent event propagation
        * stop the click from reaching parent element */
       return false;
     },
 
-    _toggleDeclineMenu: function() {
-      var currentState = this.state.showDeclineMenu;
-      this.setState({showDeclineMenu: !currentState});
-    },
-
-    _hideDeclineMenu: function() {
-      this.setState({showDeclineMenu: false});
-    },
-
     /*
      * Generate props for <AcceptCallButton> component based on
      * incoming call type. An incoming video call will render a video
      * answer button primarily, an audio call will flip them.
      **/
     _answerModeProps: function() {
       var videoButton = {
         handler: this._handleAccept("audio-video"),
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -7,47 +7,35 @@
 /* jshint newcap:false, esnext:true */
 /* global loop:true, React */
 
 var loop = loop || {};
 loop.conversation = (function(mozL10n) {
   "use strict";
 
   var sharedViews = loop.shared.views;
+  var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
 
   var IncomingCallView = React.createClass({
+    mixins: [sharedMixins.DropdownMenuMixin],
 
     propTypes: {
       model: React.PropTypes.object.isRequired,
       video: React.PropTypes.bool.isRequired
     },
 
     getDefaultProps: function() {
       return {
-        showDeclineMenu: false,
+        showMenu: false,
         video: true
       };
     },
 
-    getInitialState: function() {
-      return {showDeclineMenu: this.props.showDeclineMenu};
-    },
-
-    componentDidMount: function() {
-      window.addEventListener("click", this.clickHandler);
-      window.addEventListener("blur", this._hideDeclineMenu);
-    },
-
-    componentWillUnmount: function() {
-      window.removeEventListener("click", this.clickHandler);
-      window.removeEventListener("blur", this._hideDeclineMenu);
-    },
-
     clickHandler: function(e) {
       var target = e.target;
       if (!target.classList.contains('btn-chevron')) {
         this._hideDeclineMenu();
       }
     },
 
     _handleAccept: function(callType) {
@@ -63,25 +51,16 @@ loop.conversation = (function(mozL10n) {
 
     _handleDeclineBlock: function(e) {
       this.props.model.trigger("declineAndBlock");
       /* Prevent event propagation
        * stop the click from reaching parent element */
       return false;
     },
 
-    _toggleDeclineMenu: function() {
-      var currentState = this.state.showDeclineMenu;
-      this.setState({showDeclineMenu: !currentState});
-    },
-
-    _hideDeclineMenu: function() {
-      this.setState({showDeclineMenu: false});
-    },
-
     /*
      * Generate props for <AcceptCallButton> component based on
      * incoming call type. An incoming video call will render a video
      * answer button primarily, an audio call will flip them.
      **/
     _answerModeProps: function() {
       var videoButton = {
         handler: this._handleAccept("audio-video"),
@@ -108,36 +87,34 @@ loop.conversation = (function(mozL10n) {
       return props;
     },
 
     render: function() {
       /* jshint ignore:start */
       var dropdownMenuClassesDecline = React.addons.classSet({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
-        "visually-hidden": !this.state.showDeclineMenu
+        "visually-hidden": !this.state.showMenu
       });
       return (
         <div className="call-window">
           <h2>{mozL10n.get("incoming_call_title2")}</h2>
           <div className="btn-group call-action-group">
 
             <div className="fx-embedded-call-button-spacer"></div>
 
             <div className="btn-chevron-menu-group">
               <div className="btn-group-chevron">
                 <div className="btn-group">
 
-                  <button className="btn btn-error btn-decline"
+                  <button className="btn btn-decline"
                           onClick={this._handleDecline}>
                     {mozL10n.get("incoming_call_cancel_button")}
                   </button>
-                  <div className="btn-chevron"
-                       onClick={this._toggleDeclineMenu}>
-                  </div>
+                  <div className="btn-chevron" onClick={this.toggleDropdownMenu} />
                 </div>
 
                 <ul className={dropdownMenuClassesDecline}>
                   <li className="btn-block" onClick={this._handleDeclineBlock}>
                     {mozL10n.get("incoming_call_cancel_and_block_button")}
                   </li>
                 </ul>
 
--- a/browser/components/loop/content/shared/css/common.css
+++ b/browser/components/loop/content/shared/css/common.css
@@ -132,33 +132,39 @@ p {
   }
 
 .btn-warning {
   background-color: #f0ad4e;
 }
 
 .btn-cancel,
 .btn-error,
+.btn-decline,
 .btn-hangup,
+.btn-decline + .btn-chevron,
 .btn-error + .btn-chevron {
   background-color: #d74345;
   border: 1px solid #d74345;
 }
 
   .btn-cancel:hover,
   .btn-error:hover,
+  .btn-decline:hover,
   .btn-hangup:hover,
+  .btn-decline + .btn-chevron:hover,
   .btn-error + .btn-chevron:hover {
     background-color: #c53436;
     border: 1px solid #c53436;
   }
 
   .btn-cancel:active,
   .btn-error:active,
+  .btn-decline:active,
   .btn-hangup:active,
+  .btn-decline + .btn-chevron:active,
   .btn-error + .btn-chevron:active {
     background-color: #ae2325;
     border: 1px solid #ae2325;
   }
 
 .btn-chevron {
   width: 26px;
   height: 26px;
@@ -177,16 +183,17 @@ p {
  * and the dropdown menu */
 .btn-chevron-menu-group {
   display: flex;
   justify-content: space-between;
   flex: 8;
 }
 
 .btn-group-chevron .btn {
+  border-radius: 2px;
   border-top-right-radius: 0;
   border-bottom-right-radius: 0;
   flex: 2;
 }
 
   .btn + .btn-chevron,
   .btn + .btn-chevron:hover,
   .btn + .btn-chevron:active {
@@ -364,17 +371,17 @@ p {
 /* Web panel */
 
 .info-panel {
   border-radius: 4px;
   background: #fff;
   padding: 20px 0;
   border: 1px solid #e7e7e7;
   box-shadow: 0 2px 0 rgba(0, 0, 0, .03);
-  margin-bottom: 25px;
+  margin: 2rem 0;
 }
 
 .info-panel h1 {
   font-size: 1.2em;
   font-weight: 700;
   padding: 20px 0;
   text-align: center;
 }
--- a/browser/components/loop/content/shared/js/mixins.js
+++ b/browser/components/loop/content/shared/js/mixins.js
@@ -26,39 +26,49 @@ loop.shared.mixins = (function() {
     rootObject = obj;
   }
 
   /**
    * Dropdown menu mixin.
    * @type {Object}
    */
   var DropdownMenuMixin = {
+    get documentBody() {
+      return rootObject.document.body;
+    },
+
     getInitialState: function() {
       return {showMenu: false};
     },
 
     _onBodyClick: function() {
       this.setState({showMenu: false});
     },
 
     componentDidMount: function() {
-      rootObject.document.body.addEventListener("click", this._onBodyClick);
+      this.documentBody.addEventListener("click", this._onBodyClick);
+      this.documentBody.addEventListener("blur", this.hideDropdownMenu);
     },
 
     componentWillUnmount: function() {
-      rootObject.document.body.removeEventListener("click", this._onBodyClick);
+      this.documentBody.removeEventListener("click", this._onBodyClick);
+      this.documentBody.removeEventListener("blur", this.hideDropdownMenu);
     },
 
     showDropdownMenu: function() {
       this.setState({showMenu: true});
     },
 
     hideDropdownMenu: function() {
       this.setState({showMenu: false});
-    }
+    },
+
+    toggleDropdownMenu: function() {
+      this.setState({showMenu: !this.state.showMenu});
+    },
   };
 
   /**
    * Document visibility mixin. Allows defining the following hooks for when the
    * document visibility status changes:
    *
    * - {Function} onDocumentVisible For when the document becomes visible.
    * - {Function} onDocumentHidden  For when the document becomes hidden.
--- a/browser/components/loop/content/shared/js/models.js
+++ b/browser/components/loop/content/shared/js/models.js
@@ -24,18 +24,18 @@ loop.shared.models = (function(l10n) {
       apiKey:       undefined,     // OT api key
       callId:       undefined,     // The callId on the server
       progressURL:  undefined,     // The websocket url to use for progress
       websocketToken: undefined,   // The token to use for websocket auth, this is
                                    // stored as a hex string which is what the server
                                    // requires.
       callType:     undefined,     // The type of incoming call selected by
                                    // other peer ("audio" or "audio-video")
-      selectedCallType: undefined, // The selected type for the call that was
-                                   // initiated ("audio" or "audio-video")
+      selectedCallType: "audio-video", // The selected type for the call that was
+                                       // initiated ("audio" or "audio-video")
       callToken:    undefined,     // Incoming call token.
                                    // Used for blocking a call url
       subscribedStream: false,     // Used to indicate that a stream has been
                                    // subscribed to
       publishedStream: false       // Used to indicate that a stream has been
                                    // published
     },
 
@@ -81,18 +81,23 @@ loop.shared.models = (function(l10n) {
      */
     accepted: function() {
       this.trigger("call:accepted");
     },
 
     /**
      * Used to indicate that an outgoing call should start any necessary
      * set-up.
+     *
+     * @param {String} selectedCallType Call type ("audio" or "audio-video")
      */
-    setupOutgoingCall: function() {
+    setupOutgoingCall: function(selectedCallType) {
+      if (selectedCallType) {
+        this.set("selectedCallType", selectedCallType);
+      }
       this.trigger("call:outgoing:setup");
     },
 
     /**
      * Starts an outgoing conversation.
      *
      * @param {Object} sessionData The session data received from the
      *                             server for the outgoing call.
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/components/loop/standalone/content/css/webapp.css
@@ -110,18 +110,19 @@ body,
   font-weight: lighter;
 }
 
 .standalone-header-title {
   font-size: 1.8rem;
   line-height: 2.2rem;
 }
 
-.standalone-btn-label {
+p.standalone-btn-label {
   font-size: 1.2rem;
+  line-height: 1.5rem;
 }
 
 .light-color-font {
   opacity: .4;
   font-weight: normal;
 }
 
 .standalone-btn-chevron-menu-group {
--- a/browser/components/loop/standalone/content/js/webapp.js
+++ b/browser/components/loop/standalone/content/js/webapp.js
@@ -9,19 +9,20 @@
 
 var loop = loop || {};
 loop.webapp = (function($, _, OT, mozL10n) {
   "use strict";
 
   loop.config = loop.config || {};
   loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
 
-  var sharedModels = loop.shared.models,
-      sharedViews = loop.shared.views,
-      sharedUtils = loop.shared.utils;
+  var sharedMixins = loop.shared.mixins;
+  var sharedModels = loop.shared.models;
+  var sharedViews = loop.shared.views;
+  var sharedUtils = loop.shared.utils;
 
   /**
    * Homepage view.
    */
   var HomeView = React.createClass({displayName: 'HomeView',
     render: function() {
       return (
         React.DOM.p(null, mozL10n.get("welcome"))
@@ -111,17 +112,18 @@ loop.webapp = (function($, _, OT, mozL10
       );
     }
   });
 
   var ConversationBranding = React.createClass({displayName: 'ConversationBranding',
     render: function() {
       return (
         React.DOM.h1({className: "standalone-header-title"}, 
-          React.DOM.strong(null, mozL10n.get("brandShortname")), " ", mozL10n.get("clientShortname")
+          React.DOM.strong(null, mozL10n.get("brandShortname")), 
+          mozL10n.get("clientShortname")
         )
       );
     }
   });
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
@@ -300,200 +302,193 @@ loop.webapp = (function($, _, OT, mozL10
                       onClick: this._cancelOutgoingCall}, 
                 React.DOM.span({className: "standalone-call-btn-text"}, 
                   mozL10n.get("initiate_call_cancel_button")
                 )
               ), 
               React.DOM.div({className: "flex-padding-1"})
             )
           ), 
+          ConversationFooter(null)
+        )
+      );
+    }
+  });
 
-          ConversationFooter(null)
+  var InitiateCallButton = React.createClass({displayName: 'InitiateCallButton',
+    mixins: [sharedMixins.DropdownMenuMixin],
+
+    propTypes: {
+      caption: React.PropTypes.string.isRequired,
+      startCall: React.PropTypes.func.isRequired,
+      disabled: React.PropTypes.bool
+    },
+
+    getDefaultProps: function() {
+      return {disabled: false};
+    },
+
+    render: function() {
+      var dropdownMenuClasses = React.addons.classSet({
+        "native-dropdown-large-parent": true,
+        "standalone-dropdown-menu": true,
+        "visually-hidden": !this.state.showMenu
+      });
+      var chevronClasses = React.addons.classSet({
+        "btn-chevron": true,
+        "disabled": this.props.disabled
+      });
+      return (
+        React.DOM.div({className: "standalone-btn-chevron-menu-group"}, 
+          React.DOM.div({className: "btn-group-chevron"}, 
+            React.DOM.div({className: "btn-group"}, 
+              React.DOM.button({className: "btn btn-large btn-accept", 
+                      onClick: this.props.startCall("audio-video"), 
+                      disabled: this.props.disabled, 
+                      title: mozL10n.get("initiate_audio_video_call_tooltip2")}, 
+                React.DOM.span({className: "standalone-call-btn-text"}, 
+                  this.props.caption
+                ), 
+                React.DOM.span({className: "standalone-call-btn-video-icon"})
+              ), 
+              React.DOM.div({className: chevronClasses, 
+                   onClick: this.toggleDropdownMenu}
+              )
+            ), 
+            React.DOM.ul({className: dropdownMenuClasses}, 
+              React.DOM.li(null, 
+                React.DOM.button({className: "start-audio-only-call", 
+                        onClick: this.props.startCall("audio"), 
+                        disabled: this.props.disabled}, 
+                  mozL10n.get("initiate_audio_call_button2")
+                )
+              )
+            )
+          )
         )
       );
     }
   });
 
   /**
-   * Conversation launcher view. A ConversationModel is associated and attached
-   * as a `model` property.
-   *
-   * Required properties:
-   * - {loop.shared.models.ConversationModel}    model    Conversation model.
-   * - {loop.shared.models.NotificationCollection} notifications
+   * Initiate conversation view.
    */
-  var StartConversationView = React.createClass({displayName: 'StartConversationView',
+  var InitiateConversationView = React.createClass({displayName: 'InitiateConversationView',
+    mixins: [Backbone.Events],
+
     propTypes: {
-      model: React.PropTypes.oneOfType([
-               React.PropTypes.instanceOf(sharedModels.ConversationModel),
-               React.PropTypes.instanceOf(FxOSConversationModel)
-             ]).isRequired,
+      conversation: React.PropTypes.oneOfType([
+                      React.PropTypes.instanceOf(sharedModels.ConversationModel),
+                      React.PropTypes.instanceOf(FxOSConversationModel)
+                    ]).isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifications: React.PropTypes.object.isRequired,
-      client: React.PropTypes.object.isRequired
-    },
-
-    getDefaultProps: function() {
-      return {showCallOptionsMenu: false};
+      client: React.PropTypes.object.isRequired,
+      title: React.PropTypes.string.isRequired,
+      callButtonLabel: React.PropTypes.string.isRequired
     },
 
     getInitialState: function() {
       return {
         urlCreationDateString: '',
-        disableCallButton: false,
-        showCallOptionsMenu: this.props.showCallOptionsMenu
+        disableCallButton: false
       };
     },
 
     componentDidMount: function() {
-      // Listen for events & hide dropdown menu if user clicks away
-      window.addEventListener("click", this.clickHandler);
-      this.props.model.listenTo(this.props.model, "session:error",
-                                this._onSessionError);
-      this.props.model.listenTo(this.props.model, "fxos:app-needed",
-                                this._onFxOSAppNeeded);
-      this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
-                                           this._setConversationTimestamp);
+      this.listenTo(this.props.conversation,
+                    "session:error", this._onSessionError);
+      this.listenTo(this.props.conversation,
+                    "fxos:app-needed", this._onFxOSAppNeeded);
+      this.props.client.requestCallUrlInfo(
+        this.props.conversation.get("loopToken"),
+        this._setConversationTimestamp);
+    },
+
+    componentWillUnmount: function() {
+      this.stopListening(this.props.conversation);
+      localStorage.setItem("has-seen-tos", "true");
     },
 
     _onSessionError: function(error, l10nProps) {
       var errorL10n = error || "unable_retrieve_call_info";
       this.props.notifications.errorL10n(errorL10n, l10nProps);
       console.error(errorL10n);
     },
 
     _onFxOSAppNeeded: function() {
       this.setState({
-        marketplaceSrc: loop.config.marketplaceUrl
-      });
-      this.setState({
-        onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
-          this.props.model
+        marketplaceSrc: loop.config.marketplaceUrl,
+        onMarketplaceMessage: this.props.conversation.onMarketplaceMessage.bind(
+          this.props.conversation
         )
       });
      },
 
     /**
      * Initiates the call.
      * Takes in a call type parameter "audio" or "audio-video" and returns
      * a function that initiates the call. React click handler requires a function
      * to be called when that event happenes.
      *
      * @param {string} User call type choice "audio" or "audio-video"
      */
-    _initiateOutgoingCall: function(callType) {
+    startCall: function(callType) {
       return function() {
-        this.props.model.set("selectedCallType", callType);
+        this.props.conversation.setupOutgoingCall(callType);
         this.setState({disableCallButton: true});
-        this.props.model.setupOutgoingCall();
       }.bind(this);
     },
 
     _setConversationTimestamp: function(err, callUrlInfo) {
       if (err) {
         this.props.notifications.errorL10n("unable_retrieve_call_info");
       } else {
         var date = (new Date(callUrlInfo.urlCreationDate * 1000));
         var options = {year: "numeric", month: "long", day: "numeric"};
         var timestamp = date.toLocaleDateString(navigator.language, options);
         this.setState({urlCreationDateString: timestamp});
       }
     },
 
-    componentWillUnmount: function() {
-      window.removeEventListener("click", this.clickHandler);
-      localStorage.setItem("has-seen-tos", "true");
-    },
-
-    clickHandler: function(e) {
-      if (!e.target.classList.contains('btn-chevron') &&
-          this.state.showCallOptionsMenu) {
-            this._toggleCallOptionsMenu();
-      }
-    },
-
-    _toggleCallOptionsMenu: function() {
-      var state = this.state.showCallOptionsMenu;
-      this.setState({showCallOptionsMenu: !state});
-    },
-
     render: function() {
-      var tos_link_name = mozL10n.get("terms_of_use_link_text");
-      var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
+      var tosLinkName = mozL10n.get("terms_of_use_link_text");
+      var privacyNoticeName = mozL10n.get("privacy_notice_link_text");
 
       var tosHTML = mozL10n.get("legal_text_and_links", {
         "terms_of_use_url": "<a target=_blank href='/legal/terms/'>" +
-          tos_link_name + "</a>",
+          tosLinkName + "</a>",
         "privacy_notice_url": "<a target=_blank href='" +
-          "https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
+          "https://www.mozilla.org/privacy/'>" + privacyNoticeName + "</a>"
       });
 
-      var dropdownMenuClasses = React.addons.classSet({
-        "native-dropdown-large-parent": true,
-        "standalone-dropdown-menu": true,
-        "visually-hidden": !this.state.showCallOptionsMenu
-      });
       var tosClasses = React.addons.classSet({
         "terms-service": true,
         hide: (localStorage.getItem("has-seen-tos") === "true")
       });
-      var chevronClasses = React.addons.classSet({
-        "btn-chevron": true,
-        "disabled": this.state.disableCallButton
-      });
 
       return (
         React.DOM.div({className: "container"}, 
           React.DOM.div({className: "container-box"}, 
 
             ConversationHeader({
               urlCreationDateString: this.state.urlCreationDateString}), 
 
             React.DOM.p({className: "standalone-btn-label"}, 
-              mozL10n.get("initiate_call_button_label2")
+              this.props.title
             ), 
 
             React.DOM.div({id: "messages"}), 
 
             React.DOM.div({className: "btn-group"}, 
               React.DOM.div({className: "flex-padding-1"}), 
-              React.DOM.div({className: "standalone-btn-chevron-menu-group"}, 
-                React.DOM.div({className: "btn-group-chevron"}, 
-                  React.DOM.div({className: "btn-group"}, 
-
-                    React.DOM.button({className: "btn btn-large btn-accept", 
-                            onClick: this._initiateOutgoingCall("audio-video"), 
-                            disabled: this.state.disableCallButton, 
-                            title: mozL10n.get("initiate_audio_video_call_tooltip2")}, 
-                      React.DOM.span({className: "standalone-call-btn-text"}, 
-                        mozL10n.get("initiate_audio_video_call_button2")
-                      ), 
-                      React.DOM.span({className: "standalone-call-btn-video-icon"})
-                    ), 
-
-                    React.DOM.div({className: chevronClasses, 
-                         onClick: this._toggleCallOptionsMenu}
-                    )
-
-                  ), 
-
-                  React.DOM.ul({className: dropdownMenuClasses}, 
-                    React.DOM.li(null, 
-                      /*
-                       Button required for disabled state.
-                       */
-                      React.DOM.button({className: "start-audio-only-call", 
-                              onClick: this._initiateOutgoingCall("audio"), 
-                              disabled: this.state.disableCallButton}, 
-                        mozL10n.get("initiate_audio_call_button2")
-                      )
-                    )
-                  )
-
-                )
+              InitiateCallButton({
+                caption: this.props.callButtonLabel, 
+                disabled: this.state.disableCallButton, 
+                startCall: this.startCall}
               ), 
               React.DOM.div({className: "flex-padding-1"})
             ), 
 
             React.DOM.p({className: tosClasses, 
                dangerouslySetInnerHTML: {__html: tosHTML}})
           ), 
 
@@ -533,16 +528,36 @@ loop.webapp = (function($, _, OT, mozL10
             audio: {enabled: false, visible: false}, 
             video: {enabled: false, visible: false}}
           )
         )
       );
     }
   });
 
+  var StartConversationView = React.createClass({displayName: 'StartConversationView',
+    render: function() {
+      return this.transferPropsTo(
+        InitiateConversationView({
+          title: mozL10n.get("initiate_call_button_label2"), 
+          callButtonLabel: mozL10n.get("initiate_audio_video_call_button2")})
+      );
+    }
+  });
+
+  var FailedConversationView = React.createClass({displayName: 'FailedConversationView',
+    render: function() {
+      return this.transferPropsTo(
+        InitiateConversationView({
+          title: mozL10n.get("call_failed_title"), 
+          callButtonLabel: mozL10n.get("retry_call_button")})
+      );
+    }
+  });
+
   /**
    * This view manages the outgoing conversation views - from
    * call initiation through to the actual conversation and call end.
    *
    * At the moment, it does more than that, these parts need refactoring out.
    */
   var OutgoingConversationView = React.createClass({displayName: 'OutgoingConversationView',
     propTypes: {
@@ -590,21 +605,29 @@ loop.webapp = (function($, _, OT, mozL10
       }.bind(this);
     },
 
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
-        case "failure":
         case "start": {
           return (
             StartConversationView({
-              model: this.props.conversation, 
+              conversation: this.props.conversation, 
+              notifications: this.props.notifications, 
+              client: this.props.client}
+            )
+          );
+        }
+        case "failure": {
+          return (
+            FailedConversationView({
+              conversation: this.props.conversation, 
               notifications: this.props.notifications, 
               client: this.props.client}
             )
           );
         }
         case "pending": {
           return PendingConversationView({websocket: this._websocket});
         }
@@ -770,28 +793,27 @@ loop.webapp = (function($, _, OT, mozL10
           break;
         }
       }
     },
 
     /**
      * Handles call rejection.
      *
-     * @param {String} reason The reason the call was terminated.
+     * @param {String} reason The reason the call was terminated (reject, busy,
+     *                        timeout, cancel, media-fail, user-unknown, closed)
      */
     _handleCallTerminated: function(reason) {
-      if (reason !== "cancel") {
-        // XXX This should really display the call failed view - bug 1046959
-        // will implement this.
-        this.props.notifications.errorL10n("call_timeout_notification_text");
+      if (reason === "cancel") {
+        this.setState({callStatus: "start"});
+        return;
       }
-      // redirects the user to the call start view
-      // XXX should switch callStatus to failed for specific reasons when we
-      // get the call failed view; for now, switch back to start.
-      this.setState({callStatus: "start"});
+      // XXX later, we'll want to display more meaningfull messages (needs UX)
+      this.props.notifications.errorL10n("call_timeout_notification_text");
+      this.setState({callStatus: "failure"});
     },
 
     /**
      * Handles ending a call by resetting the view to the start state.
      */
     _endCall: function() {
       this.setState({callStatus: "end"});
     },
@@ -888,16 +910,17 @@ loop.webapp = (function($, _, OT, mozL10
     document.documentElement.lang = mozL10n.language.code;
     document.documentElement.dir = mozL10n.language.direction;
   }
 
   return {
     CallUrlExpiredView: CallUrlExpiredView,
     PendingConversationView: PendingConversationView,
     StartConversationView: StartConversationView,
+    FailedConversationView: FailedConversationView,
     OutgoingConversationView: OutgoingConversationView,
     EndedConversationView: EndedConversationView,
     HomeView: HomeView,
     UnsupportedBrowserView: UnsupportedBrowserView,
     UnsupportedDeviceView: UnsupportedDeviceView,
     init: init,
     PromoteFirefoxView: PromoteFirefoxView,
     WebappRootView: WebappRootView,
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -9,19 +9,20 @@
 
 var loop = loop || {};
 loop.webapp = (function($, _, OT, mozL10n) {
   "use strict";
 
   loop.config = loop.config || {};
   loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
 
-  var sharedModels = loop.shared.models,
-      sharedViews = loop.shared.views,
-      sharedUtils = loop.shared.utils;
+  var sharedMixins = loop.shared.mixins;
+  var sharedModels = loop.shared.models;
+  var sharedViews = loop.shared.views;
+  var sharedUtils = loop.shared.utils;
 
   /**
    * Homepage view.
    */
   var HomeView = React.createClass({
     render: function() {
       return (
         <p>{mozL10n.get("welcome")}</p>
@@ -111,17 +112,18 @@ loop.webapp = (function($, _, OT, mozL10
       );
     }
   });
 
   var ConversationBranding = React.createClass({
     render: function() {
       return (
         <h1 className="standalone-header-title">
-          <strong>{mozL10n.get("brandShortname")}</strong> {mozL10n.get("clientShortname")}
+          <strong>{mozL10n.get("brandShortname")}</strong>
+          {mozL10n.get("clientShortname")}
         </h1>
       );
     }
   });
 
   /**
    * The Firefox Marketplace exposes a web page that contains a postMesssage
    * based API that wraps a small set of functionality from the WebApps API
@@ -229,17 +231,17 @@ loop.webapp = (function($, _, OT, mozL10
 
       return (
         <header className="standalone-header header-box container-box">
           <ConversationBranding />
           <div className="loop-logo" title="Firefox WebRTC! logo"></div>
           <h3 className="call-url">
             {conversationUrl}
           </h3>
-          <h4 className={urlCreationDateClasses} >
+          <h4 className={urlCreationDateClasses}>
             {callUrlCreationDateString}
           </h4>
         </header>
       );
     }
   });
 
   var ConversationFooter = React.createClass({
@@ -281,221 +283,214 @@ loop.webapp = (function($, _, OT, mozL10
       var callState = mozL10n.get("call_progress_" + this.state.callState + "_description");
       return (
         <div className="container">
           <div className="container-box">
             <header className="pending-header header-box">
               <ConversationBranding />
             </header>
 
-            <div id="cameraPreview"></div>
+            <div id="cameraPreview" />
 
-            <div id="messages"></div>
+            <div id="messages" />
 
             <p className="standalone-btn-label">
               {callState}
             </p>
 
             <div className="btn-pending-cancel-group btn-group">
-              <div className="flex-padding-1"></div>
+              <div className="flex-padding-1" />
               <button className="btn btn-large btn-cancel"
                       onClick={this._cancelOutgoingCall} >
                 <span className="standalone-call-btn-text">
                   {mozL10n.get("initiate_call_cancel_button")}
                 </span>
               </button>
-              <div className="flex-padding-1"></div>
+              <div className="flex-padding-1" />
             </div>
           </div>
+          <ConversationFooter />
+        </div>
+      );
+    }
+  });
 
-          <ConversationFooter />
+  var InitiateCallButton = React.createClass({
+    mixins: [sharedMixins.DropdownMenuMixin],
+
+    propTypes: {
+      caption: React.PropTypes.string.isRequired,
+      startCall: React.PropTypes.func.isRequired,
+      disabled: React.PropTypes.bool
+    },
+
+    getDefaultProps: function() {
+      return {disabled: false};
+    },
+
+    render: function() {
+      var dropdownMenuClasses = React.addons.classSet({
+        "native-dropdown-large-parent": true,
+        "standalone-dropdown-menu": true,
+        "visually-hidden": !this.state.showMenu
+      });
+      var chevronClasses = React.addons.classSet({
+        "btn-chevron": true,
+        "disabled": this.props.disabled
+      });
+      return (
+        <div className="standalone-btn-chevron-menu-group">
+          <div className="btn-group-chevron">
+            <div className="btn-group">
+              <button className="btn btn-large btn-accept"
+                      onClick={this.props.startCall("audio-video")}
+                      disabled={this.props.disabled}
+                      title={mozL10n.get("initiate_audio_video_call_tooltip2")}>
+                <span className="standalone-call-btn-text">
+                  {this.props.caption}
+                </span>
+                <span className="standalone-call-btn-video-icon" />
+              </button>
+              <div className={chevronClasses}
+                   onClick={this.toggleDropdownMenu}>
+              </div>
+            </div>
+            <ul className={dropdownMenuClasses}>
+              <li>
+                <button className="start-audio-only-call"
+                        onClick={this.props.startCall("audio")}
+                        disabled={this.props.disabled}>
+                  {mozL10n.get("initiate_audio_call_button2")}
+                </button>
+              </li>
+            </ul>
+          </div>
         </div>
       );
     }
   });
 
   /**
-   * Conversation launcher view. A ConversationModel is associated and attached
-   * as a `model` property.
-   *
-   * Required properties:
-   * - {loop.shared.models.ConversationModel}    model    Conversation model.
-   * - {loop.shared.models.NotificationCollection} notifications
+   * Initiate conversation view.
    */
-  var StartConversationView = React.createClass({
+  var InitiateConversationView = React.createClass({
+    mixins: [Backbone.Events],
+
     propTypes: {
-      model: React.PropTypes.oneOfType([
-               React.PropTypes.instanceOf(sharedModels.ConversationModel),
-               React.PropTypes.instanceOf(FxOSConversationModel)
-             ]).isRequired,
+      conversation: React.PropTypes.oneOfType([
+                      React.PropTypes.instanceOf(sharedModels.ConversationModel),
+                      React.PropTypes.instanceOf(FxOSConversationModel)
+                    ]).isRequired,
       // XXX Check more tightly here when we start injecting window.loop.*
       notifications: React.PropTypes.object.isRequired,
-      client: React.PropTypes.object.isRequired
-    },
-
-    getDefaultProps: function() {
-      return {showCallOptionsMenu: false};
+      client: React.PropTypes.object.isRequired,
+      title: React.PropTypes.string.isRequired,
+      callButtonLabel: React.PropTypes.string.isRequired
     },
 
     getInitialState: function() {
       return {
         urlCreationDateString: '',
-        disableCallButton: false,
-        showCallOptionsMenu: this.props.showCallOptionsMenu
+        disableCallButton: false
       };
     },
 
     componentDidMount: function() {
-      // Listen for events & hide dropdown menu if user clicks away
-      window.addEventListener("click", this.clickHandler);
-      this.props.model.listenTo(this.props.model, "session:error",
-                                this._onSessionError);
-      this.props.model.listenTo(this.props.model, "fxos:app-needed",
-                                this._onFxOSAppNeeded);
-      this.props.client.requestCallUrlInfo(this.props.model.get("loopToken"),
-                                           this._setConversationTimestamp);
+      this.listenTo(this.props.conversation,
+                    "session:error", this._onSessionError);
+      this.listenTo(this.props.conversation,
+                    "fxos:app-needed", this._onFxOSAppNeeded);
+      this.props.client.requestCallUrlInfo(
+        this.props.conversation.get("loopToken"),
+        this._setConversationTimestamp);
+    },
+
+    componentWillUnmount: function() {
+      this.stopListening(this.props.conversation);
+      localStorage.setItem("has-seen-tos", "true");
     },
 
     _onSessionError: function(error, l10nProps) {
       var errorL10n = error || "unable_retrieve_call_info";
       this.props.notifications.errorL10n(errorL10n, l10nProps);
       console.error(errorL10n);
     },
 
     _onFxOSAppNeeded: function() {
       this.setState({
-        marketplaceSrc: loop.config.marketplaceUrl
-      });
-      this.setState({
-        onMarketplaceMessage: this.props.model.onMarketplaceMessage.bind(
-          this.props.model
+        marketplaceSrc: loop.config.marketplaceUrl,
+        onMarketplaceMessage: this.props.conversation.onMarketplaceMessage.bind(
+          this.props.conversation
         )
       });
      },
 
     /**
      * Initiates the call.
      * Takes in a call type parameter "audio" or "audio-video" and returns
      * a function that initiates the call. React click handler requires a function
      * to be called when that event happenes.
      *
      * @param {string} User call type choice "audio" or "audio-video"
      */
-    _initiateOutgoingCall: function(callType) {
+    startCall: function(callType) {
       return function() {
-        this.props.model.set("selectedCallType", callType);
+        this.props.conversation.setupOutgoingCall(callType);
         this.setState({disableCallButton: true});
-        this.props.model.setupOutgoingCall();
       }.bind(this);
     },
 
     _setConversationTimestamp: function(err, callUrlInfo) {
       if (err) {
         this.props.notifications.errorL10n("unable_retrieve_call_info");
       } else {
         var date = (new Date(callUrlInfo.urlCreationDate * 1000));
         var options = {year: "numeric", month: "long", day: "numeric"};
         var timestamp = date.toLocaleDateString(navigator.language, options);
         this.setState({urlCreationDateString: timestamp});
       }
     },
 
-    componentWillUnmount: function() {
-      window.removeEventListener("click", this.clickHandler);
-      localStorage.setItem("has-seen-tos", "true");
-    },
-
-    clickHandler: function(e) {
-      if (!e.target.classList.contains('btn-chevron') &&
-          this.state.showCallOptionsMenu) {
-            this._toggleCallOptionsMenu();
-      }
-    },
-
-    _toggleCallOptionsMenu: function() {
-      var state = this.state.showCallOptionsMenu;
-      this.setState({showCallOptionsMenu: !state});
-    },
-
     render: function() {
-      var tos_link_name = mozL10n.get("terms_of_use_link_text");
-      var privacy_notice_name = mozL10n.get("privacy_notice_link_text");
+      var tosLinkName = mozL10n.get("terms_of_use_link_text");
+      var privacyNoticeName = mozL10n.get("privacy_notice_link_text");
 
       var tosHTML = mozL10n.get("legal_text_and_links", {
         "terms_of_use_url": "<a target=_blank href='/legal/terms/'>" +
-          tos_link_name + "</a>",
+          tosLinkName + "</a>",
         "privacy_notice_url": "<a target=_blank href='" +
-          "https://www.mozilla.org/privacy/'>" + privacy_notice_name + "</a>"
+          "https://www.mozilla.org/privacy/'>" + privacyNoticeName + "</a>"
       });
 
-      var dropdownMenuClasses = React.addons.classSet({
-        "native-dropdown-large-parent": true,
-        "standalone-dropdown-menu": true,
-        "visually-hidden": !this.state.showCallOptionsMenu
-      });
       var tosClasses = React.addons.classSet({
         "terms-service": true,
         hide: (localStorage.getItem("has-seen-tos") === "true")
       });
-      var chevronClasses = React.addons.classSet({
-        "btn-chevron": true,
-        "disabled": this.state.disableCallButton
-      });
 
       return (
         <div className="container">
           <div className="container-box">
 
             <ConversationHeader
               urlCreationDateString={this.state.urlCreationDateString} />
 
             <p className="standalone-btn-label">
-              {mozL10n.get("initiate_call_button_label2")}
+              {this.props.title}
             </p>
 
             <div id="messages"></div>
 
             <div className="btn-group">
-              <div className="flex-padding-1"></div>
-              <div className="standalone-btn-chevron-menu-group">
-                <div className="btn-group-chevron">
-                  <div className="btn-group">
-
-                    <button className="btn btn-large btn-accept"
-                            onClick={this._initiateOutgoingCall("audio-video")}
-                            disabled={this.state.disableCallButton}
-                            title={mozL10n.get("initiate_audio_video_call_tooltip2")} >
-                      <span className="standalone-call-btn-text">
-                        {mozL10n.get("initiate_audio_video_call_button2")}
-                      </span>
-                      <span className="standalone-call-btn-video-icon"></span>
-                    </button>
-
-                    <div className={chevronClasses}
-                         onClick={this._toggleCallOptionsMenu}>
-                    </div>
-
-                  </div>
-
-                  <ul className={dropdownMenuClasses}>
-                    <li>
-                      {/*
-                       Button required for disabled state.
-                       */}
-                      <button className="start-audio-only-call"
-                              onClick={this._initiateOutgoingCall("audio")}
-                              disabled={this.state.disableCallButton} >
-                        {mozL10n.get("initiate_audio_call_button2")}
-                      </button>
-                    </li>
-                  </ul>
-
-                </div>
-              </div>
-              <div className="flex-padding-1"></div>
+              <div className="flex-padding-1" />
+              <InitiateCallButton
+                caption={this.props.callButtonLabel}
+                disabled={this.state.disableCallButton}
+                startCall={this.startCall}
+              />
+              <div className="flex-padding-1" />
             </div>
 
             <p className={tosClasses}
                dangerouslySetInnerHTML={{__html: tosHTML}}></p>
           </div>
 
           <FxOSHiddenMarketplace
             marketplaceSrc={this.state.marketplaceSrc}
@@ -533,16 +528,36 @@ loop.webapp = (function($, _, OT, mozL10
             audio={{enabled: false, visible: false}}
             video={{enabled: false, visible: false}}
           />
         </div>
       );
     }
   });
 
+  var StartConversationView = React.createClass({
+    render: function() {
+      return this.transferPropsTo(
+        <InitiateConversationView
+          title={mozL10n.get("initiate_call_button_label2")}
+          callButtonLabel={mozL10n.get("initiate_audio_video_call_button2")} />
+      );
+    }
+  });
+
+  var FailedConversationView = React.createClass({
+    render: function() {
+      return this.transferPropsTo(
+        <InitiateConversationView
+          title={mozL10n.get("call_failed_title")}
+          callButtonLabel={mozL10n.get("retry_call_button")} />
+      );
+    }
+  });
+
   /**
    * This view manages the outgoing conversation views - from
    * call initiation through to the actual conversation and call end.
    *
    * At the moment, it does more than that, these parts need refactoring out.
    */
   var OutgoingConversationView = React.createClass({
     propTypes: {
@@ -590,21 +605,29 @@ loop.webapp = (function($, _, OT, mozL10
       }.bind(this);
     },
 
     /**
      * Renders the conversation views.
      */
     render: function() {
       switch (this.state.callStatus) {
-        case "failure":
         case "start": {
           return (
             <StartConversationView
-              model={this.props.conversation}
+              conversation={this.props.conversation}
+              notifications={this.props.notifications}
+              client={this.props.client}
+            />
+          );
+        }
+        case "failure": {
+          return (
+            <FailedConversationView
+              conversation={this.props.conversation}
               notifications={this.props.notifications}
               client={this.props.client}
             />
           );
         }
         case "pending": {
           return <PendingConversationView websocket={this._websocket} />;
         }
@@ -770,28 +793,27 @@ loop.webapp = (function($, _, OT, mozL10
           break;
         }
       }
     },
 
     /**
      * Handles call rejection.
      *
-     * @param {String} reason The reason the call was terminated.
+     * @param {String} reason The reason the call was terminated (reject, busy,
+     *                        timeout, cancel, media-fail, user-unknown, closed)
      */
     _handleCallTerminated: function(reason) {
-      if (reason !== "cancel") {
-        // XXX This should really display the call failed view - bug 1046959
-        // will implement this.
-        this.props.notifications.errorL10n("call_timeout_notification_text");
+      if (reason === "cancel") {
+        this.setState({callStatus: "start"});
+        return;
       }
-      // redirects the user to the call start view
-      // XXX should switch callStatus to failed for specific reasons when we
-      // get the call failed view; for now, switch back to start.
-      this.setState({callStatus: "start"});
+      // XXX later, we'll want to display more meaningfull messages (needs UX)
+      this.props.notifications.errorL10n("call_timeout_notification_text");
+      this.setState({callStatus: "failure"});
     },
 
     /**
      * Handles ending a call by resetting the view to the start state.
      */
     _endCall: function() {
       this.setState({callStatus: "end"});
     },
@@ -888,16 +910,17 @@ loop.webapp = (function($, _, OT, mozL10
     document.documentElement.lang = mozL10n.language.code;
     document.documentElement.dir = mozL10n.language.direction;
   }
 
   return {
     CallUrlExpiredView: CallUrlExpiredView,
     PendingConversationView: PendingConversationView,
     StartConversationView: StartConversationView,
+    FailedConversationView: FailedConversationView,
     OutgoingConversationView: OutgoingConversationView,
     EndedConversationView: EndedConversationView,
     HomeView: HomeView,
     UnsupportedBrowserView: UnsupportedBrowserView,
     UnsupportedDeviceView: UnsupportedDeviceView,
     init: init,
     PromoteFirefoxView: PromoteFirefoxView,
     WebappRootView: WebappRootView,
--- a/browser/components/loop/standalone/content/l10n/loop.en-US.properties
+++ b/browser/components/loop/standalone/content/l10n/loop.en-US.properties
@@ -1,15 +1,16 @@
 ## LOCALIZATION NOTE: In this file, don't translate the part between {{..}}
 restart_call=Rejoin
 conversation_has_ended=Your conversation has ended.
 call_timeout_notification_text=Your call did not go through.
 missing_conversation_info=Missing conversation information.
 network_disconnected=The network connection terminated abruptly.
 peer_ended_conversation2=The person you were calling has ended the conversation.
+call_failed_title=Call failed.
 connection_error_see_console_notification=Call failed; see console for details.
 generic_failure_title=Something went wrong.
 generic_failure_with_reason2=You can try again or email a link to be reached at later.
 generic_failure_no_reason2=Would you like to try again?
 retry_call_button=Retry
 feedback_report_user_button=Report User
 unable_retrieve_call_info=Unable to retrieve conversation information.
 hangup_button_title=Hang up
--- a/browser/components/loop/test/shared/models_test.js
+++ b/browser/components/loop/test/shared/models_test.js
@@ -71,16 +71,29 @@ describe("loop.shared.models", function(
             done();
           });
 
           conversation.accepted();
         });
       });
 
       describe("#setupOutgoingCall", function() {
+        it("should set the a custom selected call type", function() {
+          conversation.setupOutgoingCall("audio");
+
+          expect(conversation.get("selectedCallType")).eql("audio");
+        });
+
+        it("should respect the default selected call type when none is passed",
+          function() {
+            conversation.setupOutgoingCall();
+
+            expect(conversation.get("selectedCallType")).eql("audio-video");
+          });
+
         it("should trigger a `call:outgoing:setup` event", function(done) {
           conversation.once("call:outgoing:setup", function() {
             done();
           });
 
           conversation.setupOutgoingCall();
         });
       });
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -191,24 +191,24 @@ describe("loop.webapp", function() {
         });
 
         describe("Progress", function() {
           describe("state: terminate, reason: reject", function() {
             beforeEach(function() {
               sandbox.stub(notifications, "errorL10n");
             });
 
-            it("should display the StartConversationView", function() {
+            it("should display the FailedConversationView", function() {
               ocView._websocket.trigger("progress", {
                 state: "terminated",
                 reason: "reject"
               });
 
               TestUtils.findRenderedComponentWithType(ocView,
-                loop.webapp.StartConversationView);
+                loop.webapp.FailedConversationView);
             });
 
             it("should display an error message if the reason is not 'cancel'",
               function() {
                 ocView._websocket.trigger("progress", {
                   state: "terminated",
                   reason: "reject"
                 });
@@ -266,24 +266,24 @@ describe("loop.webapp", function() {
         sandbox.stub(notifications, "errorL10n");
         sandbox.stub(notifications, "warnL10n");
         promiseConnectStub =
           sandbox.stub(loop.CallConnectionWebSocket.prototype, "promiseConnect");
         promiseConnectStub.returns(new Promise(function(resolve, reject) {}));
       });
 
       describe("call:outgoing", function() {
-        it("should set display the StartConversationView if session token is missing",
+        it("should display FailedConversationView if session token is missing",
           function() {
             conversation.set("loopToken", "");
 
             ocView.startCall();
 
             TestUtils.findRenderedComponentWithType(ocView,
-              loop.webapp.StartConversationView);
+              loop.webapp.FailedConversationView);
           });
 
         it("should notify the user if session token is missing", function() {
           conversation.set("loopToken", "");
 
           ocView.startCall();
 
           sinon.assert.calledOnce(notifications.errorL10n);
@@ -395,39 +395,38 @@ describe("loop.webapp", function() {
       });
 
       describe("#setupOutgoingCall", function() {
         describe("No loop token", function() {
           beforeEach(function() {
             conversation.set("loopToken", "");
           });
 
-          it("should set display the StartConversationView", function() {
+          it("should display the FailedConversationView", function() {
             conversation.setupOutgoingCall();
 
             TestUtils.findRenderedComponentWithType(ocView,
-              loop.webapp.StartConversationView);
+              loop.webapp.FailedConversationView);
           });
 
           it("should display an error", function() {
             conversation.setupOutgoingCall();
 
             sinon.assert.calledOnce(notifications.errorL10n);
           });
         });
 
         describe("Has loop token", function() {
           beforeEach(function() {
-            conversation.set("selectedCallType", "audio-video");
             sandbox.stub(conversation, "outgoing");
           });
 
           it("should call requestCallInfo on the client",
             function() {
-              conversation.setupOutgoingCall();
+              conversation.setupOutgoingCall("audio-video");
 
               sinon.assert.calledOnce(client.requestCallInfo);
               sinon.assert.calledWith(client.requestCallInfo, "fakeToken",
                                       "audio-video");
             });
 
           describe("requestCallInfo response handling", function() {
             it("should set display the CallUrlExpiredView if the call has expired",
@@ -435,24 +434,24 @@ describe("loop.webapp", function() {
                 client.requestCallInfo.callsArgWith(2, {errno: 105});
 
                 conversation.setupOutgoingCall();
 
                 TestUtils.findRenderedComponentWithType(ocView,
                   loop.webapp.CallUrlExpiredView);
               });
 
-            it("should set display the StartConversationView on any other error",
+            it("should set display the FailedConversationView on any other error",
                function() {
                 client.requestCallInfo.callsArgWith(2, {errno: 104});
 
                 conversation.setupOutgoingCall();
 
                 TestUtils.findRenderedComponentWithType(ocView,
-                  loop.webapp.StartConversationView);
+                  loop.webapp.FailedConversationView);
               });
 
             it("should notify the user on any other error", function() {
               client.requestCallInfo.callsArgWith(2, {errno: 104});
 
               conversation.setupOutgoingCall();
 
               sinon.assert.calledOnce(notifications.errorL10n);
@@ -580,59 +579,61 @@ describe("loop.webapp", function() {
           expect(view.state.callState).to.be.equal("ringing");
         });
       });
     });
   });
 
   describe("StartConversationView", function() {
     describe("#initiate", function() {
-      var conversation, setupOutgoingCall, view, fakeSubmitEvent,
-          requestCallUrlInfo;
+      var conversation, view, fakeSubmitEvent, requestCallUrlInfo;
 
       beforeEach(function() {
         conversation = new sharedModels.ConversationModel({}, {
           sdk: {}
         });
 
         fakeSubmitEvent = {preventDefault: sinon.spy()};
-        setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
 
         var standaloneClientStub = {
           requestCallUrlInfo: function(token, cb) {
             cb(null, {urlCreationDate: 0});
           },
           settings: {baseServerUrl: loop.webapp.baseServerUrl}
         };
 
         view = React.addons.TestUtils.renderIntoDocument(
             loop.webapp.StartConversationView({
-              model: conversation,
+              conversation: conversation,
               notifications: notifications,
               client: standaloneClientStub
             })
         );
       });
 
       it("should start the audio-video conversation establishment process",
         function() {
+          var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
+
           var button = view.getDOMNode().querySelector(".btn-accept");
           React.addons.TestUtils.Simulate.click(button);
 
           sinon.assert.calledOnce(setupOutgoingCall);
-          sinon.assert.calledWithExactly(setupOutgoingCall);
+          sinon.assert.calledWithExactly(setupOutgoingCall, "audio-video");
       });
 
       it("should start the audio-only conversation establishment process",
         function() {
+          var setupOutgoingCall = sinon.stub(conversation, "setupOutgoingCall");
+
           var button = view.getDOMNode().querySelector(".start-audio-only-call");
           React.addons.TestUtils.Simulate.click(button);
 
           sinon.assert.calledOnce(setupOutgoingCall);
-          sinon.assert.calledWithExactly(setupOutgoingCall);
+          sinon.assert.calledWithExactly(setupOutgoingCall, "audio");
         });
 
       it("should disable audio-video button once session is initiated",
          function() {
            conversation.set("loopToken", "fake");
 
            var button = view.getDOMNode().querySelector(".btn-accept");
            React.addons.TestUtils.Simulate.click(button);
@@ -645,45 +646,45 @@ describe("loop.webapp", function() {
            conversation.set("loopToken", "fake");
 
            var button = view.getDOMNode().querySelector(".start-audio-only-call");
            React.addons.TestUtils.Simulate.click(button);
 
            expect(button.disabled).to.eql(true);
          });
 
-         it("should set selectedCallType to audio", function() {
-           conversation.set("loopToken", "fake");
-
-           var button = view.getDOMNode().querySelector(".start-audio-only-call");
-           React.addons.TestUtils.Simulate.click(button);
+      it("should set selectedCallType to audio", function() {
+        conversation.set("loopToken", "fake");
 
-           expect(conversation.get("selectedCallType")).to.eql("audio");
-         });
-
-         it("should set selectedCallType to audio-video", function() {
-           conversation.set("loopToken", "fake");
+         var button = view.getDOMNode().querySelector(".start-audio-only-call");
+         React.addons.TestUtils.Simulate.click(button);
 
-           var button = view.getDOMNode().querySelector(".standalone-call-btn-video-icon");
-           React.addons.TestUtils.Simulate.click(button);
-
-           expect(conversation.get("selectedCallType")).to.eql("audio-video");
-         });
+         expect(conversation.get("selectedCallType")).to.eql("audio");
+       });
 
-      it("should set state.urlCreationDateString to a locale date string",
-         function() {
-        // wrap in a jquery object because text is broken up
-        // into several span elements
-        var date = new Date(0);
-        var options = {year: "numeric", month: "long", day: "numeric"};
-        var timestamp = date.toLocaleDateString(navigator.language, options);
+       it("should set selectedCallType to audio-video", function() {
+         conversation.set("loopToken", "fake");
 
-        expect(view.state.urlCreationDateString).to.eql(timestamp);
+         var button = view.getDOMNode().querySelector(".standalone-call-btn-video-icon");
+         React.addons.TestUtils.Simulate.click(button);
+
+         expect(conversation.get("selectedCallType")).to.eql("audio-video");
       });
 
+      // XXX this test breaks while the feature actually works; find a way to
+      // test this properly.
+      it.skip("should set state.urlCreationDateString to a locale date string",
+        function() {
+          var date = new Date();
+          var options = {year: "numeric", month: "long", day: "numeric"};
+          var timestamp = date.toLocaleDateString(navigator.language, options);
+          var dateElem = view.getDOMNode().querySelector(".call-url-date");
+
+          expect(dateElem.textContent).to.eql(timestamp);
+        });
     });
 
     describe("Events", function() {
       var conversation, view, StandaloneClient, requestCallUrlInfo;
 
       beforeEach(function() {
         conversation = new sharedModels.ConversationModel({
           loopToken: "fake"
@@ -692,17 +693,17 @@ describe("loop.webapp", function() {
         });
 
         conversation.onMarketplaceMessage = function() {};
         sandbox.stub(notifications, "errorL10n");
         requestCallUrlInfo = sandbox.stub();
 
         view = React.addons.TestUtils.renderIntoDocument(
             loop.webapp.StartConversationView({
-              model: conversation,
+              conversation: conversation,
               notifications: notifications,
               client: {requestCallUrlInfo: requestCallUrlInfo}
             })
           );
 
         loop.config.marketplaceUrl = "http://market/";
       });
 
@@ -777,33 +778,33 @@ describe("loop.webapp", function() {
           localStorage.setItem("has-seen-tos", oldLocalStorageValue);
       });
 
       it("should show the TOS", function() {
         var tos;
 
         view = React.addons.TestUtils.renderIntoDocument(
           loop.webapp.StartConversationView({
-            model: conversation,
+            conversation: conversation,
             notifications: notifications,
             client: {requestCallUrlInfo: requestCallUrlInfo}
           })
         );
         tos = view.getDOMNode().querySelector(".terms-service");
 
         expect(tos.classList.contains("hide")).to.equal(false);
       });
 
       it("should not show the TOS if it has already been seen", function() {
         var tos;
 
         localStorage.setItem("has-seen-tos", "true");
         view = React.addons.TestUtils.renderIntoDocument(
           loop.webapp.StartConversationView({
-            model: conversation,
+            conversation: conversation,
             notifications: notifications,
             client: {requestCallUrlInfo: requestCallUrlInfo}
           })
         );
         tos = view.getDOMNode().querySelector(".terms-service");
 
         expect(tos.classList.contains("hide")).to.equal(true);
       });
@@ -883,17 +884,17 @@ describe("loop.webapp", function() {
           requestCallUrlInfo: function(token, cb) {
             cb(null, {urlCreationDate: 0});
           },
           settings: {baseServerUrl: loop.webapp.baseServerUrl}
         };
 
         view = React.addons.TestUtils.renderIntoDocument(
             loop.webapp.StartConversationView({
-              model: conversation,
+              conversation: conversation,
               notifications: notifications,
               client: standaloneClientStub
             })
         );
       });
 
       it("should start the conversation establishment process", function() {
         var button = view.getDOMNode().querySelector("button");
@@ -998,17 +999,17 @@ describe("loop.webapp", function() {
       });
 
       describe("onMarketplaceMessage", function() {
         var view, setupOutgoingCall, trigger;
 
         before(function() {
           view = React.addons.TestUtils.renderIntoDocument(
             loop.webapp.StartConversationView({
-              model: model,
+              conversation: model,
               notifications: notifications,
               client: {requestCallUrlInfo: sandbox.stub()}
             })
           );
         });
 
         beforeEach(function() {
           setupOutgoingCall = sandbox.stub(model, "setupOutgoingCall");
--- a/browser/components/loop/test/xpcshell/test_loopapi_hawk_request.js
+++ b/browser/components/loop/test/xpcshell/test_loopapi_hawk_request.js
@@ -33,17 +33,17 @@ function generateSessionTypeVerification
 
       resolve();
     });
   };
 
   return hawkRequestStub;
 }
 
-const origHawkRequest = MozLoopService.oldHawkRequest;
+const origHawkRequest = MozLoopService.hawkRequest;
 do_register_cleanup(function() {
   MozLoopService.hawkRequest = origHawkRequest;
 });
 
 add_task(function* hawk_request_scope_passthrough() {
 
   // add a stub that verifies the parameter we want
   MozLoopService.hawkRequest =
--- a/browser/components/loop/ui/index.html
+++ b/browser/components/loop/ui/index.html
@@ -27,22 +27,23 @@
       window.OTProperties.cssURL = window.OTProperties.assetURL + 'css/ot.css';
     </script>
     <script src="../content/shared/libs/sdk.js"></script>
     <script src="../content/shared/libs/react-0.11.1.js"></script>
     <script src="../content/shared/libs/jquery-2.1.0.js"></script>
     <script src="../content/shared/libs/lodash-2.4.1.js"></script>
     <script src="../content/shared/libs/backbone-1.1.2.js"></script>
     <script src="../content/shared/js/feedbackApiClient.js"></script>
-    <script src="../content/shared/js/conversationStore.js"></script>
+    <script src="../content/shared/js/actions.js"></script>
     <script src="../content/shared/js/utils.js"></script>
     <script src="../content/shared/js/models.js"></script>
     <script src="../content/shared/js/mixins.js"></script>
     <script src="../content/shared/js/views.js"></script>
     <script src="../content/shared/js/websocket.js"></script>
+    <script src="../content/shared/js/conversationStore.js"></script>
     <script src="../content/js/conversationViews.js"></script>
     <script src="../content/js/client.js"></script>
     <script src="../standalone/content/js/webapp.js"></script>
     <script type="text/javascript;version=1.8" src="../content/js/contacts.js"></script>
     <script>
       if (!loop.contacts) {
         // For browsers that don't support ES6 without special flags (all but Fx
         // at the moment), we shim the contacts namespace with its most barebone
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -14,22 +14,23 @@
   // 1.1 Panel
   var PanelView = loop.panel.PanelView;
   // 1.2. Conversation Window
   var IncomingCallView = loop.conversation.IncomingCallView;
   var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
 
   // 2. Standalone webapp
   var HomeView = loop.webapp.HomeView;
-  var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
-  var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
-  var CallUrlExpiredView    = loop.webapp.CallUrlExpiredView;
+  var UnsupportedBrowserView  = loop.webapp.UnsupportedBrowserView;
+  var UnsupportedDeviceView   = loop.webapp.UnsupportedDeviceView;
+  var CallUrlExpiredView      = loop.webapp.CallUrlExpiredView;
   var PendingConversationView = loop.webapp.PendingConversationView;
-  var StartConversationView = loop.webapp.StartConversationView;
-  var EndedConversationView = loop.webapp.EndedConversationView;
+  var StartConversationView   = loop.webapp.StartConversationView;
+  var FailedConversationView  = loop.webapp.FailedConversationView;
+  var EndedConversationView   = loop.webapp.EndedConversationView;
 
   // 3. Shared components
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var ConversationView = loop.shared.views.ConversationView;
   var FeedbackView = loop.shared.views.FeedbackView;
 
   // Local helpers
   function returnTrue() {
@@ -170,18 +171,17 @@
               )
             )
           ), 
 
           Section({name: "IncomingCallView-ActiveState"}, 
             Example({summary: "Default", dashed: "true", style: {width: "260px", height: "254px"}}, 
               React.DOM.div({className: "fx-embedded"}, 
                 IncomingCallView({model: mockConversationModel, 
-                                   showDeclineMenu: true, 
-                                   video: true})
+                                   showMenu: true})
               )
             )
           ), 
 
           Section({name: "ConversationToolbar"}, 
             React.DOM.h2(null, "Desktop Conversation Window"), 
             React.DOM.div({className: "fx-embedded override-position"}, 
               Example({summary: "Default (260x265)", dashed: "true"}, 
@@ -247,20 +247,29 @@
                 DesktopPendingConversationView({callState: "gather", calleeId: "Mr Smith"})
               )
             )
           ), 
 
           Section({name: "StartConversationView"}, 
             Example({summary: "Start conversation view", dashed: "true"}, 
               React.DOM.div({className: "standalone"}, 
-                StartConversationView({model: mockConversationModel, 
+                StartConversationView({conversation: mockConversationModel, 
                                        client: mockClient, 
-                                       notifications: notifications, 
-                                       showCallOptionsMenu: true})
+                                       notifications: notifications})
+              )
+            )
+          ), 
+
+          Section({name: "FailedConversationView"}, 
+            Example({summary: "Failed conversation view", dashed: "true"}, 
+              React.DOM.div({className: "standalone"}, 
+                FailedConversationView({conversation: mockConversationModel, 
+                                        client: mockClient, 
+                                        notifications: notifications})
               )
             )
           ), 
 
           Section({name: "ConversationView"}, 
             Example({summary: "Desktop conversation window", dashed: "true", 
                      style: {width: "260px", height: "265px"}}, 
               React.DOM.div({className: "fx-embedded"}, 
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -14,22 +14,23 @@
   // 1.1 Panel
   var PanelView = loop.panel.PanelView;
   // 1.2. Conversation Window
   var IncomingCallView = loop.conversation.IncomingCallView;
   var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
 
   // 2. Standalone webapp
   var HomeView = loop.webapp.HomeView;
-  var UnsupportedBrowserView = loop.webapp.UnsupportedBrowserView;
-  var UnsupportedDeviceView = loop.webapp.UnsupportedDeviceView;
-  var CallUrlExpiredView    = loop.webapp.CallUrlExpiredView;
+  var UnsupportedBrowserView  = loop.webapp.UnsupportedBrowserView;
+  var UnsupportedDeviceView   = loop.webapp.UnsupportedDeviceView;
+  var CallUrlExpiredView      = loop.webapp.CallUrlExpiredView;
   var PendingConversationView = loop.webapp.PendingConversationView;
-  var StartConversationView = loop.webapp.StartConversationView;
-  var EndedConversationView = loop.webapp.EndedConversationView;
+  var StartConversationView   = loop.webapp.StartConversationView;
+  var FailedConversationView  = loop.webapp.FailedConversationView;
+  var EndedConversationView   = loop.webapp.EndedConversationView;
 
   // 3. Shared components
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var ConversationView = loop.shared.views.ConversationView;
   var FeedbackView = loop.shared.views.FeedbackView;
 
   // Local helpers
   function returnTrue() {
@@ -170,18 +171,17 @@
               </div>
             </Example>
           </Section>
 
           <Section name="IncomingCallView-ActiveState">
             <Example summary="Default" dashed="true" style={{width: "260px", height: "254px"}}>
               <div className="fx-embedded" >
                 <IncomingCallView  model={mockConversationModel}
-                                   showDeclineMenu={true}
-                                   video={true} />
+                                   showMenu={true} />
               </div>
             </Example>
           </Section>
 
           <Section name="ConversationToolbar">
             <h2>Desktop Conversation Window</h2>
             <div className="fx-embedded override-position">
               <Example summary="Default (260x265)" dashed="true">
@@ -247,20 +247,29 @@
                 <DesktopPendingConversationView callState={"gather"} calleeId="Mr Smith" />
               </div>
             </Example>
           </Section>
 
           <Section name="StartConversationView">
             <Example summary="Start conversation view" dashed="true">
               <div className="standalone">
-                <StartConversationView model={mockConversationModel}
+                <StartConversationView conversation={mockConversationModel}
                                        client={mockClient}
-                                       notifications={notifications}
-                                       showCallOptionsMenu={true} />
+                                       notifications={notifications} />
+              </div>
+            </Example>
+          </Section>
+
+          <Section name="FailedConversationView">
+            <Example summary="Failed conversation view" dashed="true">
+              <div className="standalone">
+                <FailedConversationView conversation={mockConversationModel}
+                                        client={mockClient}
+                                        notifications={notifications} />
               </div>
             </Example>
           </Section>
 
           <Section name="ConversationView">
             <Example summary="Desktop conversation window" dashed="true"
                      style={{width: "260px", height: "265px"}}>
               <div className="fx-embedded">
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -3,17 +3,16 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 # browser_506482.js is disabled because of frequent failures (bug 538672)
 # browser_526613.js is disabled because of frequent failures (bug 534489)
 # browser_589246.js is disabled for leaking browser windows (bug 752467)
 # browser_580512.js is disabled for leaking browser windows (bug 752467)
 
 [DEFAULT]
-skip-if = e10s
 support-files =
   head.js
   content.js
   content-forms.js
   browser_formdata_sample.html
   browser_formdata_xpath_sample.html
   browser_frametree_sample.html
   browser_frametree_sample_frameset.html
@@ -62,36 +61,40 @@ support-files =
 [browser_aboutPrivateBrowsing.js]
 [browser_aboutSessionRestore.js]
 [browser_attributes.js]
 [browser_backup_recovery.js]
 [browser_broadcast.js]
 [browser_capabilities.js]
 [browser_cleaner.js]
 [browser_dying_cache.js]
+skip-if = e10s
 [browser_dynamic_frames.js]
 [browser_form_restore_events.js]
 [browser_formdata.js]
 skip-if = buildapp == 'mulet'
 [browser_formdata_format.js]
 [browser_formdata_xpath.js]
 [browser_frametree.js]
 [browser_frame_history.js]
 [browser_global_store.js]
 [browser_history_cap.js]
 [browser_label_and_icon.js]
 [browser_merge_closed_tabs.js]
 [browser_pageStyle.js]
 [browser_privatetabs.js]
 [browser_scrollPositions.js]
 [browser_sessionHistory.js]
+skip-if = e10s
 [browser_sessionStorage.js]
+skip-if = e10s
 [browser_swapDocShells.js]
 skip-if = e10s # See bug 918634
 [browser_telemetry.js]
+skip-if = e10s
 [browser_upgrade_backup.js]
 [browser_windowRestore_perwindowpb.js]
 [browser_248970_b_perwindowpb.js]
 # Disabled because of leaks.
 # Re-enabling and rewriting this test is tracked in bug 936919.
 skip-if = true
 [browser_339445.js]
 [browser_345898.js]
@@ -99,31 +102,34 @@ skip-if = true
 [browser_354894_perwindowpb.js]
 [browser_367052.js]
 [browser_393716.js]
 [browser_394759_basic.js]
 # Disabled for intermittent failures, bug 944372.
 skip-if = true
 [browser_394759_behavior.js]
 [browser_394759_perwindowpb.js]
+skip-if = e10s
 [browser_394759_purge.js]
 [browser_423132.js]
+skip-if = e10s
 [browser_447951.js]
 [browser_454908.js]
 [browser_456342.js]
 [browser_461634.js]
 [browser_463205.js]
 [browser_463206.js]
 [browser_464199.js]
 [browser_465215.js]
 [browser_465223.js]
 [browser_466937.js]
 [browser_467409-backslashplosion.js]
 [browser_477657.js]
 [browser_480893.js]
+skip-if = e10s
 [browser_485482.js]
 [browser_485563.js]
 [browser_490040.js]
 [browser_491168.js]
 # Disabled for too many intermittent failures.
 # Can be re-enabled once bug 930202 lands.
 skip-if = true
 [browser_491577.js]
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -153,18 +153,29 @@ function updateIndicators() {
 
   let state = {
     showGlobalIndicator: count > 0,
     showCameraIndicator: false,
     showMicrophoneIndicator: false,
     showScreenSharingIndicator: ""
   };
 
+  let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
+               .getService(Ci.nsIMessageSender);
+  cpmm.sendAsyncMessage("webrtc:UpdatingIndicators");
+
+  // If several iframes in the same page use media streams, it's possible to
+  // have the same top level window several times. We use a Set to avoid
+  // sending duplicate notifications.
+  let contentWindows = new Set();
   for (let i = 0; i < count; ++i) {
-    let contentWindow = contentWindowSupportsArray.GetElementAt(i);
+    contentWindows.add(contentWindowSupportsArray.GetElementAt(i).top);
+  }
+
+  for (let contentWindow of contentWindows) {
     let camera = {}, microphone = {}, screen = {}, window = {}, app = {};
     MediaManagerService.mediaCaptureWindowState(contentWindow, camera,
                                                 microphone, screen, window, app);
     let tabState = {camera: camera.value, microphone: microphone.value};
     if (camera.value)
       state.showCameraIndicator = true;
     if (microphone.value)
       state.showMicrophoneIndicator = true;
@@ -184,18 +195,16 @@ function updateIndicators() {
     }
 
     tabState.windowId = getInnerWindowIDForWindow(contentWindow);
     tabState.documentURI = contentWindow.document.documentURI;
     let mm = getMessageManagerForWindow(contentWindow);
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
   }
 
-  let cpmm = Cc["@mozilla.org/childprocessmessagemanager;1"]
-               .getService(Ci.nsIMessageSender);
   cpmm.sendAsyncMessage("webrtc:UpdateGlobalIndicators", state);
 }
 
 function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
   let contentWindow = Services.wm.getOuterWindowWithId(aData);
   let mm = getMessageManagerForWindow(contentWindow);
   if (mm)
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators",
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -141,16 +141,19 @@ PluginContent.prototype = {
              blocklistState: blocklistState,
            };
   },
 
   // Map the plugin's name to a filtered version more suitable for user UI.
   makeNicePluginName : function (aName) {
     if (aName == "Shockwave Flash")
       return "Adobe Flash";
+    // Regex checks if aName begins with "Java" + non-letter char
+    if (/^Java\W/.exec(aName))
+      return "Java";
 
     // Clean up the plugin name by stripping off parenthetical clauses,
     // trailing version numbers or "plugin".
     // EG, "Foo Bar (Linux) Plugin 1.23_02" --> "Foo Bar"
     // Do this by first stripping the numbers, etc. off the end, and then
     // removing "Plugin" (and then trimming to get rid of any whitespace).
     // (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled)
     let newName = aName.replace(/\(.*?\)/g, "").
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -11,88 +11,68 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                   "resource://gre/modules/PluralForm.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
-                                   "@mozilla.org/mediaManagerService;1",
-                                   "nsIMediaManagerService");
-
 this.webrtcUI = {
   init: function () {
     Services.obs.addObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished", false);
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
+    ppmm.addMessageListener("webrtc:UpdatingIndicators", this);
     ppmm.addMessageListener("webrtc:UpdateGlobalIndicators", this);
 
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
     mm.addMessageListener("webrtc:Request", this);
     mm.addMessageListener("webrtc:UpdateBrowserIndicators", this);
   },
 
   uninit: function () {
     Services.obs.removeObserver(maybeAddMenuIndicator, "browser-delayed-startup-finished");
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
+    ppmm.removeMessageListener("webrtc:UpdatingIndicators", this);
     ppmm.removeMessageListener("webrtc:UpdateGlobalIndicators", this);
 
     let mm = Cc["@mozilla.org/globalmessagemanager;1"]
                .getService(Ci.nsIMessageListenerManager);
     mm.removeMessageListener("webrtc:Request", this);
     mm.removeMessageListener("webrtc:UpdateBrowserIndicators", this);
   },
 
   showGlobalIndicator: false,
   showCameraIndicator: false,
   showMicrophoneIndicator: false,
   showScreenSharingIndicator: "", // either "Application", "Screen" or "Window"
 
+  _streams: [],
   // The boolean parameters indicate which streams should be included in the result.
   getActiveStreams: function(aCamera, aMicrophone, aScreen) {
-    let contentWindowSupportsArray = MediaManagerService.activeMediaCaptureWindows;
-    let count = contentWindowSupportsArray.Count();
-    let activeStreams = [];
-    for (let i = 0; i < count; i++) {
-      let contentWindow = contentWindowSupportsArray.GetElementAt(i);
-
-      let info = {
-        Camera: {},
-        Microphone: {},
-        Window: {},
-        Screen: {},
-        Application: {}
-      };
-      MediaManagerService.mediaCaptureWindowState(contentWindow, info.Camera,
-                                                  info.Microphone, info.Screen,
-                                                  info.Window, info.Application);
-      if (!(aCamera && info.Camera.value ||
-            aMicrophone && info.Microphone.value ||
-            aScreen && (info.Screen.value || info.Window.value ||
-                        info.Application.value)))
-        continue;
-
-      let browser = getBrowserForWindow(contentWindow);
+    return webrtcUI._streams.filter(aStream => {
+      let state = aStream.state;
+      return aCamera && state.camera ||
+             aMicrophone && state.microphone ||
+             aScreen && state.screen;
+    }).map(aStream => {
+      let state = aStream.state;
+      let types = {camera: state.camera, microphone: state.microphone,
+                   screen: state.screen};
+      let browser = aStream.browser;
       let browserWindow = browser.ownerDocument.defaultView;
       let tab = browserWindow.gBrowser &&
-                browserWindow.gBrowser._getTabForContentWindow(contentWindow.top);
-      activeStreams.push({
-        uri: contentWindow.location.href,
-        tab: tab,
-        browser: browser,
-        types: info
-      });
-    }
-    return activeStreams;
+                browserWindow.gBrowser._getTabForBrowser(browser);
+      return {uri: state.documentURI, tab: tab, browser: browser, types: types};
+    });
   },
 
   showSharingDoorhanger: function(aActiveStream, aType) {
     let browserWindow = aActiveStream.browser.ownerDocument.defaultView;
     if (aActiveStream.tab) {
       browserWindow.gBrowser.selectedTab = aActiveStream.tab;
     } else {
       aActiveStream.browser.focus();
@@ -133,20 +113,24 @@ this.webrtcUI = {
     popupnotification.setAttribute("buttonlabel", bundle.getString(stringId));
   },
 
   receiveMessage: function(aMessage) {
     switch (aMessage.name) {
       case "webrtc:Request":
         prompt(aMessage.target, aMessage.data);
         break;
+      case "webrtc:UpdatingIndicators":
+        webrtcUI._streams = [];
+        break;
       case "webrtc:UpdateGlobalIndicators":
         updateIndicators(aMessage.data)
         break;
       case "webrtc:UpdateBrowserIndicators":
+        webrtcUI._streams.push({browser: aMessage.target, state: aMessage.data});
         updateBrowserSpecificIndicator(aMessage.target, aMessage.data);
         break;
     }
   }
 };
 
 function getBrowserForWindow(aContentWindow) {
   return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -557,26 +541,22 @@ function getGlobalIndicator() {
 #endif
 }
 
 function onTabSharingMenuPopupShowing(e) {
   let streams = webrtcUI.getActiveStreams(true, true, true);
   for (let streamInfo of streams) {
     let stringName = "getUserMedia.sharingMenu";
     let types = streamInfo.types;
-    if (types.Camera.value)
+    if (types.camera)
       stringName += "Camera";
-    if (types.Microphone.value)
+    if (types.microphone)
       stringName += "Microphone";
-    if (types.Screen.value)
-      stringName += "Screen";
-    else if (types.Application.value)
-      stringName += "Application";
-    else if (types.Window.value)
-      stringName += "Window";
+    if (types.screen)
+      stringName += types.screen;
 
     let doc = e.target.ownerDocument;
     let bundle = doc.defaultView.gNavigatorBundle;
 
     let origin;
     let uri;
     let href = streamInfo.uri;
     try {
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -129,18 +129,18 @@ template<typename> class OwningNonNull;
 template<typename> class Sequence;
 
 template<typename, typename> class CallbackObjectHolder;
 typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> NodeFilterHolder;
 } // namespace dom
 } // namespace mozilla
 
 #define NS_IDOCUMENT_IID \
-{ 0x613ea294, 0x0288, 0x48b4, \
-  { 0x9e, 0x7b, 0x0f, 0xe9, 0x3f, 0x8c, 0xf8, 0x95 } }
+{ 0x42a263db, 0x6ac6, 0x40ff, \
+  { 0x89, 0xe2, 0x25, 0x12, 0xe4, 0xbc, 0x2d, 0x2d } }
 
 // Enum for requesting a particular type of document when creating a doc
 enum DocumentFlavor {
   DocumentFlavorLegacyGuess, // compat with old code until made HTML5-compliant
   DocumentFlavorHTML, // HTMLDocument with HTMLness bit set to true
   DocumentFlavorSVG, // SVGDocument
   DocumentFlavorPlain, // Just a Document
 };
@@ -2357,20 +2357,25 @@ public:
   nsIHTMLCollection* Children();
   uint32_t ChildElementCount();
 
   virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
   virtual mozilla::dom::SVGDocument* AsSVGDocument() { return nullptr; }
 
   // Each import tree has exactly one master document which is
   // the root of the tree, and owns the browser context.
-  virtual already_AddRefed<nsIDocument> MasterDocument() = 0;
+  virtual nsIDocument* MasterDocument() = 0;
   virtual void SetMasterDocument(nsIDocument* master) = 0;
   virtual bool IsMasterDocument() = 0;
-  virtual already_AddRefed<mozilla::dom::ImportManager> ImportManager() = 0;
+  virtual mozilla::dom::ImportManager* ImportManager() = 0;
+  // We keep track of the order of sub imports were added to the document.
+  virtual bool HasSubImportLink(nsINode* aLink) = 0;
+  virtual uint32_t IndexOfSubImportLink(nsINode* aLink) = 0;
+  virtual void AddSubImportLink(nsINode* aLink) = 0;
+  virtual nsINode* GetSubImportLink(uint32_t aIdx) = 0;
 
   /*
    * Given a node, get a weak reference to it and append that reference to
    * mBlockedTrackingNodes. Can be used later on to look up a node in it.
    * (e.g., by the UI)
    */
   void AddBlockedTrackingNode(nsINode *node)
   {
--- a/content/base/src/ImportManager.cpp
+++ b/content/base/src/ImportManager.cpp
@@ -18,16 +18,20 @@
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsScriptLoader.h"
 #include "nsNetUtil.h"
 
+//-----------------------------------------------------------------------------
+// AutoError
+//-----------------------------------------------------------------------------
+
 class AutoError {
 public:
   explicit AutoError(mozilla::dom::ImportLoader* loader, bool scriptsBlocked = true)
     : mLoader(loader)
     , mPassed(false)
     , mScriptsBlocked(scriptsBlocked)
   {}
 
@@ -44,16 +48,230 @@ private:
   mozilla::dom::ImportLoader* mLoader;
   bool mPassed;
   bool mScriptsBlocked;
 };
 
 namespace mozilla {
 namespace dom {
 
+//-----------------------------------------------------------------------------
+// ImportLoader::Updater
+//-----------------------------------------------------------------------------
+
+void
+ImportLoader::Updater::GetReferrerChain(nsINode* aNode,
+                                        nsTArray<nsINode*>& aResult)
+{
+  // We fill up the array backward. First the last link: aNode.
+  MOZ_ASSERT(mLoader->mLinks.Contains(aNode));
+
+  aResult.AppendElement(aNode);
+  nsINode* node = aNode;
+  nsRefPtr<ImportManager> manager = mLoader->Manager();
+  for (ImportLoader* referrersLoader = manager->Find(node->OwnerDoc());
+       referrersLoader;
+       referrersLoader = manager->Find(node->OwnerDoc()))
+  {
+    // Then walking up the main referrer chain and append each link
+    // to the array.
+    node = referrersLoader->GetMainReferrer();
+    MOZ_ASSERT(node);
+    aResult.AppendElement(node);
+  }
+
+  // The reversed order is more useful for consumers.
+  // XXX: This should probably go to nsTArray or some generic utility
+  // lib for our containers that we don't have... I would really like to
+  // get rid of this part...
+  uint32_t l = aResult.Length();
+  for (uint32_t i = 0; i < l / 2; i++) {
+    Swap(aResult[i], aResult[l - i - 1]);
+  }
+}
+
+bool
+ImportLoader::Updater::ShouldUpdate(nsTArray<nsINode*>& aNewPath)
+{
+  // Let's walk down on the main referrer chains of both the current main and
+  // the new link, and find the last pair of links that are from the same
+  // document. This is the junction point between the two referrer chain. Their
+  // order in the subimport list of that document will determine if we have to
+  // update the spanning tree or this new edge changes nothing in the script
+  // execution order.
+  nsTArray<nsINode*> oldPath;
+  GetReferrerChain(mLoader->mLinks[mLoader->mMainReferrer], oldPath);
+  uint32_t max = std::min(oldPath.Length(), aNewPath.Length());
+  MOZ_ASSERT(max > 0);
+  uint32_t lastCommonImportAncestor = 0;
+
+  for (uint32_t i = 0;
+       i < max && oldPath[i]->OwnerDoc() == aNewPath[i]->OwnerDoc();
+       i++)
+  {
+    lastCommonImportAncestor = i;
+  }
+
+  MOZ_ASSERT(lastCommonImportAncestor < max);
+  nsINode* oldLink = oldPath[lastCommonImportAncestor];
+  nsINode* newLink = aNewPath[lastCommonImportAncestor];
+
+  if ((lastCommonImportAncestor == max - 1) &&
+      newLink == oldLink ) {
+    // If one chain contains the other entirely, then this is a simple cycle,
+    // nothing to be done here.
+    MOZ_ASSERT(oldPath.Length() != aNewPath.Length(),
+               "This would mean that new link == main referrer link");
+    return false;
+  }
+
+  MOZ_ASSERT(aNewPath != oldPath,
+             "How could this happen?");
+  nsIDocument* doc = oldLink->OwnerDoc();
+  MOZ_ASSERT(doc->HasSubImportLink(newLink));
+  MOZ_ASSERT(doc->HasSubImportLink(oldLink));
+
+  return doc->IndexOfSubImportLink(newLink) < doc->IndexOfSubImportLink(oldLink);
+}
+
+void
+ImportLoader::Updater::UpdateMainReferrer(uint32_t aNewIdx)
+{
+  MOZ_ASSERT(aNewIdx < mLoader->mLinks.Length());
+  nsINode* newMainReferrer = mLoader->mLinks[aNewIdx];
+
+  // This new link means we have to execute our scripts sooner...
+  // Let's make sure that unblocking a loader does not trigger a script execution.
+  // So we start with placing the new blockers and only then will we remove any
+  // blockers.
+  if (mLoader->IsBlocking()) {
+    // Our import parent is changed, let's block the new one and later unblock
+    // the old one.
+    newMainReferrer->OwnerDoc()->ScriptLoader()->AddExecuteBlocker();
+  }
+
+  if (mLoader->mDocument) {
+    // Our nearest predecessor has changed. So let's add the ScriptLoader to the
+    // new one if there is any. And remove it from the old one.
+    nsRefPtr<ImportManager> manager = mLoader->Manager();
+    nsScriptLoader* loader = mLoader->mDocument->ScriptLoader();
+    ImportLoader*& pred = mLoader->mBlockingPredecessor;
+    ImportLoader* newPred = manager->GetNearestPredecessor(newMainReferrer);
+    if (pred) {
+      if (newPred) {
+        newPred->AddBlockedScriptLoader(loader);
+      }
+      pred->RemoveBlockedScriptLoader(loader);
+    }
+  }
+
+  if (mLoader->IsBlocking()) {
+    mLoader->mImportParent->ScriptLoader()->RemoveExecuteBlocker();
+  }
+
+  // Finally update mMainReferrer to point to the newly added link.
+  mLoader->mMainReferrer = aNewIdx;
+  mLoader->mImportParent = newMainReferrer->OwnerDoc();
+}
+
+nsINode*
+ImportLoader::Updater::NextDependant(nsINode* aCurrentLink,
+                                     nsTArray<nsINode*>& aPath,
+                                     NodeTable& aVisitedNodes, bool aSkipChildren)
+{
+  // Depth first graph traversal.
+  if (!aSkipChildren) {
+    // "first child"
+    ImportLoader* loader = mLoader->Manager()->Find(aCurrentLink);
+    if (loader && loader->GetDocument()) {
+      nsINode* firstSubImport = loader->GetDocument()->GetSubImportLink(0);
+      if (firstSubImport && !aVisitedNodes.Contains(firstSubImport)) {
+        aPath.AppendElement(aCurrentLink);
+        aVisitedNodes.PutEntry(firstSubImport);
+        return firstSubImport;
+      }
+    }
+  }
+
+  aPath.AppendElement(aCurrentLink);
+  // "(parent's) next sibling"
+  while(aPath.Length() > 1) {
+    aCurrentLink = aPath[aPath.Length() - 1];
+    aPath.RemoveElementAt(aPath.Length() - 1);
+
+    // Let's find the next "sibling"
+    ImportLoader* loader =  mLoader->Manager()->Find(aCurrentLink->OwnerDoc());
+    MOZ_ASSERT(loader && loader->GetDocument(), "How can this happend?");
+    nsIDocument* doc = loader->GetDocument();
+    MOZ_ASSERT(doc->HasSubImportLink(aCurrentLink));
+    uint32_t idx = doc->IndexOfSubImportLink(aCurrentLink);
+    nsINode* next = doc->GetSubImportLink(idx + 1);
+    if (next) {
+      // Note: If we found an already visited link that means the parent links has
+      // closed the circle it's always the "first child" section that should find
+      // the first already visited node. Let's just assert that.
+      MOZ_ASSERT(!aVisitedNodes.Contains(next));
+      aVisitedNodes.PutEntry(next);
+      return next;
+    }
+  }
+
+  return nullptr;
+}
+
+void
+ImportLoader::Updater::UpdateDependants(nsINode* aNode,
+                                        nsTArray<nsINode*>& aPath)
+{
+  NodeTable visitedNodes;
+  nsINode* current = aNode;
+  uint32_t initialLength = aPath.Length();
+  bool neededUpdate = true;
+  while ((current = NextDependant(current, aPath, visitedNodes, !neededUpdate))) {
+    if (!current || aPath.Length() <= initialLength) {
+      break;
+    }
+    ImportLoader* loader = mLoader->Manager()->Find(current);
+    if (!loader) {
+      continue;
+    }
+    Updater& updater = loader->mUpdater;
+    neededUpdate = updater.ShouldUpdate(aPath);
+    if (neededUpdate) {
+      updater.UpdateMainReferrer(loader->mLinks.IndexOf(current));
+    }
+  }
+}
+
+void
+ImportLoader::Updater::UpdateSpanningTree(nsINode* aNode)
+{
+  if (mLoader->mReady || mLoader->mStopped) {
+    // Scripts already executed, nothing to be done here.
+    return;
+  }
+
+  if (mLoader->mLinks.Length() == 1) {
+    // If this is the first referrer, let's mark it.
+    mLoader->mMainReferrer = 0;
+    return;
+  }
+
+  nsTArray<nsINode*> newReferrerChain;
+  GetReferrerChain(aNode, newReferrerChain);
+  if (ShouldUpdate(newReferrerChain)) {
+    UpdateMainReferrer(mLoader->mLinks.Length() - 1);
+    UpdateDependants(aNode, newReferrerChain);
+  }
+}
+
+//-----------------------------------------------------------------------------
+// ImportLoader
+//-----------------------------------------------------------------------------
+
 NS_INTERFACE_MAP_BEGIN(ImportLoader)
   NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
   NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ImportLoader)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportLoader)
@@ -61,64 +279,98 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportL
 NS_IMPL_CYCLE_COLLECTION(ImportLoader,
                          mDocument,
                          mImportParent,
                          mLinks)
 
 ImportLoader::ImportLoader(nsIURI* aURI, nsIDocument* aImportParent)
   : mURI(aURI)
   , mImportParent(aImportParent)
+  , mBlockingPredecessor(nullptr)
   , mReady(false)
   , mStopped(false)
   , mBlockingScripts(false)
-{}
+  , mUpdater(MOZ_THIS_IN_INITIALIZER_LIST())
+{
+}
 
 void
 ImportLoader::BlockScripts()
 {
   MOZ_ASSERT(!mBlockingScripts);
   mImportParent->ScriptLoader()->AddExecuteBlocker();
   mBlockingScripts = true;
 }
 
 void
 ImportLoader::UnblockScripts()
 {
   MOZ_ASSERT(mBlockingScripts);
   mImportParent->ScriptLoader()->RemoveExecuteBlocker();
+  for (uint32_t i = 0; i < mBlockedScriptLoaders.Length(); i++) {
+    mBlockedScriptLoaders[i]->RemoveExecuteBlocker();
+  }
+  mBlockedScriptLoaders.Clear();
   mBlockingScripts = false;
 }
 
 void
+ImportLoader::SetBlockingPredecessor(ImportLoader* aLoader)
+{
+  mBlockingPredecessor = aLoader;
+}
+
+void
 ImportLoader::DispatchEventIfFinished(nsINode* aNode)
 {
   MOZ_ASSERT(!(mReady && mStopped));
   if (mReady) {
     DispatchLoadEvent(aNode);
   }
   if (mStopped) {
     DispatchErrorEvent(aNode);
   }
 }
 
 void
+ImportLoader::AddBlockedScriptLoader(nsScriptLoader* aScriptLoader)
+{
+  if (mBlockedScriptLoaders.Contains(aScriptLoader)) {
+    return;
+  }
+
+  aScriptLoader->AddExecuteBlocker();
+
+  // Let's keep track of the pending script loaders.
+  mBlockedScriptLoaders.AppendElement(aScriptLoader);
+}
+
+bool
+ImportLoader::RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader)
+{
+  aScriptLoader->RemoveExecuteBlocker();
+  return mBlockedScriptLoaders.RemoveElement(aScriptLoader);
+}
+
+void
 ImportLoader::AddLinkElement(nsINode* aNode)
 {
   // If a new link element is added to the import tree that
   // refers to an import that is already finished loading or
   // stopped trying, we need to fire the corresponding event
   // on it.
-  mLinks.AppendObject(aNode);
+  mLinks.AppendElement(aNode);
+  mUpdater.UpdateSpanningTree(aNode);
   DispatchEventIfFinished(aNode);
 }
 
 void
 ImportLoader::RemoveLinkElement(nsINode* aNode)
 {
-  mLinks.RemoveObject(aNode);
+  mLinks.RemoveElement(aNode);
 }
 
 // Events has to be fired with a script runner, so mImport can
 // be set on the link element before the load event is fired even
 // if ImportLoader::Get returns an already loaded import and we
 // fire the load event immediately on the new referring link element.
 class AsyncEvent : public nsRunnable {
 public:
@@ -154,31 +406,31 @@ ImportLoader::DispatchLoadEvent(nsINode*
 {
   nsContentUtils::AddScriptRunner(new AsyncEvent(aNode, /* aSuccess = */ true));
 }
 
 void
 ImportLoader::Done()
 {
   mReady = true;
-  uint32_t count = mLinks.Count();
-  for (uint32_t i = 0; i < count; i++) {
+  uint32_t l = mLinks.Length();
+  for (uint32_t i = 0; i < l; i++) {
     DispatchLoadEvent(mLinks[i]);
   }
   UnblockScripts();
   ReleaseResources();
 }
 
 void
 ImportLoader::Error(bool aUnblockScripts)
 {
   mDocument = nullptr;
   mStopped = true;
-  uint32_t count = mLinks.Count();
-  for (uint32_t i = 0; i < count; i++) {
+  uint32_t l = mLinks.Length();
+  for (uint32_t i = 0; i < l; i++) {
     DispatchErrorEvent(mLinks[i]);
   }
   if (aUnblockScripts) {
     UnblockScripts();
   }
   ReleaseResources();
 }
 
@@ -386,25 +638,47 @@ ImportLoader::OnStartRequest(nsIRequest*
   nsCOMPtr<nsILoadGroup> newLoadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   NS_ENSURE_TRUE(newLoadGroup, NS_ERROR_OUT_OF_MEMORY);
   newLoadGroup->SetLoadGroup(loadGroup);
   rv = mDocument->StartDocumentLoad("import", channel, newLoadGroup,
                                     nullptr, getter_AddRefs(listener),
                                     true);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
 
-  // Let's start parser.
+  nsCOMPtr<nsIURI> originalURI;
+  rv = channel->GetOriginalURI(getter_AddRefs(originalURI));
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
+
+  nsCOMPtr<nsIURI> URI;
+  rv = channel->GetURI(getter_AddRefs(URI));
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
+  MOZ_ASSERT(URI, "URI of a channel should never be null");
+
+  bool equals;
+  rv = URI->Equals(originalURI, &equals);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
+
+  if (!equals) {
+    // In case of a redirection we must add the new URI to the import map.
+    Manager()->AddLoaderWithNewURI(this, URI);
+  }
+
+  // Let's start the parser.
   mParserStreamListener = listener;
   rv = listener->OnStartRequest(aRequest, aContext);
   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_ABORT_ERR);
 
   ae.Pass();
   return NS_OK;
 }
 
+//-----------------------------------------------------------------------------
+// ImportManager
+//-----------------------------------------------------------------------------
+
 NS_IMPL_CYCLE_COLLECTION(ImportManager,
                          mImports)
 
 NS_INTERFACE_MAP_BEGIN(ImportManager)
   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(ImportManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ImportManager)
@@ -412,21 +686,83 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(ImportM
 
 already_AddRefed<ImportLoader>
 ImportManager::Get(nsIURI* aURI, nsINode* aNode, nsIDocument* aOrigDocument)
 {
   // Check if we have a loader for that URI, if not create one,
   // and start it up.
   nsRefPtr<ImportLoader> loader;
   mImports.Get(aURI, getter_AddRefs(loader));
-
+  bool needToStart = false;
   if (!loader) {
     loader = new ImportLoader(aURI, aOrigDocument);
     mImports.Put(aURI, loader);
+    needToStart = true;
+  }
+
+  MOZ_ASSERT(loader);
+  // Let's keep track of the sub imports links in each document. It will
+  // be used later for scrip execution order calculation. (see UpdateSpanningTree)
+  // NOTE: removing and adding back the link to the tree somewhere else will
+  // NOT have an effect on script execution order.
+  if (!aOrigDocument->HasSubImportLink(aNode)) {
+    aOrigDocument->AddSubImportLink(aNode);
+  }
+
+  loader->AddLinkElement(aNode);
+
+  if (needToStart) {
     loader->Open();
   }
-  loader->AddLinkElement(aNode);
-  MOZ_ASSERT(loader);
+
   return loader.forget();
 }
 
+ImportLoader*
+ImportManager::Find(nsIDocument* aImport)
+{
+  return mImports.GetWeak(aImport->GetDocumentURIObject());
+}
+
+ImportLoader*
+ImportManager::Find(nsINode* aLink)
+{
+  HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(aLink);
+  nsCOMPtr<nsIURI> uri = linkElement->GetHrefURI();
+  return mImports.GetWeak(uri);
+}
+
+void
+ImportManager::AddLoaderWithNewURI(ImportLoader* aLoader, nsIURI* aNewURI)
+{
+  mImports.Put(aNewURI, aLoader);
+}
+
+nsRefPtr<ImportLoader> ImportManager::GetNearestPredecessor(nsINode* aNode)
+{
+  // Return the previous link if there is any in the same document.
+  nsIDocument* doc = aNode->OwnerDoc();
+  int32_t idx = doc->IndexOfSubImportLink(aNode);
+  MOZ_ASSERT(idx != -1, "aNode must be a sub import link of its owner document");
+  if (idx == 0) {
+    if (doc->IsMasterDocument()) {
+      // If there is no previous one, and it was the master document, then
+      // there is no predecessor.
+      return nullptr;
+    }
+    // Else we find the main referrer of the import parent of the link's document.
+    // And do a recursion.
+    ImportLoader* owner = Find(doc);
+    MOZ_ASSERT(owner);
+    nsCOMPtr<nsINode> mainReferrer = owner->GetMainReferrer();
+    return GetNearestPredecessor(mainReferrer);
+  }
+  MOZ_ASSERT(idx > 0);
+  HTMLLinkElement* link =
+    static_cast<HTMLLinkElement*>(doc->GetSubImportLink(idx - 1));
+  nsCOMPtr<nsIURI> uri = link->GetHrefURI();
+  nsRefPtr<ImportLoader> ret;
+  mImports.Get(uri, getter_AddRefs(ret));
+  return ret;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/ImportManager.h
+++ b/content/base/src/ImportManager.h
@@ -34,40 +34,100 @@
  *  |                                        |
  *  LinkElement <-----------------------------
  *
  */
 
 #ifndef mozilla_dom_ImportManager_h__
 #define mozilla_dom_ImportManager_h__
 
-#include "nsCOMArray.h"
+#include "nsTArray.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
 #include "nsIStreamListener.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsRefPtrHashtable.h"
+#include "nsScriptLoader.h"
 #include "nsURIHashKey.h"
 
 class nsIDocument;
 class nsIChannel;
 class nsIPrincipal;
 class nsINode;
 class AutoError;
 
 namespace mozilla {
 namespace dom {
 
 class ImportManager;
 
+typedef nsTHashtable<nsPtrHashKey<nsINode>> NodeTable;
+
 class ImportLoader MOZ_FINAL : public nsIStreamListener
                              , public nsIDOMEventListener
 {
+
+  // A helper inner class to decouple the logic of updating the import graph
+  // after a new import link has been found by one of the parsers.
+  class Updater {
+
+  public:
+    Updater(ImportLoader* aLoader) : mLoader(aLoader)
+    {}
+
+    // After a new link is added that refers to this import, we
+    // have to update the spanning tree, since given this new link the
+    // priority of this import might be higher in the scripts
+    // execution order than before. It updates mMainReferrer, mImportParent,
+    // the corresponding pending ScriptRunners, etc.
+    // It also handles updating additional dependant loaders via the
+    // UpdateDependants calls.
+    // (NOTE: See GetMainReferrer about spanning tree.)
+    void UpdateSpanningTree(nsINode* aNode);
+
+  private:
+    // Returns an array of links that forms a referring chain from
+    // the master document to this import. Each link in the array
+    // is marked as main referrer in the list.
+    void GetReferrerChain(nsINode* aNode, nsTArray<nsINode*>& aResult);
+
+    // Once we find a new referrer path to our import, we have to see if
+    // it changes the load order hence we have to do an update on the graph.
+    bool ShouldUpdate(nsTArray<nsINode*>& aNewPath);
+    void UpdateMainReferrer(uint32_t newIdx);
+
+    // It's a depth first graph traversal algorithm, for UpdateDependants. The
+    // nodes in the graph are the import link elements, and there is a directed
+    // edge from link1 to link2 if link2 is a subimport in the import document
+    // of link1.
+    // If the ImportLoader that aCurrentLink points to didn't need to be updated
+    // the algorithm skips its "children" (subimports). Note, that this graph can
+    // also contain cycles, aVisistedLinks is used to track the already visited
+    // links to avoid an infinite loop.
+    // aPath - (in/out) the referrer link chain of aCurrentLink when called, and
+    //                  of the next link when the function returns
+    // aVisitedLinks - (in/out) list of links that the traversal already visited
+    //                          (to handle cycles in the graph)
+    // aSkipChildren - when aCurrentLink points to an import that did not need
+    //                 to be updated, we can skip its sub-imports ('children')
+    nsINode* NextDependant(nsINode* aCurrentLink,
+                           nsTArray<nsINode*>& aPath,
+                           NodeTable& aVisitedLinks, bool aSkipChildren);
+
+    // When we find a new link that changes the load order of the known imports,
+    // we also have to check all the subimports of it, to see if they need an
+    // update too. (see test_imports_nested_2.html)
+    void UpdateDependants(nsINode* aNode, nsTArray<nsINode*>& aPath);
+
+    ImportLoader* mLoader;
+  };
+
   friend class ::AutoError;
   friend class ImportManager;
+  friend class Updater;
 
 public:
   ImportLoader(nsIURI* aURI, nsIDocument* aOriginDocument);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ImportLoader, nsIStreamListener)
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
@@ -78,21 +138,56 @@ public:
 
   // Validation then opening and starting up the channel.
   void Open();
   void AddLinkElement(nsINode* aNode);
   void RemoveLinkElement(nsINode* aNode);
   bool IsReady() { return mReady; }
   bool IsStopped() { return mStopped; }
   bool IsBlocking() { return mBlockingScripts; }
-  already_AddRefed<nsIDocument> GetImport()
+
+  ImportManager* Manager() {
+    MOZ_ASSERT(mDocument || mImportParent, "One of them should be always set");
+    return (mDocument ? mDocument : mImportParent)->ImportManager();
+  }
+
+  // Simply getter for the import document. Can return a partially parsed
+  // document if called too early.
+  nsIDocument* GetDocument()
+  {
+    return mDocument;
+  }
+
+  // Getter for the import document that is used in the spec. Returns
+  // nullptr if the import is not yet ready.
+  nsIDocument* GetImport()
   {
-    return mReady ? nsCOMPtr<nsIDocument>(mDocument).forget() : nullptr;
+    return mReady ? mDocument : nullptr;
   }
 
+  // There is only one referring link that is marked as primary link per
+  // imports. This is the one that has to be taken into account when
+  // scrip execution order is determined. Links marked as primary link form
+  // a spanning tree in the import graph. (Eliminating the cycles and
+  // multiple parents.) This spanning tree is recalculated every time
+  // a new import link is added to the manager.
+  nsINode* GetMainReferrer()
+  {
+    return mLinks.IsEmpty() ? nullptr : mLinks[mMainReferrer];
+  }
+
+  // An import is not only blocked by its import children, but also
+  // by its predecessors. It's enough to find the closest predecessor
+  // and wait for that to run its scripts. We keep track of all the
+  // ScriptRunners that are waiting for this import. NOTE: updating
+  // the main referrer might change this list.
+  void AddBlockedScriptLoader(nsScriptLoader* aScriptLoader);
+  bool RemoveBlockedScriptLoader(nsScriptLoader* aScriptLoader);
+  void SetBlockingPredecessor(ImportLoader* aLoader);
+
 private:
   ~ImportLoader() {}
 
   // If a new referrer LinkElement was added, let's
   // see if we are already finished and if so fire
   // the right event.
   void DispatchEventIfFinished(nsINode* aNode);
 
@@ -117,39 +212,67 @@ private:
   void UnblockScripts();
 
   nsIPrincipal* Principal();
 
   nsCOMPtr<nsIDocument> mDocument;
   nsCOMPtr<nsIURI> mURI;
   nsCOMPtr<nsIStreamListener> mParserStreamListener;
   nsCOMPtr<nsIDocument> mImportParent;
+  ImportLoader* mBlockingPredecessor;
+
   // List of the LinkElements that are referring to this import
   // we need to keep track of them so we can fire event on them.
-  nsCOMArray<nsINode> mLinks;
+  nsTArray<nsCOMPtr<nsINode>> mLinks;
+
+  // List of pending ScriptLoaders that are waiting for this import
+  // to finish.
+  nsTArray<nsRefPtr<nsScriptLoader>> mBlockedScriptLoaders;
+
+  // There is always exactly one referrer link that is flagged as
+  // the main referrer the primary link. This is the one that is
+  // used in the script execution order calculation.
+  // ("Branch" according to the spec.)
+  uint32_t mMainReferrer;
   bool mReady;
   bool mStopped;
   bool mBlockingScripts;
+  Updater mUpdater;
 };
 
 class ImportManager MOZ_FINAL : public nsISupports
 {
   typedef nsRefPtrHashtable<nsURIHashKey, ImportLoader> ImportMap;
 
   ~ImportManager() {}
 
 public:
   ImportManager() {}
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(ImportManager)
 
+  // Finds the ImportLoader that belongs to aImport in the map.
+  ImportLoader* Find(nsIDocument* aImport);
+
+  // Find the ImportLoader aLink refers to.
+  ImportLoader* Find(nsINode* aLink);
+
+  void AddLoaderWithNewURI(ImportLoader* aLoader, nsIURI* aNewURI);
+
+  // When a new import link is added, this getter either creates
+  // a new ImportLoader for it, or returns an existing one if
+  // it was already created and in the import map.
   already_AddRefed<ImportLoader> Get(nsIURI* aURI, nsINode* aNode,
                                      nsIDocument* aOriginDocument);
 
+  // It finds the predecessor for an import link node that runs its
+  // scripts the latest among its predecessors.
+  nsRefPtr<ImportLoader> GetNearestPredecessor(nsINode* aNode);
+
 private:
   ImportMap mImports;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImportManager_h__
--- a/content/base/src/nsCSPContext.cpp
+++ b/content/base/src/nsCSPContext.cpp
@@ -687,16 +687,29 @@ nsCSPContext::SendReports(nsISupports* a
     }
 
     if (NS_FAILED(rv)) {
       CSPCONTEXTLOG(("Could not create new channel for report URI %s",
                      reportURICstring.get()));
       continue; // don't return yet, there may be more URIs
     }
 
+    // log a warning to console if scheme is not http or https
+    bool isHttpScheme =
+      (NS_SUCCEEDED(reportURI->SchemeIs("http", &isHttpScheme)) && isHttpScheme) ||
+      (NS_SUCCEEDED(reportURI->SchemeIs("https", &isHttpScheme)) && isHttpScheme);
+
+    if (!isHttpScheme) {
+      const char16_t* params[] = { reportURIs[r].get() };
+      CSP_LogLocalizedStr(NS_LITERAL_STRING("reportURInotHttpsOrHttp2").get(),
+                          params, ArrayLength(params),
+                          aSourceFile, aScriptSample, aLineNum, 0,
+                          nsIScriptError::errorFlag, "CSP", mInnerWindowID);
+    }
+
     // make sure this is an anonymous request (no cookies) so in case the
     // policy URI is injected, it can't be abused for CSRF.
     nsLoadFlags flags;
     rv = reportChannel->GetLoadFlags(&flags);
     NS_ENSURE_SUCCESS(rv, rv);
     flags |= nsIRequest::LOAD_ANONYMOUS;
     rv = reportChannel->SetLoadFlags(flags);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1992,16 +1992,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
 
   // Traverse all our nsCOMArrays.
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnDemandBuiltInUASheets)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreloadingImages)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSubImportLinks)
+
   for (uint32_t i = 0; i < tmp->mFrameRequestCallbacks.Length(); ++i) {
     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mFrameRequestCallbacks[i]");
     cb.NoteXPCOMChild(tmp->mFrameRequestCallbacks[i].mCallback.GetISupports());
   }
 
   // Traverse animation components
   if (tmp->mAnimationController) {
     tmp->mAnimationController->Traverse(&cb);
@@ -2056,16 +2058,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMasterDocument)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mImportManager)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSubImportLinks)
 
   tmp->mParentDocument = nullptr;
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreloadingImages)
 
 
   if (tmp->mBoxObjectTable) {
    tmp->mBoxObjectTable->EnumerateRead(ClearAllBoxObjects, nullptr);
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1285,49 +1285,70 @@ public:
                                                   const nsAString& aTypeExtension,
                                                   mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual already_AddRefed<Element> CreateElementNS(const nsAString& aNamespaceURI,
                                                     const nsAString& aQualifiedName,
                                                     const nsAString& aTypeExtension,
                                                     mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) MOZ_OVERRIDE;
 
-  virtual already_AddRefed<nsIDocument> MasterDocument()
+  virtual nsIDocument* MasterDocument()
   {
-    return mMasterDocument ? (nsCOMPtr<nsIDocument>(mMasterDocument)).forget()
-                           : (nsCOMPtr<nsIDocument>(this)).forget();
+    return mMasterDocument ? mMasterDocument.get()
+                           : this;
   }
 
   virtual void SetMasterDocument(nsIDocument* master)
   {
     mMasterDocument = master;
   }
 
   virtual bool IsMasterDocument()
   {
     return !mMasterDocument;
   }
 
-  virtual already_AddRefed<mozilla::dom::ImportManager> ImportManager()
+  virtual mozilla::dom::ImportManager* ImportManager()
   {
     if (mImportManager) {
       MOZ_ASSERT(!mMasterDocument, "Only the master document has ImportManager set");
-      return nsRefPtr<mozilla::dom::ImportManager>(mImportManager).forget();
+      return mImportManager.get();
     }
 
     if (mMasterDocument) {
       return mMasterDocument->ImportManager();
     }
 
     // ImportManager is created lazily.
     // If the manager is not yet set it has to be the
     // master document and this is the first import in it.
     // Let's create a new manager.
     mImportManager = new mozilla::dom::ImportManager();
-    return nsRefPtr<mozilla::dom::ImportManager>(mImportManager).forget();
+    return mImportManager.get();
+  }
+
+  virtual bool HasSubImportLink(nsINode* aLink)
+  {
+    return mSubImportLinks.Contains(aLink);
+  }
+
+  virtual uint32_t IndexOfSubImportLink(nsINode* aLink)
+  {
+    return mSubImportLinks.IndexOf(aLink);
+  }
+
+  virtual void AddSubImportLink(nsINode* aLink)
+  {
+    mSubImportLinks.AppendElement(aLink);
+  }
+
+  virtual nsINode* GetSubImportLink(uint32_t aIdx)
+  {
+    return aIdx < mSubImportLinks.Length() ? mSubImportLinks[aIdx].get()
+                                           : nullptr;
   }
 
   virtual void UnblockDOMContentLoaded() MOZ_OVERRIDE;
 
 protected:
   friend class nsNodeUtils;
   friend class nsDocumentOnStack;
 
@@ -1749,16 +1770,17 @@ private:
 
   nsrefcnt mStackRefCnt;
   bool mNeedsReleaseAfterStackRefCntRelease;
 
   CSPErrorQueue mCSPWebConsoleErrorQueue;
 
   nsCOMPtr<nsIDocument> mMasterDocument;
   nsRefPtr<mozilla::dom::ImportManager> mImportManager;
+  nsTArray<nsCOMPtr<nsINode> > mSubImportLinks;
 
   // Set to true when the document is possibly controlled by the ServiceWorker.
   // Used to prevent multiple requests to ServiceWorkerManager.
   bool mMaybeServiceWorkerControlled;
 
 #ifdef DEBUG
 public:
   bool mWillReparent;
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -44,16 +44,17 @@
 #include "nsIChannelPolicy.h"
 #include "nsChannelPolicy.h"
 #include "nsCRT.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsSandboxFlags.h"
 #include "nsContentTypeParser.h"
 #include "nsINetworkPredictor.h"
+#include "ImportManager.h"
 #include "mozilla/dom/EncodingUtils.h"
 
 #include "mozilla/CORSMode.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/unused.h"
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gCspPRLog;
@@ -1231,30 +1232,64 @@ nsScriptLoader::ProcessPendingRequests()
 bool
 nsScriptLoader::ReadyToExecuteScripts()
 {
   // Make sure the SelfReadyToExecuteScripts check is first, so that
   // we don't block twice on an ancestor.
   if (!SelfReadyToExecuteScripts()) {
     return false;
   }
-  
+
   for (nsIDocument* doc = mDocument; doc; doc = doc->GetParentDocument()) {
     nsScriptLoader* ancestor = doc->ScriptLoader();
     if (!ancestor->SelfReadyToExecuteScripts() &&
         ancestor->AddPendingChildLoader(this)) {
       AddExecuteBlocker();
       return false;
     }
   }
 
+  if (!mDocument->IsMasterDocument()) {
+    nsRefPtr<ImportManager> im = mDocument->ImportManager();
+    nsRefPtr<ImportLoader> loader = im->Find(mDocument);
+    MOZ_ASSERT(loader, "How can we have an import document without a loader?");
+
+    // The referring link that counts in the execution order calculation
+    // (in spec: flagged as branch)
+    nsCOMPtr<nsINode> referrer = loader->GetMainReferrer();
+    MOZ_ASSERT(referrer, "There has to be a main referring link for each imports");
+
+    // Import documents are blocked by their import predecessors. We need to
+    // wait with script execution until all the predecessors are done.
+    // Technically it means we have to wait for the last one to finish,
+    // which is the neares one to us in the order.
+    nsRefPtr<ImportLoader> lastPred = im->GetNearestPredecessor(referrer);
+    if (!lastPred) {
+      // If there is no predecessor we can run.
+      return true;
+    }
+
+    nsCOMPtr<nsIDocument> doc = lastPred->GetDocument();
+    if (lastPred->IsBlocking() || !doc || (doc && !doc->ScriptLoader()->SelfReadyToExecuteScripts())) {
+      // Document has not been created yet or it was created but not ready.
+      // Either case we are blocked by it. The ImportLoader will take care
+      // of blocking us, and adding the pending child loader to the blocking
+      // ScriptLoader when it's possible (at this point the blocking loader
+      // might not have created the document/ScriptLoader)
+      lastPred->AddBlockedScriptLoader(this);
+      // As more imports are parsed, this can change, let's cache what we
+      // blocked, so it can be later updated if needed (see: ImportLoader::Updater).
+      loader->SetBlockingPredecessor(lastPred);
+      return false;
+    }
+  }
+
   return true;
 }
 
-
 // This function was copied from nsParser.cpp. It was simplified a bit.
 static bool
 DetectByteOrderMark(const unsigned char* aBytes, int32_t aLen, nsCString& oCharset)
 {
   if (aLen < 2)
     return false;
 
   switch(aBytes[0]) {
--- a/content/base/src/nsScriptLoader.h
+++ b/content/base/src/nsScriptLoader.h
@@ -244,16 +244,20 @@ public:
 
   /**
    * Process a request that was deferred so that the script could be compiled
    * off thread.
    */
   nsresult ProcessOffThreadRequest(nsScriptLoadRequest *aRequest,
                                    void **aOffThreadToken);
 
+  bool AddPendingChildLoader(nsScriptLoader* aChild) {
+    return mPendingChildLoaders.AppendElement(aChild) != nullptr;
+  }
+
 private:
   virtual ~nsScriptLoader();
 
   /**
    * Unblocks the creator parser of the parser-blocking scripts.
    */
   void UnblockParser(nsScriptLoadRequest* aParserBlockingRequest);
 
@@ -297,20 +301,16 @@ private:
   /**
    * Return whether just this loader is ready to execute scripts.
    */
   bool SelfReadyToExecuteScripts()
   {
     return mEnabled && !mBlockerCount;
   }
 
-  bool AddPendingChildLoader(nsScriptLoader* aChild) {
-    return mPendingChildLoaders.AppendElement(aChild) != nullptr;
-  }
-
   nsresult AttemptAsyncScriptParse(nsScriptLoadRequest* aRequest);
   nsresult ProcessRequest(nsScriptLoadRequest* aRequest,
                           void **aOffThreadToken = nullptr);
   void FireScriptAvailable(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult,
                            nsScriptLoadRequest* aRequest);
   nsresult EvaluateScript(nsScriptLoadRequest* aRequest,
--- a/content/base/test/chrome/cpows_child.js
+++ b/content/base/test/chrome/cpows_child.js
@@ -8,16 +8,17 @@ var is_remote;
 
 (function start() {
     [is_remote] = sendSyncMessage("cpows:is_remote");
     parent_test();
     error_reporting_test();
     dom_test();
     xray_test();
     compartment_test();
+    regexp_test();
     sync_test();
     async_test();
     rpc_test();
     nested_sync_test();
     // The sync-ness of this call is important, because otherwise
     // we tear down the child's document while we are
     // still in the async test in the parent.
     // This test races with itself to be the final test.
@@ -138,16 +139,21 @@ function compartment_test()
     is(obj.expando, undefined, "child->parent CPOW cannot access properties");
 
     return results;
   }
   sendSyncMessage("cpows:compartment_test", {}, { getUnprivilegedObject: sb.getUnprivilegedObject,
                                                   testParentObject: testParentObject });
 }
 
+function regexp_test()
+{
+  sendSyncMessage("cpows:regexp_test", {}, { regexp: /myRegExp/g });
+}
+
 function sync_test()
 {
   dump('beginning cpow sync test\n');
   sync_obj = make_object();
   sendSyncMessage("cpows:sync",
     make_json(),
     make_object());
 }
--- a/content/base/test/chrome/cpows_parent.xul
+++ b/content/base/test/chrome/cpows_parent.xul
@@ -228,16 +228,31 @@
       // Send an object to the child to let it verify invariants in the other direction.
       function passMe() { return 42; };
       passMe.expando = 42;
       let results = testParentObject(passMe);
       ok(results.length > 0, "Need results");
       results.forEach((x) => is(x.result, "PASS", x.message));
     }
 
+    function recvRegExpTest(message) {
+      let regexp = message.objects.regexp;
+
+      // These work generically.
+      is(regexp.toString(), "/myRegExp/g", "toString works right");
+      ok(regexp.test("I like myRegExp to match"), "No false positives");
+      ok(!regexp.test("asdfsdf"), "No false positives");
+
+      // These go over regexp_toShared.
+      is("filler myRegExp filler".search(regexp), 7, "String.prototype.match works right");
+      var shell = /x/;
+      shell.compile(regexp);
+      is(regexp.toString(), shell.toString(), ".compile works right");
+    }
+
     let savedWilldieObj;
     let wontDie = {f:2, __exposedProps__: {"f": "r"}};
     function recvLifetimeTest1(message) {
       let obj = message.objects.obj;
       savedWilldieObj = obj.will_die;
       ok(savedWilldieObj.f == 1, "limited-lifetime CPOW works at first");
       obj.wont_die = wontDie;
       obj = null;
@@ -281,16 +296,17 @@
       mm.addMessageListener("cpows:done", recvDoneMessage);
       mm.addMessageListener("cpows:fail", recvFailMessage);
       mm.addMessageListener("cpows:parent_test", recvParentTest);
       mm.addMessageListener("cpows:error_reporting_test", recvErrorReportingTest);
       mm.addMessageListener("cpows:dom_test", recvDomTest);
       mm.addMessageListener("cpows:dom_test_after_gc", recvDomTestAfterGC);
       mm.addMessageListener("cpows:xray_test", recvXrayTest);
       mm.addMessageListener("cpows:compartment_test", recvCompartmentTest);
+      mm.addMessageListener("cpows:regexp_test", recvRegExpTest);
       mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1);
       mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2);
       mm.loadFrameScript("chrome://mochitests/content/chrome/content/base/test/chrome/cpows_child.js", true);
     }
 
     function start() {
       run_tests('remote');
     }
--- a/content/html/content/moz.build
+++ b/content/html/content/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['public', 'src']
 
 MOCHITEST_MANIFESTS += [
     'test/forms/mochitest.ini',
+    'test/imports/mochitest.ini',
     'test/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'test/chrome.ini',
     'test/forms/chrome.ini',
 ]
 
--- a/content/html/content/src/HTMLLinkElement.cpp
+++ b/content/html/content/src/HTMLLinkElement.cpp
@@ -258,25 +258,16 @@ HTMLLinkElement::UpdateImport()
     // the import and reset mImportLoader.
     if (mImportLoader) {
       mImportLoader->RemoveLinkElement(this);
       mImportLoader = nullptr;
     }
     return;
   }
 
-  // Until the script execution order is not sorted out for nested cases
-  // let's not allow them.
-  if (!doc->IsMasterDocument()) {
-    nsContentUtils::LogSimpleConsoleError(
-      NS_LITERAL_STRING("Nested imports are not supported yet"),
-      "Imports");
-    return;
-  }
-
   // 2. rel type should be import.
   nsAutoString rel;
   GetAttr(kNameSpaceID_None, nsGkAtoms::rel, rel);
   uint32_t linkTypes = nsStyleLinkElement::ParseLinkTypes(rel, NodePrincipal());
   if (!(linkTypes & eHTMLIMPORT)) {
     mImportLoader = nullptr;
     return;
   }
@@ -521,13 +512,13 @@ JSObject*
 HTMLLinkElement::WrapNode(JSContext* aCx)
 {
   return HTMLLinkElementBinding::Wrap(aCx, this);
 }
 
 already_AddRefed<nsIDocument>
 HTMLLinkElement::GetImport()
 {
-  return mImportLoader ? mImportLoader->GetImport() : nullptr;
+  return mImportLoader ? nsRefPtr<nsIDocument>(mImportLoader->GetImport()).forget() : nullptr;
 }
 
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importA1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importA2.html" id="A2" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("A1");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importA2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+  </head>
+  <body>
+    <script>
+      order.push("A2");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importB1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importB2.html" id="B2" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("B1");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importB2.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+  </head>
+  <body>
+    <script>
+      order.push("B2");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC2.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C1");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC10.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importE.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C10");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC2.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC3.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C2");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC3.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC4.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C3");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC4.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC5.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C4");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC5.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC6.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C5");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC6.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC7.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C6");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC7.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC8.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C7");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC8.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC9.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C8");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importC9.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importC10.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("C9");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importD.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <body>
+    <script>
+      order.push("D");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/file_importE.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en-US">
+  <head>
+    <link rel="import" href="file_importD.html" onload="loaded()" onerror="failed()"></link>
+  </head>
+  <body>
+    <script>
+      order.push("E");
+    </script>
+  </body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/imports/mochitest.ini
@@ -0,0 +1,20 @@
+[DEFAULT]
+support-files =
+  file_importA1.html
+  file_importA2.html
+  file_importB1.html
+  file_importB2.html
+  file_importC1.html
+  file_importC2.html
+  file_importC3.html
+  file_importC4.html
+  file_importC5.html
+  file_importC6.html
+  file_importC7.html
+  file_importC8.html
+  file_importC9.html
+  file_importC10.html
+  file_importD.html
+  file_importE.html
+
+
--- a/content/html/content/test/mochitest.ini
+++ b/content/html/content/test/mochitest.ini
@@ -458,16 +458,20 @@ skip-if = buildapp == 'b2g' || e10s # b2
 [test_iframe_sandbox_redirect.html]
 [test_iframe_sandbox_same_origin.html]
 [test_iframe_sandbox_workers.html]
 [test_img_attributes_reflection.html]
 [test_imageSrcSet.html]
 [test_imports_basics.html]
 [test_imports_redirect.html]
 [test_imports_nonhttp.html]
+[test_imports_nested.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
+[test_imports_nested_2.html]
+skip-if = toolkit == 'gonk' # nested imports fail on b2g emulator
 [test_li_attributes_reflection.html]
 [test_link_attributes_reflection.html]
 [test_link_sizes.html]
 [test_map_attributes_reflection.html]
 [test_meta_attributes_reflection.html]
 [test_mod_attributes_reflection.html]
 [test_mozaudiochannel.html]
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(Perma-orange on debug emulator) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_imports_nested.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877072
+-->
+<head>
+  <title>Test for Bug 877072</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877072">Mozilla Bug 877072</a>
+
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+
+  <link rel="import" href="imports/file_importA1.html" id="A1" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="imports/file_importB1.html" id="B1" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="imports/file_importB2.html" id="B2_2" onload="loaded()" onerror="failed()"></link>
+
+  <script type="text/javascript">
+    is(counter, 5, "Imports are loaded");
+    is(fcounter, 0, "No error in imports");
+    var expected = ["A2", "A1", "B2", "B1"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_imports_nested_2.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877072
+-->
+<head>
+  <title>Test for Bug 877072</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877072">Mozilla Bug 877072</a>
+
+  <script type="text/javascript">
+    SimpleTest.waitForExplicitFinish();
+    var counter = 0;
+    var fcounter = 0;
+    var order = [];
+    function loaded() {
+      counter++;
+    }
+    function failed() {
+      fcounter++;
+    }
+  </script>
+
+<!--
+
+Master -> c1 -> ... -> C10
+|  |                    |
+|  - - -> D             |
+|         ^             |
+|         |             |
+ - - - -> E < - - - - - -
+
+This test is for testing ImportLoader::UpdateDependants.Because of the long
+chain to C10, it's very likely that C10->E will be the last edge the ImportLoaders
+find. At that point it won't only have to update E but also its subimport D.
+
+-->
+
+  <link rel="import" href="imports/file_importC1.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="imports/file_importD.html" onload="loaded()" onerror="failed()"></link>
+  <link rel="import" href="imports/file_importE.html" onload="loaded()" onerror="failed()"></link>
+
+  <script type="text/javascript">
+    is(counter, 14, "Imports are loaded"); // 12 imports but 14 link imports...
+    is(fcounter, 0, "No error in imports");
+    var expected = ["D", "E", "C10", "C9", "C8", "C7", "C6", "C5", "C4", "C3", "C2", "C1"];
+    for (i in expected)
+      is(order[i], expected[i], "import " + i + " should be " + expected[i]);
+    SimpleTest.finish();
+  </script>
+</body>
+</html>
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -58,17 +58,17 @@ PRLogModuleInfo* gMediaStreamGraphLog;
 #  endif
 #else
 #  define LIFECYCLE_LOG(...)
 #endif
 
 /**
  * The singleton graph instance.
  */
-static nsDataHashtable<nsUint32HashKey, MediaStreamGraphImpl*> gGraphs;
+static MediaStreamGraphImpl* gGraph;
 
 MediaStreamGraphImpl::~MediaStreamGraphImpl()
 {
   NS_ASSERTION(IsEmpty(),
                "All streams should have been destroyed by messages from the main thread");
   STREAM_LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p destroyed", this));
   LIFECYCLE_LOG("MediaStreamGraphImpl::~MediaStreamGraphImpl\n");
 }
@@ -1628,20 +1628,19 @@ MediaStreamGraphImpl::RunInStableState(b
         // synchronously because it spins the event loop waiting for threads
         // to shut down, and we don't want to do that in a stable state handler.
         mLifecycleState = LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN;
         LIFECYCLE_LOG("Sending MediaStreamGraphShutDownRunnable %p", this);
         nsCOMPtr<nsIRunnable> event = new MediaStreamGraphShutDownRunnable(this );
         NS_DispatchToMainThread(event);
 
         LIFECYCLE_LOG("Disconnecting MediaStreamGraph %p", this);
-        MediaStreamGraphImpl* graph;
-        if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
+        if (this == gGraph) {
           // null out gGraph if that's the graph being shut down
-          gGraphs.Remove(mAudioChannel);
+          gGraph = nullptr;
         }
       }
     } else {
       if (mLifecycleState <= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
         MessageBlock* block = mBackMessageQueue.AppendElement();
         block->mMessages.SwapElements(mCurrentTaskMessageQueue);
         block->mGraphUpdateIndex = mNextGraphUpdateIndex;
         ++mNextGraphUpdateIndex;
@@ -1782,22 +1781,19 @@ MediaStreamGraphImpl::AppendMessage(Cont
 #endif
     aMessage->RunDuringShutdown();
 #ifdef DEBUG
     mCanRunMessagesSynchronously = true;
 #endif
     delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
-
-      MediaStreamGraphImpl* graph;
-      if (gGraphs.Get(mAudioChannel, &graph) && graph == this) {
-        gGraphs.Remove(mAudioChannel);
+      if (gGraph == this) {
+        gGraph = nullptr;
       }
-
       Destroy();
     }
     return;
   }
 
   mCurrentTaskMessageQueue.AppendElement(aMessage);
   EnsureRunInStableState();
 }
@@ -2735,17 +2731,16 @@ MediaStreamGraphImpl::MediaStreamGraphIm
 #endif
   , mMemoryReportMonitor("MSGIMemory")
   , mSelfRef(MOZ_THIS_IN_INITIALIZER_LIST())
   , mAudioStreamSizes()
   , mNeedsMemoryReport(false)
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
-  , mAudioChannel(static_cast<uint32_t>(aChannel))
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   if (mRealtime) {
@@ -2774,65 +2769,50 @@ MediaStreamGraphImpl::Destroy()
   // Clear the self reference which will destroy this instance.
   mSelfRef = nullptr;
 }
 
 NS_IMPL_ISUPPORTS(MediaStreamGraphShutdownObserver, nsIObserver)
 
 static bool gShutdownObserverRegistered = false;
 
-namespace {
-
-PLDHashOperator
-ForceShutdownEnumerator(const uint32_t& /* aAudioChannel */,
-                        MediaStreamGraphImpl* aGraph,
-                        void* /* aUnused */)
-{
-  aGraph->ForceShutDown();
-  return PL_DHASH_NEXT;
-}
-
-} // anonymous namespace
-
 NS_IMETHODIMP
 MediaStreamGraphShutdownObserver::Observe(nsISupports *aSubject,
                                           const char *aTopic,
                                           const char16_t *aData)
 {
   if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
-    gGraphs.EnumerateRead(ForceShutdownEnumerator, nullptr);
+    if (gGraph) {
+      gGraph->ForceShutDown();
+    }
     nsContentUtils::UnregisterShutdownObserver(this);
     gShutdownObserverRegistered = false;
   }
   return NS_OK;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::GetInstance(DOMMediaStream::TrackTypeHints aHint, dom::AudioChannel aChannel)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
-  uint32_t channel = static_cast<uint32_t>(aChannel);
-  MediaStreamGraphImpl* graph = nullptr;
-
-  if (!gGraphs.Get(channel, &graph)) {
+  if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
     CubebUtils::InitPreferredSampleRate();
 
-    graph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
-    gGraphs.Put(channel, graph);
+    gGraph = new MediaStreamGraphImpl(true, CubebUtils::PreferredSampleRate(), aHint, aChannel);
 
-    STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", graph));
+    STREAM_LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
   }
 
-  return graph;
+  return gGraph;
 }
 
 MediaStreamGraph*
 MediaStreamGraph::CreateNonRealtimeInstance(TrackRate aSampleRate)
 {
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false, aSampleRate);
@@ -2993,20 +2973,17 @@ MediaStreamGraph::CreateAudioNodeStream(
   }
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 bool
 MediaStreamGraph::IsNonRealtime() const
 {
-  const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
-  MediaStreamGraphImpl* graph;
-
-  return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl;
+  return this != gGraph;
 }
 
 void
 MediaStreamGraph::StartNonRealtimeProcessing(TrackRate aRate, uint32_t aTicksToProcess)
 {
   NS_ASSERTION(NS_IsMainThread(), "main thread only");
 
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -649,18 +649,16 @@ public:
    * Hold a ref to the Latency logger
    */
   nsRefPtr<AsyncLatencyLogger> mLatencyLog;
   AudioMixer mMixer;
 #ifdef MOZ_WEBRTC
   nsRefPtr<AudioOutputObserver> mFarendObserverRef;
 #endif
 
-  uint32_t AudioChannel() const { return mAudioChannel; }
-
 private:
   virtual ~MediaStreamGraphImpl();
 
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   /**
    * Used to signal that a memory report has been requested.
    */
@@ -684,16 +682,13 @@ private:
 
 #ifdef DEBUG
   /**
    * Used to assert when AppendMessage() runs ControlMessages synchronously.
    */
   bool mCanRunMessagesSynchronously;
 #endif
 
-  // We use uint32_t instead AudioChannel because this is just used as key for
-  // the hashtable gGraphs.
-  uint32_t mAudioChannel;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */
--- a/content/media/webaudio/AudioContext.cpp
+++ b/content/media/webaudio/AudioContext.cpp
@@ -643,16 +643,22 @@ AudioContext::Unmute() const
 }
 
 AudioChannel
 AudioContext::MozAudioChannelType() const
 {
   return mDestination->MozAudioChannelType();
 }
 
+void
+AudioContext::SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv)
+{
+  mDestination->SetMozAudioChannelType(aValue, aRv);
+}
+
 AudioChannel
 AudioContext::TestAudioChannelInAudioNodeStream()
 {
   MediaStream* stream = mDestination->Stream();
   MOZ_ASSERT(stream);
 
   return stream->AudioChannelType();
 }
--- a/content/media/webaudio/AudioContext.h
+++ b/content/media/webaudio/AudioContext.h
@@ -217,16 +217,17 @@ public:
   uint32_t MaxChannelCount() const;
 
   void Mute() const;
   void Unmute() const;
 
   JSObject* GetGlobalJSObject() const;
 
   AudioChannel MozAudioChannelType() const;
+  void SetMozAudioChannelType(AudioChannel aValue, ErrorResult& aRv);
 
   AudioChannel TestAudioChannelInAudioNodeStream();
 
   void UpdateNodeCount(int32_t aDelta);
 
   double DOMTimeToStreamTime(double aTime) const
   {
     return aTime - ExtraCurrentTime();
--- a/content/media/webaudio/test/test_mozaudiochannel.html
+++ b/content/media/webaudio/test/test_mozaudiochannel.html
@@ -13,33 +13,37 @@
 
 function test_basic() {
   var ac = new AudioContext();
   ok(ac, "AudioContext created");
 
   // Default
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  // Unpermitted channels
-  ac = new AudioContext("content");
+  // random wrong channel
+  ac.mozAudioChannelType = "foo";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac = new AudioContext("notification");
+  // Unpermitted channels
+  ac.mozAudioChannelType = "content";
+  is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
+
+  ac.mozAudioChannelType = "notification";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac = new AudioContext("alarm");
+  ac.mozAudioChannelType = "alarm";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac = new AudioContext("telephony");
+  ac.mozAudioChannelType = "telephony";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac = new AudioContext("ringer");
+  ac.mozAudioChannelType = "ringer";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
-  ac = new AudioContext("publicnotification");
+  ac.mozAudioChannelType = "publicnotification";
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
   runTest();
 }
 
 function test_permission(aChannel) {
   var ac = new AudioContext();
   ok(ac, "AudioContext created");
@@ -47,17 +51,17 @@ function test_permission(aChannel) {
   is(ac.mozAudioChannelType, "normal", "Default ac channel == 'normal'");
 
   var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream();
   is(channel, "normal", "AudioNodeStream is using the correct default audio channel.");
 
   SpecialPowers.pushPermissions(
     [{ "type": "audio-channel-" + aChannel, "allow": true, "context": document }],
     function() {
-      var ac = new AudioContext(aChannel);
+      ac.mozAudioChannelType = aChannel;
       is(ac.mozAudioChannelType, aChannel, "Default ac channel == '" + aChannel + "'");
 
       var channel = SpecialPowers.wrap(ac).testAudioChannelInAudioNodeStream();
       is(channel, aChannel, "AudioNodeStream is using the correct new audio channel.");
 
       runTest();
     }
   );
--- a/dom/base/URLSearchParams.cpp
+++ b/dom/base/URLSearchParams.cpp
@@ -46,17 +46,17 @@ URLSearchParams::Constructor(const Globa
 }
 
 /* static */ already_AddRefed<URLSearchParams>
 URLSearchParams::Constructor(const GlobalObject& aGlobal,
                              URLSearchParams& aInit,
                              ErrorResult& aRv)
 {
   nsRefPtr<URLSearchParams> sp = new URLSearchParams();
-  aInit.mSearchParams.EnumerateRead(CopyEnumerator, sp);
+  sp->mSearchParams = aInit.mSearchParams;
   return sp.forget();
 }
 
 void
 URLSearchParams::ParseInput(const nsACString& aInput,
                             URLSearchParamsObserver* aObserver)
 {
   // Remove all the existing data before parsing a new input.
@@ -202,30 +202,16 @@ URLSearchParams::ConvertString(const nsA
     return;
   }
 
   if (newOutputLength < outputLength) {
     aOutput.Truncate(newOutputLength);
   }
 }
 
-/* static */ PLDHashOperator
-URLSearchParams::CopyEnumerator(const nsAString& aName,
-                                nsTArray<nsString>* aArray,
-                                void *userData)
-{
-  URLSearchParams* aSearchParams = static_cast<URLSearchParams*>(userData);
-
-  nsTArray<nsString>* newArray = new nsTArray<nsString>();
-  newArray->AppendElements(*aArray);
-
-  aSearchParams->mSearchParams.Put(aName, newArray);
-  return PL_DHASH_NEXT;
-}
-
 void
 URLSearchParams::AddObserver(URLSearchParamsObserver* aObserver)
 {
   MOZ_ASSERT(aObserver);
   MOZ_ASSERT(!mObservers.Contains(aObserver));
   mObservers.AppendElement(aObserver);
 }
 
@@ -240,156 +226,153 @@ void
 URLSearchParams::RemoveObservers()
 {
   mObservers.Clear();
 }
 
 void
 URLSearchParams::Get(const nsAString& aName, nsString& aRetval)
 {
-  nsTArray<nsString>* array;
-  if (!mSearchParams.Get(aName, &array)) {
-    aRetval.Truncate();
-    return;
+  aRetval.Truncate();
+
+  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+    if (mSearchParams[i].mKey.Equals(aName)) {
+      aRetval.Assign(mSearchParams[i].mValue);
+      break;
+    }
   }
-
-  aRetval.Assign(array->ElementAt(0));
 }
 
 void
 URLSearchParams::GetAll(const nsAString& aName, nsTArray<nsString>& aRetval)
 {
-  nsTArray<nsString>* array;
-  if (!mSearchParams.Get(aName, &array)) {
-    return;
+  aRetval.Clear();
+
+  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+    if (mSearchParams[i].mKey.Equals(aName)) {
+      aRetval.AppendElement(mSearchParams[i].mValue);
+    }
   }
-
-  aRetval.AppendElements(*array);
 }
 
 void
 URLSearchParams::Set(const nsAString& aName, const nsAString& aValue)
 {
-  nsTArray<nsString>* array;
-  if (!mSearchParams.Get(aName, &array)) {
-    array = new nsTArray<nsString>();
-    array->AppendElement(aValue);
-    mSearchParams.Put(aName, array);
-  } else {
-    array->ElementAt(0) = aValue;
+  Param* param = nullptr;
+  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+    if (mSearchParams[i].mKey.Equals(aName)) {
+      param = &mSearchParams[i];
+      break;
+    }
   }
 
+  if (!param) {
+    param = mSearchParams.AppendElement();
+    param->mKey = aName;
+  }
+
+  param->mValue = aValue;
+
   NotifyObservers(nullptr);
 }
 
 void
 URLSearchParams::Append(const nsAString& aName, const nsAString& aValue)
 {
   AppendInternal(aName, aValue);
   NotifyObservers(nullptr);
 }
 
 void
 URLSearchParams::AppendInternal(const nsAString& aName, const nsAString& aValue)
 {
-  nsTArray<nsString>* array;
-  if (!mSearchParams.Get(aName, &array)) {
-    array = new nsTArray<nsString>();
-    mSearchParams.Put(aName, array);
-  }
-
-  array->AppendElement(aValue);
+  Param* param = mSearchParams.AppendElement();
+  param->mKey = aName;
+  param->mValue = aValue;
 }
 
 bool
 URLSearchParams::Has(const nsAString& aName)
 {
-  return mSearchParams.Get(aName, nullptr);
+  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+    if (mSearchParams[i].mKey.Equals(aName)) {
+      return true;
+    }
+  }
+
+  return false;
 }
 
 void
 URLSearchParams::Delete(const nsAString& aName)
 {
-  nsTArray<nsString>* array;
-  if (!mSearchParams.Get(aName, &array)) {
-    return;
+  bool found = false;
+  for (uint32_t i = 0; i < mSearchParams.Length();) {
+    if (mSearchParams[i].mKey.Equals(aName)) {
+      mSearchParams.RemoveElementAt(i);
+      found = true;
+    } else {
+      ++i;
+    }
   }
 
-  mSearchParams.Remove(aName);
-
-  NotifyObservers(nullptr);
+  if (found) {
+    NotifyObservers(nullptr);
+  }
 }
 
 void
 URLSearchParams::DeleteAll()
 {
   mSearchParams.Clear();
 }
 
-class MOZ_STACK_CLASS SerializeData
-{
-public:
-  SerializeData()
-    : mFirst(true)
-  {}
+namespace {
 
-  nsAutoString mValue;
-  bool mFirst;
-
-  void Serialize(const nsCString& aInput)
-  {
-    const unsigned char* p = (const unsigned char*) aInput.get();
+void SerializeString(const nsCString& aInput, nsAString& aValue)
+{
+  const unsigned char* p = (const unsigned char*) aInput.get();
 
-    while (p && *p) {
-      // ' ' to '+'
-      if (*p == 0x20) {
-        mValue.Append(0x2B);
-      // Percent Encode algorithm
-      } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
-                 (*p >= 0x30 && *p <= 0x39) ||
-                 (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
-                 (*p >= 0x61 && *p <= 0x7A)) {
-        mValue.Append(*p);
-      } else {
-        mValue.AppendPrintf("%%%.2X", *p);
-      }
+  while (p && *p) {
+    // ' ' to '+'
+    if (*p == 0x20) {
+      aValue.Append(0x2B);
+    // Percent Encode algorithm
+    } else if (*p == 0x2A || *p == 0x2D || *p == 0x2E ||
+               (*p >= 0x30 && *p <= 0x39) ||
+               (*p >= 0x41 && *p <= 0x5A) || *p == 0x5F ||
+               (*p >= 0x61 && *p <= 0x7A)) {
+      aValue.Append(*p);
+    } else {
+      aValue.AppendPrintf("%%%.2X", *p);
+    }
 
-      ++p;
-    }
+    ++p;
   }
-};
+}
+
+} // anonymous namespace
 
 void
 URLSearchParams::Serialize(nsAString& aValue) const
 {
-  SerializeData data;
-  mSearchParams.EnumerateRead(SerializeEnumerator, &data);
-  aValue.Assign(data.mValue);
-}
+  aValue.Truncate();
+  bool first = true;
 
-/* static */ PLDHashOperator
-URLSearchParams::SerializeEnumerator(const nsAString& aName,
-                                     nsTArray<nsString>* aArray,
-                                     void *userData)
-{
-  SerializeData* data = static_cast<SerializeData*>(userData);
-
-  for (uint32_t i = 0, len = aArray->Length(); i < len; ++i) {
-    if (data->mFirst) {
-      data->mFirst = false;
+  for (uint32_t i = 0, len = mSearchParams.Length(); i < len; ++i) {
+    if (first) {
+      first = false;
     } else {
-      data->mValue.Append('&');
+      aValue.Append('&');
     }
 
-    data->Serialize(NS_ConvertUTF16toUTF8(aName));
-    data->mValue.Append('=');
-    data->Serialize(NS_ConvertUTF16toUTF8(aArray->ElementAt(i)));
+    SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mKey), aValue);
+    aValue.Append('=');
+    SerializeString(NS_ConvertUTF16toUTF8(mSearchParams[i].mValue), aValue);
   }
-
-  return PL_DHASH_NEXT;
 }
 
 void
 URLSearchParams::NotifyObservers(URLSearchParamsObserver* aExceptObserver)
 {
   for (uint32_t i = 0; i < mObservers.Length(); ++i) {
     if (mObservers[i] != aExceptObserver) {
       mObservers[i]->URLSearchParamsUpdated(this);
--- a/dom/base/URLSearchParams.h
+++ b/dom/base/URLSearchParams.h
@@ -5,18 +5,16 @@
 
 #ifndef mozilla_dom_URLSearchParams_h
 #define mozilla_dom_URLSearchParams_h
 
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/ErrorResult.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
-#include "nsClassHashtable.h"
-#include "nsHashKeys.h"
 #include "nsISupports.h"
 #include "nsIUnicodeDecoder.h"
 
 namespace mozilla {
 namespace dom {
 
 class URLSearchParams;
 
@@ -87,25 +85,23 @@ private:
 
   void DeleteAll();
 
   void DecodeString(const nsACString& aInput, nsAString& aOutput);
   void ConvertString(const nsACString& aInput, nsAString& aOutput);
 
   void NotifyObservers(URLSearchParamsObserver* aExceptObserver);
 
-  static PLDHashOperator
-  CopyEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
-                 void *userData);
+  struct Param
+  {
+    nsString mKey;
+    nsString mValue;
+  };
 
-  static PLDHashOperator
-  SerializeEnumerator(const nsAString& aName, nsTArray<nsString>* aArray,
-                      void *userData);
-
-  nsClassHashtable<nsStringHashKey, nsTArray<nsString>> mSearchParams;
+  nsTArray<Param> mSearchParams;
 
   nsTArray<nsRefPtr<URLSearchParamsObserver>> mObservers;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/test/test_urlSearchParams.html
+++ b/dom/base/test/test_urlSearchParams.html
@@ -243,25 +243,54 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(a.searchParams.get('bar'), 'foo', "a has bar=foo");
     is(b.searchParams.get('bar'), 'foo', "b has bar=foo");
     is(c.searchParams.get('bar'), 'foo', "c has bar=foo");
     is(d.searchParams.get('bar'), 'foo', "d has bar=foo");
 
     runTest();
   }
 
+  function testOrdering() {
+    var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
+    is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
+    is(a.getAll('a').length, 3, "Correct length of getAll()");
+
+    var b = new URLSearchParams();
+    b.append('a', '1');
+    b.append('b', '2');
+    b.append('a', '3');
+    is(b.toString(), "a=1&b=2&a=3", "Order is correct");
+    is(b.getAll('a').length, 2, "Correct length of getAll()");
+
+    runTest();
+  }
+
+  function testDelete() {
+    var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
+    is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
+    is(a.getAll('a').length, 3, "Correct length of getAll()");
+
+    a.delete('a');
+    is(a.getAll('a').length, 0, "Correct length of getAll()");
+    is(a.toString(), "b=3&c=4&c=5", "Order is correct");
+
+    runTest();
+  }
+
   var tests = [
     testSimpleURLSearchParams,
     testCopyURLSearchParams,
     testParserURLSearchParams,
     testURL,
     function() { testElement(document.getElementById('anchor')) },
     function() { testElement(document.getElementById('area')) },
     testEncoding,
-    testMultiURL
+    testMultiURL,
+    testOrdering,
+    testDelete
   ];
 
   function runTest() {
     if (!tests.length) {
       SimpleTest.finish();
       return;
     }
 
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -14,16 +14,35 @@
 #include "nsString.h"
 
 #define DOM_PROXY_OBJECT_SLOT js::PROXY_PRIVATE_SLOT
 
 namespace mozilla {
 namespace dom {
 
 enum {
+  /**
+   * DOM proxies have an extra slot for the expando object at index
+   * JSPROXYSLOT_EXPANDO.
+   *
+   * The expando object is a plain JSObject whose properties correspond to
+   * "expandos" (custom properties set by the script author).
+   *
+   * The exact value stored in the JSPROXYSLOT_EXPANDO slot depends on whether
+   * the interface is annotated with the [OverrideBuiltins] extended attribute.
+   *
+   * If it is, the proxy is initialized with a PrivateValue, which contains a
+   * pointer to a js::ExpandoAndGeneration object; this contains a pointer to
+   * the actual expando object as well as the "generation" of the object.
+   *
+   * If it is not, the proxy is initialized with an UndefinedValue. In
+   * EnsureExpandoObject, it is set to an ObjectValue that points to the
+   * expando object directly. (It is set back to an UndefinedValue only when
+   * the object is about to die.)
+   */
   JSPROXYSLOT_EXPANDO = 0
 };
 
 template<typename T> struct Prefable;
 
 class BaseDOMProxyHandler : public js::BaseProxyHandler
 {
 public:
--- a/dom/ipc/PScreenManager.ipdl
+++ b/dom/ipc/PScreenManager.ipdl
@@ -9,17 +9,19 @@ include protocol PContent;
 using struct nsIntRect from "nsRect.h";
 
 namespace mozilla {
 namespace dom {
 
 struct ScreenDetails {
   uint32_t id;
   nsIntRect rect;
+  nsIntRect rectDisplayPix;
   nsIntRect availRect;
+  nsIntRect availRectDisplayPix;
   int32_t pixelDepth;
   int32_t colorDepth;
   double contentsScaleFactor;
 };
 
 rpc protocol PScreenManager
 {
   manager PContent;
--- a/dom/ipc/ScreenManagerParent.cpp
+++ b/dom/ipc/ScreenManagerParent.cpp
@@ -156,22 +156,36 @@ ScreenManagerParent::ExtractScreenDetail
   NS_ENSURE_SUCCESS(rv, false);
   aDetails.id() = id;
 
   nsIntRect rect;
   rv = aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
   NS_ENSURE_SUCCESS(rv, false);
   aDetails.rect() = rect;
 
+  nsIntRect rectDisplayPix;
+  rv = aScreen->GetRectDisplayPix(&rectDisplayPix.x, &rectDisplayPix.y,
+                                  &rectDisplayPix.width, &rectDisplayPix.height);
+  NS_ENSURE_SUCCESS(rv, false);
+  aDetails.rectDisplayPix() = rectDisplayPix;
+
   nsIntRect availRect;
   rv = aScreen->GetAvailRect(&availRect.x, &availRect.y, &availRect.width,
                              &availRect.height);
   NS_ENSURE_SUCCESS(rv, false);
   aDetails.availRect() = availRect;
 
+  nsIntRect availRectDisplayPix;
+  rv = aScreen->GetAvailRectDisplayPix(&availRectDisplayPix.x,
+                                       &availRectDisplayPix.y,
+                                       &availRectDisplayPix.width,
+                                       &availRectDisplayPix.height);
+  NS_ENSURE_SUCCESS(rv, false);
+  aDetails.availRectDisplayPix() = availRectDisplayPix;
+
   int32_t pixelDepth = 0;
   rv = aScreen->GetPixelDepth(&pixelDepth);
   NS_ENSURE_SUCCESS(rv, false);
   aDetails.pixelDepth() = pixelDepth;
 
   int32_t colorDepth = 0;
   rv = aScreen->GetColorDepth(&colorDepth);
   NS_ENSURE_SUCCESS(rv, false);
--- a/dom/ipc/tests/chrome.ini
+++ b/dom/ipc/tests/chrome.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 support-files =
   process_error.xul
   process_error_contentscript.js
 
 [test_process_error.xul]
-skip-if = buildapp == "mulet"
+skip-if = buildapp == "mulet" || !crashreporter
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -15,49 +15,34 @@ CSPViolationWithURI = The page's setting
 CSPROViolation = A violation occurred for a report-only CSP policy ("%1$S"). The behavior was allowed, and a CSP report was sent.
 # LOCALIZATION NOTE (CSPROViolationWithURI):
 # %1$S is the directive that has been violated.
 # %2$S is the URI of the resource which violated the directive.
 CSPROViolationWithURI = The page's settings observed the loading of a resource at %2$S ("%1$S"). A CSP report is being sent.
 # LOCALIZATION NOTE (triedToSendReport):
 # %1$S is the URI we attempted to send a report to.
 triedToSendReport = Tried to send report to invalid URI: "%1$S"
-# LOCALIZATION NOTE (errorWas):
-# %1$S is the error resulting from attempting to send the report
-errorWas = error was: "%1$S"
 # LOCALIZATION NOTE (couldNotParseReportURI):
 # %1$S is the report URI that could not be parsed
 couldNotParseReportURI = couldn't parse report URI: %1$S
 # LOCALIZATION NOTE (couldNotProcessUnknownDirective):
 # %1$S is the unknown directive
 couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
 # LOCALIZATION NOTE (ignoringUnknownOption):
 # %1$S is the option that could not be understood
 ignoringUnknownOption = Ignoring unknown option %1$S
 # LOCALIZATION NOTE (reportURInotHttpsOrHttp2):
 # %1$S is the ETLD of the report URI that is not HTTP or HTTPS
 reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI.
 # LOCALIZATION NOTE (reportURInotInReportOnlyHeader):
 # %1$S is the ETLD of the page with the policy
 reportURInotInReportOnlyHeader = This site (%1$S) has a Report-Only policy without a report URI. CSP will not block and cannot report violations of this policy.
-# LOCALIZATION NOTE (pageCannotSendReportsTo):
-# %1$S is the URI of the page with the policy
-# %2$S is the report URI that could not be used
-pageCannotSendReportsTo = page on %1$S cannot send reports to %2$S
-allowOrDefaultSrcRequired = 'allow' or 'default-src' directive required but not present.  Reverting to "default-src 'none'"
 # LOCALIZATION NOTE (failedToParseUnrecognizedSource):
 # %1$S is the CSP Source that could not be parsed
 failedToParseUnrecognizedSource = Failed to parse unrecognized source %1$S
-# LOCALIZATION NOTE (reportPostRedirect):
-# %1$S is the specified report URI before redirect
-reportPostRedirect = Post of violation report to %1$S failed, as a redirect occurred
-# LOCALIZATION NOTE (allowDirectiveIsDeprecated):
-# Don't translate "allow" and "default-src" as they are keywords and part of
-# the CSP protocol syntax.
-allowDirectiveIsDeprecated = allow directive is deprecated, use the equivalent default-src directive instead
 # LOCALIZATION NOTE (inlineScriptBlocked):
 # inline script refers to JavaScript code that is embedded into the HTML document.
 inlineScriptBlocked = An attempt to execute inline scripts has been blocked
 # LOCALIZATION NOTE (inlineStyleBlocked):
 # inline style refers to CSS code that is embedded into the HTML document.
 inlineStyleBlocked = An attempt to apply inline style sheets has been blocked
 # LOCALIZATION NOTE (scriptFromStringBlocked):
 # eval is a name and should not be localized.
@@ -65,68 +50,23 @@ scriptFromStringBlocked = An attempt to 
 # LOCALIZATION NOTE (hostNameMightBeKeyword):
 # %1$S is the hostname in question and %2$S is the keyword
 hostNameMightBeKeyword = Interpreting %1$S as a hostname, not a keyword. If you intended this to be a keyword, use '%2$S' (wrapped in single quotes).
 # LOCALIZATION NOTE (notSupportingDirective):
 # directive is not supported (e.g. 'reflected-xss')
 notSupportingDirective = Not supporting directive '%1$S'. Directive and values will be ignored.
 
 # CSP Errors:
-policyURINotAlone = policy-uri directive can only appear alone
-noParentRequest = The policy-uri cannot be fetched without a parent request and a CSP.
-# LOCALIZATION NOTE (policyURIParseError):
-# %1$S is the URI that could not be parsed
-policyURIParseError = could not parse URI in policy URI: %1$S
-# LOCALIZATION NOTE (nonMatchingHost):
-# %1$S is the URI host that does not match
-nonMatchingHost = can't fetch policy uri from non-matching hostname: %1$S
-# LOCALIZATION NOTE (nonMatchingPort):
-# %1$S is the URI port that does not match
-nonMatchingPort = can't fetch policy uri from non-matching port: %1$S
-# LOCALIZATION NOTE (nonMatchingScheme):
-# %1$S is the URI scheme that does not match
-nonMatchingScheme = can't fetch policy uri from non-matching scheme: %1$S
-# LOCALIZATION NOTE (errorFetchingPolicy):
-# %1$S is the error that caused fetching to fail
-errorFetchingPolicy = Error fetching policy-uri: %1$S
-cspSourceNotURI = Provided argument is not an nsIURI
-argumentIsNotString = Provided argument is not a string
-selfDataNotProvided = Can't use 'self' if self data is not provided
-# LOCALIZATION NOTE (uriWithoutScheme):
-# %1$S is the URI without a scheme
-uriWithoutScheme = can't parse a URI without a scheme: %1$S
-selfKeywordNoSelfData = self keyword used, but no self data specified
 # LOCALIZATION NOTE (couldntParseInvalidSource):
 # %1$S is the source that could not be parsed
 couldntParseInvalidSource = Couldn't parse invalid source %1$S
-# LOCALIZATION NOTE (hostSourceWithoutData):
-# %1$S is the source
-hostSourceWithoutData = Can't create host-only source %1$S without 'self' data
-# LOCALIZATION NOTE (sourceWithoutData):
-# %1$S is the source
-sourceWithoutData = Can't create source %1$S without 'self' data
 # LOCALIZATION NOTE (couldntParseInvalidHost):
 # %1$S is the host that's invalid
 couldntParseInvalidHost = Couldn't parse invalid host %1$S
 # LOCALIZATION NOTE (couldntParseScheme):
 # %1$S is the string source
 couldntParseScheme = Couldn't parse scheme in %1$S
 # LOCALIZATION NOTE (couldntParsePort):
 # %1$S is the string source
 couldntParsePort = Couldn't parse port in %1$S
-# LOCALIZATION NOTE (notIntersectPort):
-# %1$S is one source we tried to intersect
-# %2$S is the other
-notIntersectPort = Could not intersect %1$S with %2$S due to port problems.
-# LOCALIZATION NOTE (notIntersectScheme):
-# %1$S is one source we tried to intersect
-# %2$S is the other
-notIntersectScheme = Could not intersect %1$S with %2$S due to scheme problems.
-# LOCALIZATION NOTE (intersectingSourceWithUndefinedHost):
-# %1$S is the source
-intersectingSourceWithUndefinedHost = intersecting source with undefined host: %1$S
-# LOCALIZATION NOTE (intersectingSourcesWithUndefinedHosts):
-# %1$S is the first source
-# %2$S is the second source
-intersectingSourcesWithUndefinedHosts = intersecting two sources with undefined hosts: %1$S and %2$S
 # LOCALIZATION NOTE (duplicateDirective):
 # %1$S is the name of the duplicate directive
 duplicateDirective = Duplicate %1$S directives detected.  All but the first instance will be ignored.
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -73,18 +73,18 @@ interface AudioContext : EventTarget {
     [NewObject, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
 // Mozilla extensions
 partial interface AudioContext {
   // Read AudioChannel.webidl for more information about this attribute.
-  [Pref="media.useAudioChannelService"]
-  readonly attribute AudioChannel mozAudioChannelType;
+  [Pref="media.useAudioChannelService", SetterThrows]
+  attribute AudioChannel mozAudioChannelType;
 
   // These 2 events are dispatched when the AudioContext object is muted by
   // the AudioChannelService. It's call 'interrupt' because when this event is
   // dispatched on a HTMLMediaElement, the audio stream is paused.
   [Pref="media.useAudioChannelService"]
   attribute EventHandler onmozinterruptbegin;
 
   [Pref="media.useAudioChannelService"]
--- a/dom/xslt/tests/mochitest/mochitest.ini
+++ b/dom/xslt/tests/mochitest/mochitest.ini
@@ -1,10 +1,11 @@
 [DEFAULT]
 
+[test_bug1072116.html]
 [test_bug319374.xhtml]
 [test_bug427060.html]
 [test_bug440974.html]
 [test_bug453441.html]
 [test_bug468208.html]
 [test_bug511487.html]
 [test_bug551412.html]
 [test_bug551654.html]
new file mode 100644
--- /dev/null
+++ b/dom/xslt/tests/mochitest/test_bug1072116.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1072116
+-->
+<head>
+  <title>Test for Bug 1072116</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1072116">Mozilla Bug 1072116</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1072116 **/
+var attr = document.createAttribute("c");
+
+var xpathExpr = document.createExpression('a', null);
+
+var status = false;
+try {
+  xpathExpr.evaluate(attr, null, null, null, null);
+} catch(e) {
+  status = true;
+}
+
+ok(status, "Still alive \\o/");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/xslt/xpath/XPathExpression.cpp
+++ b/dom/xslt/xpath/XPathExpression.cpp
@@ -140,16 +140,21 @@ XPathExpression::EvaluateWithContext(nsI
              nodeType != nsIDOMNode::ATTRIBUTE_NODE &&
              nodeType != nsIDOMNode::COMMENT_NODE &&
              nodeType != nsIDOMNode::PROCESSING_INSTRUCTION_NODE) {
         aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
         return nullptr;
     }
 
     nsAutoPtr<txXPathNode> contextNode(txXPathNativeNode::createXPathNode(&aContextNode));
+    if (!contextNode) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
     EvalContextImpl eContext(*contextNode, aContextPosition, aContextSize,
                              mRecycler);
     nsRefPtr<txAExprResult> exprResult;
     aRv = mExpression->evaluate(&eContext, getter_AddRefs(exprResult));
     if (aRv.Failed()) {
         return nullptr;
     }
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -1205,26 +1205,26 @@ DrawTargetD2D1::CreateBrushForPattern(co
       static_cast<const SurfacePattern*>(&aPattern);
 
     if (!pat->mSurface) {
       gfxDebug() << "No source surface specified for surface pattern";
       return CreateTransparentBlackBrush();
     }
 
     D2D1_RECT_F samplingBounds;
+    Matrix mat = pat->mMatrix;
     if (!pat->mSamplingRect.IsEmpty()) {
       samplingBounds = D2DRect(pat->mSamplingRect);
+      mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
     } else {
       samplingBounds = D2D1::RectF(0, 0,
                                    Float(pat->mSurface->GetSize().width),
                                    Float(pat->mSurface->GetSize().height));
     }
 
-    Matrix mat = pat->mMatrix;
-    
     RefPtr<ID2D1ImageBrush> imageBrush;
     RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode);
     mDC->CreateImageBrush(image,
                           D2D1::ImageBrushProperties(samplingBounds,
                                                      D2DExtend(pat->mExtendMode),
                                                      D2DExtend(pat->mExtendMode),
                                                      D2DInterpolationMode(pat->mFilter)),
                           D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
--- a/gfx/2d/Matrix.h
+++ b/gfx/2d/Matrix.h
@@ -6,16 +6,17 @@
 #ifndef MOZILLA_GFX_MATRIX_H_
 #define MOZILLA_GFX_MATRIX_H_
 
 #include "Types.h"
 #include "Rect.h"
 #include "Point.h"
 #include <math.h>
 #include "mozilla/Attributes.h"
+#include "mozilla/DebugOnly.h"
 
 namespace mozilla {
 namespace gfx {
 
 static bool FuzzyEqual(Float aV1, Float aV2) {
   // XXX - Check if fabs does the smart thing and just negates the sign bit.
   return fabs(aV2 - aV1) < 1e-6;
 }
@@ -181,16 +182,24 @@ public:
     _21 = inv_det * B;
     _22 = inv_det * E;
     _31 = inv_det * C;
     _32 = inv_det * F;
 
     return true;
   }
 
+  Matrix Inverse() const
+  {
+    Matrix clone = *this;
+    DebugOnly<bool> inverted = clone.Invert();
+    MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+    return clone;
+  }
+
   Float Determinant() const
   {
     return _11 * _22 - _12 * _21;
   }
 
   Matrix operator*(const Matrix &aMatrix) const
   {
     Matrix resultMatrix;
@@ -666,16 +675,24 @@ public:
          + _13 * _21 * _32 * _44
          - _11 * _23 * _32 * _44
          - _12 * _21 * _33 * _44
          + _11 * _22 * _33 * _44;
   }
 
   bool Invert();
 
+  Matrix4x4 Inverse() const
+  {
+    Matrix4x4 clone = *this;
+    DebugOnly<bool> inverted = clone.Invert();
+    MOZ_ASSERT(inverted, "Attempted to get the inverse of a non-invertible matrix");
+    return clone;
+  }
+
   void Normalize()
   {
       for (int i = 0; i < 4; i++) {
           for (int j = 0; j < 4; j++) {
               (*this)[i][j] /= (*this)[3][3];
          }
       }
   }
--- a/gfx/2d/ScaledFontDWrite.cpp
+++ b/gfx/2d/ScaledFontDWrite.cpp
@@ -321,17 +321,17 @@ ScaledFontDWrite::GetPathForGlyphs(const
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
 
   return pathBuilder->Finish();
 }
 
 void
 ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint)
 {
-  if (aBackendType != BackendType::DIRECT2D) {
+  if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) {
     ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint);
     return;
   }
 
   PathBuilderD2D *pathBuilderD2D =
     static_cast<PathBuilderD2D*>(aBuilder);
 
   CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -897,27 +897,23 @@ APZCTreeManager::ClearTree()
  * @param aEndPoint the end point of the displacement
  */
 static void
 TransformDisplacement(APZCTreeManager* aTreeManager,
                       AsyncPanZoomController* aSource,
                       AsyncPanZoomController* aTarget,
                       ScreenPoint& aStartPoint,
                       ScreenPoint& aEndPoint) {
-  Matrix4x4 transformToApzc;
-
   // Convert start and end points to untransformed screen coordinates.
-  transformToApzc = aTreeManager->GetScreenToApzcTransform(aSource);
-  Matrix4x4 untransformToApzc = transformToApzc;
-  untransformToApzc.Invert();
+  Matrix4x4 untransformToApzc = aTreeManager->GetScreenToApzcTransform(aSource).Inverse();
   ApplyTransform(&aStartPoint, untransformToApzc);
   ApplyTransform(&aEndPoint, untransformToApzc);
 
   // Convert start and end points to aTarget's transformed screen coordinates.
-  transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
+  Matrix4x4 transformToApzc = aTreeManager->GetScreenToApzcTransform(aTarget);
   ApplyTransform(&aStartPoint, transformToApzc);
   ApplyTransform(&aEndPoint, transformToApzc);
 }
 
 bool
 APZCTreeManager::DispatchScroll(AsyncPanZoomController* aPrev,
                                 ScreenPoint aStartPoint,
                                 ScreenPoint aEndPoint,
@@ -1187,34 +1183,31 @@ APZCTreeManager::GetAPZCAtPoint(AsyncPan
   // explained in the comment above GetScreenToApzcTransform. This function will recurse with aApzc at L and P, and the
   // comments explain what values are stored in the variables at these two levels. All the comments
   // use standard matrix notation where the leftmost matrix in a multiplication is applied first.
 
   // ancestorUntransform takes points from aApzc's parent APZC's CSS-transformed layer coordinates
   // to aApzc's parent layer's layer coordinates.
   // It is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() at recursion level for L,
   //   and RC.Inverse() * QC.Inverse()                               at recursion level for P.
-  Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform();
-  ancestorUntransform.Invert();
+  Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
 
   // Hit testing for this layer takes place in our parent layer coordinates,
   // since the composition bounds (used to initialize the visible rect against
   // which we hit test are in those coordinates).
   Point4D hitTestPointForThisLayer = ancestorUntransform.ProjectPoint(aHitTestPoint);
   APZCTM_LOG("Untransformed %f %f to transient coordinates %f %f for hit-testing APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
            hitTestPointForThisLayer.x, hitTestPointForThisLayer.y, aApzc);
 
   // childUntransform takes points from aApzc's parent APZC's CSS-transformed layer coordinates
   // to aApzc's CSS-transformed layer coordinates.
   // It is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse() * LA.Inverse() at L
   //   and RC.Inverse() * QC.Inverse() * PA.Inverse()                               at P.
-  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransform();
-  asyncUntransform.Invert();
-  Matrix4x4 childUntransform = ancestorUntransform * asyncUntransform;
+  Matrix4x4 childUntransform = ancestorUntransform * Matrix4x4(aApzc->GetCurrentAsyncTransform()).Inverse();
   Point4D hitTestPointForChildLayers = childUntransform.ProjectPoint(aHitTestPoint);
   APZCTM_LOG("Untransformed %f %f to layer coordinates %f %f for APZC %p\n",
            aHitTestPoint.x, aHitTestPoint.y,
            hitTestPointForChildLayers.x, hitTestPointForChildLayers.y, aApzc);
 
   AsyncPanZoomController* result = nullptr;
   // This walks the tree in depth-first, reverse order, so that it encounters
   // APZCs front-to-back on the screen.
@@ -1351,29 +1344,26 @@ APZCTreeManager::GetScreenToApzcTransfor
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // ancestorUntransform is PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
-  Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform();
-  ancestorUntransform.Invert();
+  Matrix4x4 ancestorUntransform = aApzc->GetAncestorTransform().Inverse();
 
   // result is initialized to PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
   result = ancestorUntransform;
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // ancestorUntransform is updated to RC.Inverse() * QC.Inverse() when parent == P
-    ancestorUntransform = parent->GetAncestorTransform();
-    ancestorUntransform.Invert();
+    ancestorUntransform = parent->GetAncestorTransform().Inverse();
     // asyncUntransform is updated to PA.Inverse() when parent == P
-    Matrix4x4 asyncUntransform = parent->GetCurrentAsyncTransform();
-    asyncUntransform.Invert();
+    Matrix4x4 asyncUntransform = Matrix4x4(parent->GetCurrentAsyncTransform()).Inverse();
     // untransformSinceLastApzc is RC.Inverse() * QC.Inverse() * PA.Inverse()
     Matrix4x4 untransformSinceLastApzc = ancestorUntransform * asyncUntransform;
 
     // result is RC.Inverse() * QC.Inverse() * PA.Inverse() * PC.Inverse() * OC.Inverse() * NC.Inverse() * MC.Inverse()
     result = untransformSinceLastApzc * result;
 
     // The above value for result when parent == P matches the required output
     // as explained in the comment above this method. Note that any missing
@@ -1395,18 +1385,17 @@ APZCTreeManager::GetApzcToGeckoTransform
 
   // The comments below assume there is a chain of layers L..R with L and P having APZC instances as
   // explained in the comment above. This function is called with aApzc at L, and the loop
   // below performs one iteration, where parent is at P. The comments explain what values are stored
   // in the variables at these two levels. All the comments use standard matrix notation where the
   // leftmost matrix in a multiplication is applied first.
 
   // asyncUntransform is LA.Inverse()
-  Matrix4x4 asyncUntransform = aApzc->GetCurrentAsyncTransform();
-  asyncUntransform.Invert();
+  Matrix4x4 asyncUntransform = Matrix4x4(aApzc->GetCurrentAsyncTransform()).Inverse();
 
   // aTransformToGeckoOut is initialized to LA.Inverse() * LD * MC * NC * OC * PC
   result = asyncUntransform * aApzc->GetTransformToLastDispatchedPaint() * aApzc->GetAncestorTransform();
 
   for (AsyncPanZoomController* parent = aApzc->GetParent(); parent; parent = parent->GetParent()) {
     // aTransformToGeckoOut is LA.Inverse() * LD * MC * NC * OC * PC * PD * QC * RC
     result = result * parent->GetTransformToLastDispatchedPaint() * parent->GetAncestorTransform();
 
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -336,18 +336,16 @@ public:
    * the caller uses this return value to determine whether it should consume
    * the excess fling itself by going into an overscroll fling.
    */
   bool DispatchFling(AsyncPanZoomController* aApzc,
                      ScreenPoint aVelocity,
                      nsRefPtr<const OverscrollHandoffChain> aOverscrollHandoffChain,
                      bool aHandoff);
 
-  void SnapBackOverscrolledApzc(AsyncPanZoomController* aStart);
-
   /*
    * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|.
    */
   nsRefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(const nsRefPtr<AsyncPanZoomController>& aInitialTarget);
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -515,17 +515,18 @@ public:
       // This APZC or an APZC further down the handoff chain may be be overscrolled.
       // Start a snap-back animation on the overscrolled APZC.
       // Note:
       //   This needs to be a deferred task even though it can safely run
       //   while holding mMonitor, because otherwise, if the overscrolled APZC
       //   is this one, then the SetState(NOTHING) in UpdateAnimation will
       //   stomp on the SetState(SNAP_BACK) it does.
       mDeferredTasks.append(NewRunnableMethod(mOverscrollHandoffChain.get(),
-                                              &OverscrollHandoffChain::SnapBackOverscrolledApzc));
+                                              &OverscrollHandoffChain::SnapBackOverscrolledApzc,
+                                              &mApzc));
       return false;
     }
 
     // AdjustDisplacement() zeroes out the Axis velocity if we're in overscroll.
     // Since we need to hand off the velocity to the tree manager in such a case,
     // we save it here. Would be ScreenVector instead of ScreenPoint if we had
     // vector classes.
     ScreenPoint velocity(mApzc.mX.GetVelocity(), mApzc.mY.GetVelocity());
@@ -1737,19 +1738,18 @@ static void TransformVector(const Matrix
   start = TransformTo<ScreenPixel>(aTransform, start);
   end = TransformTo<ScreenPixel>(aTransform, end);
   *aVector = end - start;
 }
 
 void AsyncPanZoomController::ToGlobalScreenCoordinates(ScreenPoint* aVector,
                                                        const ScreenPoint& aAnchor) const {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
-    Matrix4x4 transform = treeManagerLocal->GetScreenToApzcTransform(this);
-    transform.Invert();
-    TransformVector(transform, aVector, aAnchor);
+    Matrix4x4 apzcToScreen = treeManagerLocal->GetScreenToApzcTransform(this).Inverse();
+    TransformVector(apzcToScreen, aVector, aAnchor);
   }
 }
 
 void AsyncPanZoomController::ToLocalScreenCoordinates(ScreenPoint* aVector,
                                                       const ScreenPoint& aAnchor) const {
   if (APZCTreeManager* treeManagerLocal = GetApzcTreeManager()) {
     Matrix4x4 transform = treeManagerLocal->GetScreenToApzcTransform(this);
     TransformVector(transform, aVector, aAnchor);
--- a/gfx/layers/apz/src/OverscrollHandoffState.cpp
+++ b/gfx/layers/apz/src/OverscrollHandoffState.cpp
@@ -83,28 +83,29 @@ OverscrollHandoffChain::CancelAnimations
 
 void
 OverscrollHandoffChain::ClearOverscroll() const
 {
   ForEachApzc(&AsyncPanZoomController::ClearOverscroll);
 }
 
 void
-OverscrollHandoffChain::SnapBackOverscrolledApzc() const
+OverscrollHandoffChain::SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const
 {
-  uint32_t i = 0;
-  for (i = 0; i < Length(); ++i) {
+  uint32_t i = IndexOf(aStart);
+  for (; i < Length(); ++i) {
     AsyncPanZoomController* apzc = mChain[i];
     if (!apzc->IsDestroyed() && apzc->SnapBackIfOverscrolled()) {
-      // At most one APZC along the hand-off chain can be overscrolled.
+      // At most one APZC from |aStart| onwards can be overscrolled.
       break;
     }
   }
 
-  // In debug builds, verify our assumption that only one APZC is overscrolled.
+  // In debug builds, verify our assumption that only one APZC from |aStart|
+  // onwards is overscrolled.
 #ifdef DEBUG
   ++i;
   for (; i < Length(); ++i) {
     AsyncPanZoomController* apzc = mChain[i];
     if (!apzc->IsDestroyed()) {
       MOZ_ASSERT(!apzc->IsOverscrolled());
     }
   }
--- a/gfx/layers/apz/src/OverscrollHandoffState.h
+++ b/gfx/layers/apz/src/OverscrollHandoffState.h
@@ -93,18 +93,19 @@ public:
   void FlushRepaints() const;
 
   // Cancel animations all the way up the chain.
   void CancelAnimations() const;
 
   // Clear overscroll all the way up the chain.
   void ClearOverscroll() const;
 
-  // Snap back the APZC that is overscrolled, if any.
-  void SnapBackOverscrolledApzc() const;
+  // Snap back the APZC that is overscrolled on the subset of the chain from
+  // |aStart| onwards, if any.
+  void SnapBackOverscrolledApzc(const AsyncPanZoomController* aStart) const;
 
   // Determine whether the given APZC, or any APZC further in the chain,
   // has room to be panned.
   bool CanBePanned(const AsyncPanZoomController* aApzc) const;
 private:
   std::vector<nsRefPtr<AsyncPanZoomController>> mChain;
 
   typedef void (AsyncPanZoomController::*APZCMethod)();
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -507,17 +507,17 @@ TileClient::~TileClient()
 TileClient::TileClient(const TileClient& o)
 {
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
-  mCompositableClient = nullptr;
+  mCompositableClient = o.mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
 }
 
@@ -526,17 +526,17 @@ TileClient::operator=(const TileClient& 
 {
   if (this == &o) return *this;
   mBackBuffer.Set(this, o.mBackBuffer);
   mBackBufferOnWhite = o.mBackBufferOnWhite;
   mFrontBuffer = o.mFrontBuffer;
   mFrontBufferOnWhite = o.mFrontBufferOnWhite;
   mBackLock = o.mBackLock;
   mFrontLock = o.mFrontLock;
-  mCompositableClient = nullptr;
+  mCompositableClient = o.mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   mLastUpdate = o.mLastUpdate;
 #endif
   mManager = o.mManager;
   mInvalidFront = o.mInvalidFront;
   mInvalidBack = o.mInvalidBack;
   return *this;
 }
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -303,18 +303,17 @@ AsyncCompositionManager::AlignFixedAndSt
 
   // Calculate the cumulative transforms between the subtree root with the
   // old transform and the current transform.
   Matrix oldCumulativeTransform = ancestorTransform * oldRootTransform;
   Matrix newCumulativeTransform = ancestorTransform * newRootTransform;
   if (newCumulativeTransform.IsSingular()) {
     return;
   }
-  Matrix newCumulativeTransformInverse = newCumulativeTransform;
-  newCumulativeTransformInverse.Invert();
+  Matrix newCumulativeTransformInverse = newCumulativeTransform.Inverse();
 
   // Now work out the translation necessary to make sure the layer doesn't
   // move given the new sub-tree root transform.
   Matrix layerTransform;
   if (!GetBaseTransform2D(aLayer, &layerTransform)) {
     return;
   }
 
@@ -694,19 +693,17 @@ ApplyAsyncTransformToScrollbarForContent
     return;
   }
 
   const FrameMetrics& metrics = aContent.Metrics();
   AsyncPanZoomController* apzc = aContent.GetApzc();
 
   Matrix4x4 asyncTransform = apzc->GetCurrentAsyncTransform();
   Matrix4x4 nontransientTransform = apzc->GetNontransientAsyncTransform();
-  Matrix4x4 nontransientUntransform = nontransientTransform;
-  nontransientUntransform.Invert();
-  Matrix4x4 transientTransform = asyncTransform * nontransientUntransform;
+  Matrix4x4 transientTransform = asyncTransform * nontransientTransform.Inverse();
 
   // |transientTransform| represents the amount by which we have scrolled and
   // zoomed since the last paint. Because the scrollbar was sized and positioned based
   // on the painted content, we need to adjust it based on transientTransform so that
   // it reflects what the user is actually seeing now.
   // - The scroll thumb needs to be scaled in the direction of scrolling by the inverse
   //   of the transientTransform scale (representing the zoom). This is because zooming
   //   in decreases the fraction of the whole scrollable rect that is in view.
@@ -731,25 +728,25 @@ ApplyAsyncTransformToScrollbarForContent
   Matrix4x4 transform = scrollbarTransform * aScrollbar->GetTransform();
 
   if (aScrollbarIsDescendant) {
     // If the scrollbar layer is a child of the content it is a scrollbar for, then we
     // need to do an extra untransform to cancel out the transient async transform on
     // the content. This is needed because otherwise that transient async transform is
     // part of the effective transform of this scrollbar, and the scrollbar will jitter
     // as the content scrolls.
-    transientTransform.Invert();
-    transform = transform * transientTransform;
+    Matrix4x4 transientUntransform = transientTransform.Inverse();
+    transform = transform * transientUntransform;
 
     // We also need to make a corresponding change on the clip rect of all the
     // layers on the ancestor chain from the scrollbar layer up to but not
     // including the layer with the async transform. Otherwise the scrollbar
     // shifts but gets clipped and so appears to flicker.
     for (Layer* ancestor = aScrollbar; ancestor != aContent.GetLayer(); ancestor = ancestor->GetParent()) {
-      TransformClipRect(ancestor, transientTransform);
+      TransformClipRect(ancestor, transientUntransform);
     }
   }
 
   // GetTransform already takes the pre- and post-scale into account.  Since we
   // will apply the pre- and post-scale again when computing the effective
   // transform, we must apply the inverses here.
   if (ContainerLayer* container = aScrollbar->AsContainerLayer()) {
     transform.Scale(1.0f/container->GetPreXScale(),
--- a/gfx/thebes/gfxCoreTextShaper.cpp
+++ b/gfx/thebes/gfxCoreTextShaper.cpp
@@ -47,16 +47,17 @@ gfxCoreTextShaper::~gfxCoreTextShaper()
 }
 
 bool
 gfxCoreTextShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
+                             bool             aVertical,
                              gfxShapedText   *aShapedText)
 {
     // Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.
 
     bool isRightToLeft = aShapedText->IsRightToLeft();
     uint32_t length = aLength;
 
     // we need to bidi-wrap the text if the run is RTL,
--- a/gfx/thebes/gfxCoreTextShaper.h
+++ b/gfx/thebes/gfxCoreTextShaper.h
@@ -18,16 +18,17 @@ public:
 
     virtual ~gfxCoreTextShaper();
 
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
+                           bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     // clean up static objects that may have been cached
     static void Shutdown();
 
 protected:
     CTFontRef mCTFont;
     CFDictionaryRef mAttributesDict;
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -214,19 +214,20 @@ gfxDWriteFont::ComputeMetrics(AntialiasO
 
     mMetrics->maxAdvance = mAdjustedSize;
 
     // try to get the true maxAdvance value from 'hhea'
     gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
                                       TRUETYPE_TAG('h','h','e','a'));
     if (hheaTable) {
         uint32_t len;
-        const HheaTable* hhea =
-            reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
-        if (len >= sizeof(HheaTable)) {
+        const MetricsHeader* hhea =
+            reinterpret_cast<const MetricsHeader*>
+                (hb_blob_get_data(hheaTable, &len));
+        if (len >= sizeof(MetricsHeader)) {
             mMetrics->maxAdvance =
                 uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
         }
     }
 
     mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
     mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
 
--- a/gfx/thebes/gfxFT2Fonts.cpp
+++ b/gfx/thebes/gfxFT2Fonts.cpp
@@ -43,20 +43,21 @@
  */
 
 bool
 gfxFT2Font::ShapeText(gfxContext     *aContext,
                       const char16_t *aText,
                       uint32_t        aOffset,
                       uint32_t        aLength,
                       int32_t         aScript,
+                      bool            aVertical,
                       gfxShapedText  *aShapedText)
 {
     if (!gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
-                            aShapedText)) {
+                            aVertical, aShapedText)) {
         // harfbuzz must have failed(?!), just render raw glyphs
         AddRange(aText, aOffset, aLength, aShapedText);
         PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
     }
 
     return true;
 }
 
--- a/gfx/thebes/gfxFT2Fonts.h
+++ b/gfx/thebes/gfxFT2Fonts.h
@@ -72,16 +72,17 @@ public: // new functions
 #endif
 
 protected:
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
+                           bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);
 
     void AddRange(const char16_t *aText,
                   uint32_t         aOffset,
                   uint32_t         aLength,
                   gfxShapedText   *aShapedText);
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1687,26 +1687,38 @@ ForcePaintingDrawMode(DrawMode aDrawMode
 // coordinates (devPt) here.
 void
 gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
                       GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
 {
     const TextRunDrawParams& runParams(aBuffer.mRunParams);
     const FontDrawParams& fontParams(aBuffer.mFontParams);
 
-    double glyphX;
-    if (runParams.isRTL) {
-        aPt->x -= aAdvance;
+    double glyphX, glyphY;
+    if (fontParams.isVerticalFont) {
         glyphX = aPt->x;
+        if (runParams.isRTL) {
+            aPt->y -= aAdvance;
+            glyphY = aPt->y;
+        } else {
+            glyphY = aPt->y;
+            aPt->y += aAdvance;
+        }
     } else {
-        glyphX = aPt->x;
-        aPt->x += aAdvance;
+        glyphY = aPt->y;
+        if (runParams.isRTL) {
+            aPt->x -= aAdvance;
+            glyphX = aPt->x;
+        } else {
+            glyphX = aPt->x;
+            aPt->x += aAdvance;
+        }
     }
     gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
-                   ToDeviceUnits(aPt->y, runParams.devPerApp));
+                   ToDeviceUnits(glyphY, runParams.devPerApp));
 
     if (fontParams.haveSVGGlyphs) {
         if (!runParams.paintSVGGlyphs) {
             return;
         }
         DrawMode mode = ForcePaintingDrawMode(runParams.drawMode);
         if (RenderSVGGlyph(runParams.context, devPt, mode,
                            aGlyphID, fontParams.contextPaint,
@@ -1722,17 +1734,21 @@ gfxFont::DrawOneGlyph(uint32_t aGlyphID,
                          aGlyphID)) {
         return;
     }
 
     aBuffer.OutputGlyph(aGlyphID, devPt);
 
     // Synthetic bolding (if required) by multi-striking.
     for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
-        devPt.x += fontParams.synBoldOnePixelOffset;
+        if (fontParams.isVerticalFont) {
+            devPt.y += fontParams.synBoldOnePixelOffset;
+        } else {
+            devPt.x += fontParams.synBoldOnePixelOffset;
+        }
         aBuffer.OutputGlyph(aGlyphID, devPt);
     }
 
     *aEmittedGlyphs = true;
 }
 
 // Draw a run of CharacterGlyph records from the given offset in aShapedText.
 // Returns true if glyph paths were actually emitted.
@@ -1742,18 +1758,20 @@ gfxFont::DrawGlyphs(gfxShapedText       
                     uint32_t                  aCount, // length of run to draw
                     gfxPoint                 *aPt,
                     const TextRunDrawParams&  aRunParams,
                     const FontDrawParams&     aFontParams)
 {
     bool emittedGlyphs = false;
     GlyphBufferAzure buffer(aRunParams, aFontParams);
 
+    gfxFloat& inlineCoord = aFontParams.isVerticalFont ? aPt->y : aPt->x;
+
     if (aRunParams.spacing) {
-        aPt->x += aRunParams.direction * aRunParams.spacing[0].mBefore;
+        inlineCoord += aRunParams.direction * aRunParams.spacing[0].mBefore;
     }
 
     const gfxShapedText::CompressedGlyph *glyphData =
         &aShapedText->GetCharacterGlyphs()[aOffset];
 
     for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
         if (glyphData->IsSimpleGlyph()) {
             DrawOneGlyph(glyphData->GetSimpleGlyph(),
@@ -1762,32 +1780,41 @@ gfxFont::DrawGlyphs(gfxShapedText       
         } else {
             uint32_t glyphCount = glyphData->GetGlyphCount();
             if (glyphCount > 0) {
                 const gfxShapedText::DetailedGlyph *details =
                     aShapedText->GetDetailedGlyphs(aOffset + i);
                 NS_ASSERTION(details, "detailedGlyph should not be missing!");
                 for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
                     double advance = details->mAdvance;
+
                     if (glyphData->IsMissing()) {
                         // Default-ignorable chars will have zero advance width;
                         // we don't have to draw the hexbox for them.
                         if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
                             advance > 0) {
                             double glyphX = aPt->x;
+                            double glyphY = aPt->y;
                             if (aRunParams.isRTL) {
-                                glyphX -= advance;
+                                if (aFontParams.isVerticalFont) {
+                                    glyphY -= advance;
+                                } else {
+                                    glyphX -= advance;
+                                }
                             }
                             gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
-                                        ToDeviceUnits(aPt->y, aRunParams.devPerApp));
+                                        ToDeviceUnits(glyphY, aRunParams.devPerApp));
                             gfxFloat advanceDevUnits =
                                 ToDeviceUnits(advance, aRunParams.devPerApp);
                             gfxFloat height = GetMetrics(eHorizontal).maxAscent;
-                            gfxRect glyphRect(pt.x, pt.y - height,
-                                              advanceDevUnits, height);
+                            gfxRect glyphRect = aFontParams.isVerticalFont ?
+                                gfxRect(pt.x - height / 2, pt.y,
+                                        height, advanceDevUnits) :
+                                gfxRect(pt.x, pt.y - height,
+                                        advanceDevUnits, height);
 
                             // If there's a fake-italic skew in effect as part
                             // of the drawTarget's transform, we need to remove
                             // this before drawing the hexbox. (Bug 983985)
                             Matrix oldMat;
                             if (aFontParams.passedInvMatrix) {
                                 oldMat = aRunParams.dt->GetTransform();
                                 aRunParams.dt->SetTransform(
@@ -1801,41 +1828,48 @@ gfxFont::DrawGlyphs(gfxShapedText       
                             // Restore the matrix, if we modified it before
                             // drawing the hexbox.
                             if (aFontParams.passedInvMatrix) {
                                 aRunParams.dt->SetTransform(oldMat);
                             }
                         }
                     } else {
                         gfxPoint glyphXY(*aPt);
-                        glyphXY.x += details->mXOffset;
-                        glyphXY.y += details->mYOffset;
+                        if (aFontParams.isVerticalFont) {
+                            glyphXY.x += details->mYOffset;
+                            glyphXY.y += details->mXOffset;
+                        } else {
+                            glyphXY.x += details->mXOffset;
+                            glyphXY.y += details->mYOffset;
+                        }
                         DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
                                      buffer, &emittedGlyphs);
                     }
-                    aPt->x += aRunParams.direction * advance;
+
+                    inlineCoord += aRunParams.direction * advance;
                 }
             }
         }
 
         if (aRunParams.spacing) {
             double space = aRunParams.spacing[i].mAfter;
             if (i + 1 < aCount) {
                 space += aRunParams.spacing[i + 1].mBefore;
             }
-            aPt->x += aRunParams.direction * space;
+            inlineCoord += aRunParams.direction * space;
         }
     }
 
     return emittedGlyphs;
 }
 
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-              gfxPoint *aPt, const TextRunDrawParams& aRunParams)
+              gfxPoint *aPt, const TextRunDrawParams& aRunParams,
+              uint16_t aOrientation)
 {
     NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
                  !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
 
     if (aStart >= aEnd) {
         return;
     }
@@ -1845,16 +1879,44 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
     fontParams.scaledFont = GetScaledFont(aRunParams.dt);
     if (!fontParams.scaledFont) {
         return;
     }
 
     fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
     fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
     fontParams.contextPaint = aRunParams.runContextPaint;
+    fontParams.isVerticalFont =
+        aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+
+    bool sideways = false;
+    gfxPoint origPt = *aPt;
+    if (aRunParams.isVerticalRun && !fontParams.isVerticalFont) {
+        sideways = true;
+        aRunParams.context->Save();
+        gfxPoint p(aPt->x * aRunParams.devPerApp,
+                   aPt->y * aRunParams.devPerApp);
+        const Metrics& metrics = GetMetrics(eHorizontal);
+        // Adjust the matrix to draw the (horizontally-shaped) textrun with
+        // 90-degree CW rotation, and adjust position so that the rotated
+        // horizontal text (which uses a standard alphabetic baseline) will
+        // look OK when juxtaposed with upright glyphs (rendered on a centered
+        // vertical baseline). The adjustment here is somewhat ad hoc; we
+        // should eventually look for baseline tables[1] in the fonts and use
+        // those if available.
+        // [1] http://www.microsoft.com/typography/otspec/base.htm
+        aRunParams.context->SetMatrix(aRunParams.context->CurrentMatrix().
+            Translate(p).       // translate origin for rotation
+            Rotate(M_PI / 2.0). // turn 90deg clockwise
+            Translate(-p).      // undo the translation
+            Translate(gfxPoint(0, metrics.emAscent - metrics.emDescent) / 2));
+                                // and offset the (alphabetic) baseline of the
+                                // horizontally-shaped text from the (centered)
+                                // default baseline used for vertical
+    }
 
     nsAutoPtr<gfxTextContextPaint> contextPaint;
     if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
         NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
                      "no pattern supplied for stroking text");
         nsRefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
         contextPaint =
@@ -1915,33 +1977,40 @@ gfxFont::Draw(gfxTextRun *aTextRun, uint
 
             fontParams.matInv = mat;
             fontParams.matInv.Invert();
 
             fontParams.passedInvMatrix = &fontParams.matInv;
         }
     }
 
-    double origY = aPt->y;
+    gfxFloat& baseline = fontParams.isVerticalFont ? aPt->x : aPt->y;
+    gfxFloat origBaseline = baseline;
     if (mStyle.baselineOffset != 0.0) {
-        aPt->y += mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
+        baseline +=
+            mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
     }
 
     bool emittedGlyphs =
         DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt,
                    aRunParams, fontParams);
 
-    aPt->y = origY;
+    baseline = origBaseline;
 
     if (aRunParams.callbacks && emittedGlyphs) {
         aRunParams.callbacks->NotifyGlyphPathEmitted();
     }
 
     aRunParams.dt->SetTransform(oldMat);
     aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
+
+    if (sideways) {
+        aRunParams.context->Restore();
+        *aPt = gfxPoint(origPt.x, origPt.y + (aPt->x - origPt.x));
+    }
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const
 {
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
         return false;
@@ -2209,16 +2278,17 @@ IsBoundarySpace(char16_t aChar, char16_t
 
 template<typename T>
 gfxShapedWord*
 gfxFont::GetShapedWord(gfxContext *aContext,
                        const T    *aText,
                        uint32_t    aLength,
                        uint32_t    aHash,
                        int32_t     aRunScript,
+                       bool        aVertical,
                        int32_t     aAppUnitsPerDevUnit,
                        uint32_t    aFlags,
                        gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
 {
     // if the cache is getting too big, flush it and start over
     uint32_t wordCacheMaxEntries =
         gfxPlatform::GetPlatform()->WordCacheMaxEntries();
     if (mWordCache->Count() > wordCacheMaxEntries) {
@@ -2268,17 +2338,17 @@ gfxFont::GetShapedWord(gfxContext *aCont
                                                     aAppUnitsPerDevUnit,
                                                     aFlags);
     if (!sw) {
         NS_WARNING("failed to create gfxShapedWord - expect missing text");
         return nullptr;
     }
 
     DebugOnly<bool> ok =
-        ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
+        ShapeText(aContext, aText, 0, aLength, aRunScript, aVertical, sw);
 
     NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
 
     return sw;
 }
 
 bool
 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
@@ -2318,54 +2388,58 @@ gfxFont::CacheHashEntry::KeyEquals(const
 }
 
 bool
 gfxFont::ShapeText(gfxContext    *aContext,
                    const uint8_t *aText,
                    uint32_t       aOffset,
                    uint32_t       aLength,
                    int32_t        aScript,
+                   bool           aVertical,
                    gfxShapedText *aShapedText)
 {
     nsDependentCSubstring ascii((const char*)aText, aLength);
     nsAutoString utf16;
     AppendASCIItoUTF16(ascii, utf16);
     if (utf16.Length() != aLength) {
         return false;
     }
     return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
-                     aScript, aShapedText);
+                     aScript, aVertical, aShapedText);
 }
 
 bool
 gfxFont::ShapeText(gfxContext      *aContext,
                    const char16_t *aText,
                    uint32_t         aOffset,
                    uint32_t         aLength,
                    int32_t          aScript,
+                   bool             aVertical,
                    gfxShapedText   *aShapedText)
 {
     bool ok = false;
 
-    if (FontCanSupportGraphite()) {
+    // XXX Currently, we do all vertical shaping through harfbuzz.
+    // Vertical graphite support may be wanted as a future enhancement.
+    if (FontCanSupportGraphite() && !aVertical) {
         if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
             if (!mGraphiteShaper) {
                 mGraphiteShaper = new gfxGraphiteShaper(this);
             }
             ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
-                                            aScript, aShapedText);
+                                            aScript, aVertical, aShapedText);
         }
     }
 
     if (!ok) {
         if (!mHarfBuzzShaper) {
             mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
         }
         ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
-                                        aScript, aShapedText);
+                                        aScript, aVertical, aShapedText);
     }
 
     NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
 
     PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
 
     return ok;
 }
@@ -2392,16 +2466,17 @@ gfxFont::PostShapingFixup(gfxContext    
 
 template<typename T>
 bool
 gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
                                        const T    *aText,
                                        uint32_t    aOffset,
                                        uint32_t    aLength,
                                        int32_t     aScript,
+                                       bool        aVertical,
                                        gfxTextRun *aTextRun)
 {
     aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
 
     bool ok = true;
 
     while (ok && aLength > 0) {
         uint32_t fragLen = aLength;
@@ -2427,17 +2502,18 @@ gfxFont::ShapeFragmentWithoutWordCache(g
                     if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
                         NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
                         --fragLen;
                     }
                 }
             }
         }
 
-        ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
+        ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aVertical,
+                       aTextRun);
 
         aText += fragLen;
         aOffset += fragLen;
         aLength -= fragLen;
     }
 
     return ok;
 }
@@ -2455,16 +2531,17 @@ IsInvalidControlChar(uint32_t aCh)
 
 template<typename T>
 bool
 gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
                                    const T    *aText,
                                    uint32_t    aOffset,
                                    uint32_t    aLength,
                                    int32_t     aScript,
+                                   bool        aVertical,
                                    gfxTextRun *aTextRun)
 {
     uint32_t fragStart = 0;
     bool ok = true;
 
     for (uint32_t i = 0; i <= aLength && ok; ++i) {
         T ch = (i < aLength) ? aText[i] : '\n';
         bool invalid = gfxFontGroup::IsInvalidChar(ch);
@@ -2473,17 +2550,17 @@ gfxFont::ShapeTextWithoutWordCache(gfxCo
         // break into separate fragments when we hit an invalid char
         if (!invalid) {
             continue;
         }
 
         if (length > 0) {
             ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
                                                aOffset + fragStart, length,
-                                               aScript, aTextRun);
+                                               aScript, aVertical, aTextRun);
         }
 
         if (i == aLength) {
             break;
         }
 
         // fragment was terminated by an invalid char: skip it,
         // unless it's a control char that we want to show as a hexbox,
@@ -2529,17 +2606,18 @@ inline static bool HasSpaces(const char1
 
 template<typename T>
 bool
 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
                              gfxTextRun *aTextRun,
                              const T *aString, // text for this font run
                              uint32_t aRunStart, // position in the textrun
                              uint32_t aRunLength,
-                             int32_t aRunScript)
+                             int32_t aRunScript,
+                             bool aVertical)
 {
     if (aRunLength == 0) {
         return true;
     }
 
     gfxTextPerfMetrics *tp = nullptr;
 
 #ifndef RELEASE_BUILD
@@ -2564,17 +2642,18 @@ gfxFont::SplitAndInitTextRun(gfxContext 
     // fractions), need to shape without using the word cache which segments
     // textruns on space boundaries. Word cache can be used if the textrun
     // is short enough to fit in the word cache and it lacks spaces.
     if (SpaceMayParticipateInShaping(aRunScript)) {
         if (aRunLength > wordCacheCharLimit ||
             HasSpaces(aString, aRunLength)) {
             TEXT_PERF_INCR(tp, wordCacheSpaceRules);
             return ShapeTextWithoutWordCache(aContext, aString,
-                                             aRunStart, aRunLength, aRunScript,
+                                             aRunStart, aRunLength,
+                                             aRunScript, aVertical,
                                              aTextRun);
         }
     }
 
     InitWordCache();
 
     // the only flags we care about for ShapedWord construction/caching
     uint32_t flags = aTextRun->GetFlags();
@@ -2617,57 +2696,60 @@ gfxFont::SplitAndInitTextRun(gfxContext 
         // font's word cache but just shape directly into the textrun.
         if (length > wordCacheCharLimit) {
             TEXT_PERF_INCR(tp, wordCacheLong);
             bool ok = ShapeFragmentWithoutWordCache(aContext,
                                                     aString + wordStart,
                                                     aRunStart + wordStart,
                                                     length,
                                                     aRunScript,
+                                                    aVertical,
                                                     aTextRun);
             if (!ok) {
                 return false;
             }
         } else if (length > 0) {
             uint32_t wordFlags = flags;
             // in the 8-bit version of this method, TEXT_IS_8BIT was
             // already set as part of |flags|, so no need for a per-word
             // adjustment here
             if (sizeof(T) == sizeof(char16_t)) {
                 if (wordIs8Bit) {
                     wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
                 }
             }
             gfxShapedWord *sw = GetShapedWord(aContext,
                                               aString + wordStart, length,
-                                              hash, aRunScript,
+                                              hash, aRunScript, aVertical,
                                               appUnitsPerDevUnit,
                                               wordFlags, tp);
             if (sw) {
                 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
             } else {
                 return false; // failed, presumably out of memory?
             }
         }
 
         if (boundary) {
             // word was terminated by a space: add that to the textrun
             uint16_t orientation = flags & gfxTextRunFactory::TEXT_ORIENT_MASK;
             if (orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
-                orientation = gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
+                orientation = aVertical ?
+                    gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT :
+                    gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
             }
             if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
                                                  aRunStart + i, ch,
                                                  orientation))
             {
                 static const uint8_t space = ' ';
                 gfxShapedWord *sw =
                     GetShapedWord(aContext,
                                   &space, 1,
-                                  gfxShapedWord::HashMix(0, ' '), aRunScript,
+                                  gfxShapedWord::HashMix(0, ' '), aRunScript, aVertical,
                                   appUnitsPerDevUnit,
                                   flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
                 if (sw) {
                     aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
                 } else {
                     return false;
                 }
             }
@@ -2706,25 +2788,27 @@ gfxFont::SplitAndInitTextRun(gfxContext 
 
 // Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
 template bool
 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
                              gfxTextRun *aTextRun,
                              const uint8_t *aString,
                              uint32_t aRunStart,
                              uint32_t aRunLength,
-                             int32_t aRunScript);
+                             int32_t aRunScript,
+                             bool aVertical);
 template bool
 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
                              gfxTextRun *aTextRun,
                              const char16_t *aString,
                              uint32_t aRunStart,
                              uint32_t aRunLength,
-                             int32_t aRunScript);
- 
+                             int32_t aRunScript,
+                             bool aVertical);
+
 template<>
 bool
 gfxFont::InitFakeSmallCapsRun(gfxContext     *aContext,
                               gfxTextRun     *aTextRun,
                               const char16_t *aText,
                               uint32_t        aOffset,
                               uint32_t        aLength,
                               uint8_t         aMatchType,
@@ -2740,16 +2824,18 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
     enum RunCaseAction {
         kNoChange,
         kUppercaseReduce,
         kUppercase
     };
 
     RunCaseAction runAction = kNoChange;
     uint32_t runStart = 0;
+    bool vertical =
+        aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
 
     for (uint32_t i = 0; i <= aLength; ++i) {
         uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
                                      // a trailing surrogate as well as the
                                      // current code unit.
         RunCaseAction chAction = kNoChange;
         // Unless we're at the end, figure out what treatment the current
         // character will need.
@@ -2798,17 +2884,17 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
             switch (runAction) {
             case kNoChange:
                 // just use the current font and the existing string
                 aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
                                       aOrientation);
                 if (!f->SplitAndInitTextRun(aContext, aTextRun,
                                             aText + runStart,
                                             aOffset + runStart, runLength,
-                                            aScript)) {
+                                            aScript, vertical)) {
                     ok = false;
                 }
                 break;
 
             case kUppercaseReduce:
                 // use reduced-size font, then fall through to uppercase the text
                 f = smallCapsFont;
 
@@ -2839,17 +2925,17 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
                     nsAutoPtr<gfxTextRun> tempRun;
                     tempRun =
                         gfxTextRun::Create(&params, convertedString.Length(),
                                            aTextRun->GetFontGroup(), 0);
                     tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation);
                     if (!f->SplitAndInitTextRun(aContext, tempRun,
                                                 convertedString.BeginReading(),
                                                 0, convertedString.Length(),
-                                                aScript)) {
+                                                aScript, vertical)) {
                         ok = false;
                     } else {
                         nsAutoPtr<gfxTextRun> mergedRun;
                         mergedRun =
                             gfxTextRun::Create(&params, runLength,
                                                aTextRun->GetFontGroup(), 0);
                         MergeCharactersInTextRun(mergedRun, tempRun,
                                                  charsToMergeArray.Elements(),
@@ -2858,17 +2944,17 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
                                                     aOffset + runStart);
                     }
                 } else {
                     aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
                                           true, aOrientation);
                     if (!f->SplitAndInitTextRun(aContext, aTextRun,
                                                 convertedString.BeginReading(),
                                                 aOffset + runStart, runLength,
-                                                aScript)) {
+                                                aScript, vertical)) {
                         ok = false;
                     }
                 }
                 break;
             }
 
             runStart = i;
         }
@@ -2892,17 +2978,17 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
                               uint8_t         aMatchType,
                               uint16_t        aOrientation,
                               int32_t         aScript,
                               bool            aSyntheticLower,
                               bool            aSyntheticUpper)
 {
     NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
                                          aLength);
-    return InitFakeSmallCapsRun(aContext, aTextRun, unicodeString.get(),
+    return InitFakeSmallCapsRun(aContext, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
                                 aOffset, aLength, aMatchType, aOrientation,
                                 aScript, aSyntheticLower, aSyntheticUpper);
 }
 
 already_AddRefed<gfxFont>
 gfxFont::GetSmallCapsFont()
 {
     gfxFontStyle style(*GetStyle());
@@ -3019,19 +3105,20 @@ gfxFont::InitMetricsFromSfntTables(Metri
         mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
     }
 
     // 'hhea' table is required to get vertical extents
     gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
     if (!hheaTable) {
         return false; // no 'hhea' table -> not an sfnt
     }
-    const HheaTable* hhea =
-        reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
-    if (len < sizeof(HheaTable)) {
+    const MetricsHeader* hhea =
+        reinterpret_cast<const MetricsHeader*>
+            (hb_blob_get_data(hheaTable, &len));
+    if (len < sizeof(MetricsHeader)) {
         return false;
     }
 
 #define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
 #define SET_SIGNED(field,src)   aMetrics.field = int16_t(src) * mFUnitsConvFactor
 
     SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
     SET_SIGNED(maxAscent, hhea->ascender);
@@ -3267,36 +3354,36 @@ gfxFont::CreateVerticalMetrics()
         }
     }
 
     // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
     // and use the line height from its ascent/descent.
     if (!metrics->aveCharWidth) {
         gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
         if (hheaTable) {
-            const HheaTable* hhea =
-                reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable,
-                                                                    &len));
-            if (len >= sizeof(HheaTable)) {
+            const MetricsHeader* hhea =
+                reinterpret_cast<const MetricsHeader*>
+                    (hb_blob_get_data(hheaTable, &len));
+            if (len >= sizeof(MetricsHeader)) {
                 SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
                                          int16_t(hhea->descender));
                 metrics->maxAscent = metrics->aveCharWidth / 2;
                 metrics->maxDescent =
                     metrics->aveCharWidth - metrics->maxAscent;
             }
         }
     }
 
     // Read real vertical metrics if available.
     gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
     if (vheaTable) {
-        const HheaTable* vhea =
-            reinterpret_cast<const HheaTable*>(hb_blob_get_data(vheaTable,
-                                                                &len));
-        if (len >= sizeof(HheaTable)) {
+        const MetricsHeader* vhea =
+            reinterpret_cast<const MetricsHeader*>
+                (hb_blob_get_data(vheaTable, &len));
+        if (len >= sizeof(MetricsHeader)) {
             SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
             SET_SIGNED(maxAscent, vhea->ascender);
             SET_SIGNED(maxDescent, -int16_t(vhea->descender));
             SET_SIGNED(externalLeading, vhea->lineGap);
         }
     }
 
     // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -605,16 +605,17 @@ public:
     // Shape a piece of text and store the resulting glyph data into
     // aShapedText. Parameters aOffset/aLength indicate the range of
     // aShapedText to be updated; aLength is also the length of aText.
     virtual bool ShapeText(gfxContext     *aContext,
                            const char16_t *aText,
                            uint32_t        aOffset,
                            uint32_t        aLength,
                            int32_t         aScript,
+                           bool            aVertical,
                            gfxShapedText  *aShapedText) = 0;
 
     gfxFont *GetFont() const { return mFont; }
 
     // returns true if features exist in output, false otherwise
     static bool
     MergeFontFeatures(const gfxFontStyle *aStyle,
                       const nsTArray<gfxFontFeature>& aFontFeatures,
@@ -1522,17 +1523,18 @@ public:
      * .context  the Thebes graphics context to which we're drawing
      * .dt  Moz2D DrawTarget to which we're drawing
      *
      * Callers guarantee:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
      */
     void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-              gfxPoint *aPt, const TextRunDrawParams& aRunParams);
+              gfxPoint *aPt, const TextRunDrawParams& aRunParams,
+              uint16_t aOrientation);
 
     /**
      * Measure a run of characters. See gfxTextRun::Metrics.
      * @param aTight if false, then return the union of the glyph extents
      * with the font-box for the characters (the rectangle with x=0,width=
      * the advance width for the character run,y=-(font ascent), and height=
      * font ascent + font descent). Otherwise, we must return as tight as possible
      * an approximation to the area actually painted by glyphs.
@@ -1625,26 +1627,28 @@ public:
     // limiting the length of text passed by processing the run in multiple
     // segments if necessary
     template<typename T>
     bool SplitAndInitTextRun(gfxContext *aContext,
                              gfxTextRun *aTextRun,
                              const T *aString,
                              uint32_t aRunStart,
                              uint32_t aRunLength,
-                             int32_t aRunScript);
+                             int32_t aRunScript,
+                             bool aVertical);
 
     // Get a ShapedWord representing the given text (either 8- or 16-bit)
     // for use in setting up a gfxTextRun.
     template<typename T>
     gfxShapedWord* GetShapedWord(gfxContext *aContext,
                                  const T *aText,
                                  uint32_t aLength,
                                  uint32_t aHash,
                                  int32_t aRunScript,
+                                 bool aVertical,
                                  int32_t aAppUnitsPerDevUnit,
                                  uint32_t aFlags,
                                  gfxTextPerfMetrics *aTextPerf);
 
     // Ensure the ShapedWord cache is initialized. This MUST be called before
     // any attempt to use GetShapedWord().
     void InitWordCache() {
         if (!mWordCache) {
@@ -1811,25 +1815,27 @@ protected:
     bool SpaceMayParticipateInShaping(int32_t aRunScript);
 
     // For 8-bit text, expand to 16-bit and then call the following method.
     bool ShapeText(gfxContext    *aContext,
                    const uint8_t *aText,
                    uint32_t       aOffset, // dest offset in gfxShapedText
                    uint32_t       aLength,
                    int32_t        aScript,
+                   bool           aVertical,
                    gfxShapedText *aShapedText); // where to store the result
 
     // Call the appropriate shaper to generate glyphs for aText and store
     // them into aShapedText.
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
+                           bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     // Helper to adjust for synthetic bold and set character-type flags
     // in the shaped text; implementations of ShapeText should call this
     // after glyph shaping has been completed.
     void PostShapingFixup(gfxContext      *aContext,
                           const char16_t *aText,
                           uint32_t         aOffset, // position within aShapedText
@@ -1844,29 +1850,31 @@ protected:
     // not handled via normal shaping, but does not otherwise divide up the
     // text.
     template<typename T>
     bool ShapeTextWithoutWordCache(gfxContext *aContext,
                                    const T    *aText,
                                    uint32_t    aOffset,
                                    uint32_t    aLength,
                                    int32_t     aScript,
+                                   bool        aVertical,
                                    gfxTextRun *aTextRun);
 
     // Shape a fragment of text (a run that is known to contain only
     // "valid" characters, no newlines/tabs/other control chars).
     // All non-wordcache shaping goes through here; this is the function
     // that will ensure we don't pass excessively long runs to the various
     // platform shapers.
     template<typename T>
     bool ShapeFragmentWithoutWordCache(gfxContext *aContext,
                                        const T    *aText,
                                        uint32_t    aOffset,
                                        uint32_t    aLength,
                                        int32_t     aScript,
+                                       bool        aVertical,
                                        gfxTextRun *aTextRun);
 
     void CheckForFeaturesInvolvingSpace();
 
     // whether a given feature is included in feature settings from both the
     // font and the style. aFeatureOn set if resolved feature value is non-zero
     bool HasFeatureSet(uint32_t aFeature, bool& aFeatureOn);
 
@@ -2048,26 +2056,28 @@ struct TextRunDrawParams {
     mozilla::RefPtr<mozilla::gfx::DrawTarget> dt;
     gfxContext              *context;
     gfxFont::Spacing        *spacing;
     gfxTextRunDrawCallbacks *callbacks;
     gfxTextContextPaint     *runContextPaint;
     gfxFloat                 direction;
     double                   devPerApp;
     DrawMode                 drawMode;
+    bool                     isVerticalRun;
     bool                     isRTL;
     bool                     paintSVGGlyphs;
 };
 
 struct FontDrawParams {
     mozilla::RefPtr<mozilla::gfx::ScaledFont>            scaledFont;
     mozilla::RefPtr<mozilla::gfx::GlyphRenderingOptions> renderingOptions;
     gfxTextContextPaint      *contextPaint;
     mozilla::gfx::Matrix     *passedInvMatrix;
     mozilla::gfx::Matrix      matInv;
     double                    synBoldOnePixelOffset;
     int32_t                   extraStrikes;
     mozilla::gfx::DrawOptions drawOptions;
+    bool                      isVerticalFont;
     bool                      haveSVGGlyphs;
     bool                      haveColorGlyphs;
 };
 
 #endif
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -548,34 +548,37 @@ struct PostTable {
     AutoSwap_PRUint16    underlineThickness;
     AutoSwap_PRUint32    isFixedPitch;
     AutoSwap_PRUint32    minMemType42;
     AutoSwap_PRUint32    maxMemType42;
     AutoSwap_PRUint32    minMemType1;
     AutoSwap_PRUint32    maxMemType1;
 };
 
-struct HheaTable {
+// This structure is used for both 'hhea' and 'vhea' tables.
+// The field names here are those of the horizontal version; the
+// vertical table just exchanges vertical and horizontal coordinates.
+struct MetricsHeader {
     AutoSwap_PRUint32    version;
     AutoSwap_PRInt16     ascender;
     AutoSwap_PRInt16     descender;
     AutoSwap_PRInt16     lineGap;
     AutoSwap_PRUint16    advanceWidthMax;
     AutoSwap_PRInt16     minLeftSideBearing;
     AutoSwap_PRInt16     minRightSideBearing;
     AutoSwap_PRInt16     xMaxExtent;
     AutoSwap_PRInt16     caretSlopeRise;
     AutoSwap_PRInt16     caretSlopeRun;
     AutoSwap_PRInt16     caretOffset;
     AutoSwap_PRInt16     reserved1;
     AutoSwap_PRInt16     reserved2;
     AutoSwap_PRInt16     reserved3;
     AutoSwap_PRInt16     reserved4;
     AutoSwap_PRInt16     metricDataFormat;
-    AutoSwap_PRUint16    numOfLongHorMetrics;
+    AutoSwap_PRUint16    numOfLongMetrics;
 };
 
 struct MaxpTableHeader {
     AutoSwap_PRUint32    version; // CFF: 0x00005000; TrueType: 0x00010000
     AutoSwap_PRUint16    numGlyphs;
 // truetype version has additional fields that we don't currently use
 };
 
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -78,16 +78,17 @@ gfxGDIFont::CopyWithAntialiasOption(Anti
 }
 
 bool
 gfxGDIFont::ShapeText(gfxContext     *aContext,
                       const char16_t *aText,
                       uint32_t        aOffset,
                       uint32_t        aLength,
                       int32_t         aScript,
+                      bool            aVertical,
                       gfxShapedText  *aShapedText)
 {
     if (!mMetrics) {
         Initialize();
     }
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
@@ -97,17 +98,17 @@ gfxGDIFont::ShapeText(gfxContext     *aC
     // creating a "toy" font internally (see bug 544617).
     // We must check that this succeeded, otherwise we risk cairo creating the
     // wrong kind of font internally as a fallback (bug 744480).
     if (!SetupCairoFont(aContext)) {
         return false;
     }
 
     return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
-                              aShapedText);
+                              aVertical, aShapedText);
 }
 
 const gfxFont::Metrics&
 gfxGDIFont::GetHorizontalMetrics()
 {
     if (!mMetrics) {
         Initialize();
     }
--- a/gfx/thebes/gfxGDIFont.h
+++ b/gfx/thebes/gfxGDIFont.h
@@ -72,16 +72,17 @@ protected:
     virtual const Metrics& GetHorizontalMetrics();
 
     /* override to ensure the cairo font is set up properly */
     virtual bool ShapeText(gfxContext     *aContext,
                            const char16_t *aText,
                            uint32_t        aOffset,
                            uint32_t        aLength,
                            int32_t         aScript,
+                           bool            aVertical,
                            gfxShapedText  *aShapedText);
 
     void Initialize(); // creates metrics and Cairo fonts
 
     // Fill the given LOGFONT record according to our style, but don't adjust
     // the lfItalic field if we're going to use a cairo transform for fake
     // italics.
     void FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic);
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -84,16 +84,17 @@ AddFeature(const uint32_t& aTag, uint32_
 }
 
 bool
 gfxGraphiteShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
+                             bool             aVertical,
                              gfxShapedText   *aShapedText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     if (!mFont->SetupCairoFont(aContext)) {
         return false;
     }
 
     mCallbackData.mContext = aContext;
--- a/gfx/thebes/gfxGraphiteShaper.h
+++ b/gfx/thebes/gfxGraphiteShaper.h
@@ -17,16 +17,17 @@ public:
     explicit gfxGraphiteShaper(gfxFont *aFont);
     virtual ~gfxGraphiteShaper();
 
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
+                           bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     static void Shutdown();
 
 protected:
     nsresult SetGlyphsFromSegment(gfxContext      *aContext,
                                   gfxShapedText   *aShapedText,
                                   uint32_t         aOffset,
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -34,24 +34,28 @@ using namespace mozilla::unicode; // for
  */
 
 gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
     : gfxFontShaper(aFont),
       mHBFace(aFont->GetFontEntry()->GetHBFace()),
       mHBFont(nullptr),
       mKernTable(nullptr),
       mHmtxTable(nullptr),
-      mNumLongMetrics(0),
+      mVmtxTable(nullptr),
+      mVORGTable(nullptr),
       mCmapTable(nullptr),
       mCmapFormat(-1),
       mSubtableOffset(0),
       mUVSTableOffset(0),
+      mNumLongHMetrics(0),
+      mNumLongVMetrics(0),
       mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
       mUseFontGlyphWidths(false),
-      mInitialized(false)
+      mInitialized(false),
+      mVerticalInitialized(false)
 {
 }
 
 gfxHarfBuzzShaper::~gfxHarfBuzzShaper()
 {
     if (mCmapTable) {
         hb_blob_destroy(mCmapTable);
     }
@@ -161,65 +165,78 @@ HBGetGlyph(hb_font_t *font, void *font_d
            void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
         static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
     *glyph = fcd->mShaper->GetGlyph(unicode, variation_selector);
     return *glyph != 0;
 }
 
-struct HMetricsHeader {
-    AutoSwap_PRUint32    tableVersionNumber;
-    AutoSwap_PRInt16     ascender;
-    AutoSwap_PRInt16     descender;
-    AutoSwap_PRInt16     lineGap;
-    AutoSwap_PRUint16    advanceWidthMax;
-    AutoSwap_PRInt16     minLeftSideBearing;
-    AutoSwap_PRInt16     minRightSideBearing;
-    AutoSwap_PRInt16     xMaxExtent;
-    AutoSwap_PRInt16     caretSlopeRise;
-    AutoSwap_PRInt16     caretSlopeRun;
-    AutoSwap_PRInt16     caretOffset;
-    AutoSwap_PRInt16     reserved[4];
-    AutoSwap_PRInt16     metricDataFormat;
-    AutoSwap_PRUint16    numberOfHMetrics;
+// Glyph metrics structures, shared (with appropriate reinterpretation of
+// field names) by horizontal and vertical metrics tables.
+struct LongMetric {
+    AutoSwap_PRUint16    advanceWidth; // or advanceHeight, when vertical
+    AutoSwap_PRInt16     lsb;          // or tsb, when vertical
 };
 
-struct HLongMetric {
-    AutoSwap_PRUint16    advanceWidth;
-    AutoSwap_PRInt16     lsb;
-};
-
-struct HMetrics {
-    HLongMetric          metrics[1]; // actually numberOfHMetrics
+struct GlyphMetrics {
+    LongMetric           metrics[1]; // actually numberOfLongMetrics
 // the variable-length metrics[] array is immediately followed by:
 //  AutoSwap_PRUint16    leftSideBearing[];
 };
 
 hb_position_t
 gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
                                     hb_codepoint_t glyph) const
 {
-    // font did not implement GetHintedGlyphWidth, so get an unhinted value
+    // font did not implement GetGlyphWidth, so get an unhinted value
     // directly from the font tables
 
-    NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
+    NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
                  "font is lacking metrics, we shouldn't be here");
 
-    if (glyph >= uint32_t(mNumLongMetrics)) {
-        glyph = mNumLongMetrics - 1;
+    if (glyph >= uint32_t(mNumLongHMetrics)) {
+        glyph = mNumLongHMetrics - 1;
     }
 
     // glyph must be valid now, because we checked during initialization
-    // that mNumLongMetrics is > 0, and that the hmtx table is large enough
-    // to contain mNumLongMetrics records
-    const HMetrics* hmtx =
-        reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nullptr));
+    // that mNumLongHMetrics is > 0, and that the metrics table is large enough
+    // to contain mNumLongHMetrics records
+    const GlyphMetrics* metrics =
+        reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mHmtxTable,
+                                                               nullptr));
     return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
-                        uint16_t(hmtx->metrics[glyph].advanceWidth));
+                        uint16_t(metrics->metrics[glyph].advanceWidth));
+}
+
+hb_position_t
+gfxHarfBuzzShaper::GetGlyphVAdvance(gfxContext *aContext,
+                                    hb_codepoint_t glyph) const
+{
+    if (!mVmtxTable) {
+        // Must be a "vertical" font that doesn't actually have vertical metrics;
+        // use a fixed advance.
+        return FloatToFixed(mFont->GetMetrics(gfxFont::eVertical).aveCharWidth);
+    }
+
+    NS_ASSERTION(mNumLongVMetrics > 0,
+                 "font is lacking metrics, we shouldn't be here");
+
+    if (glyph >= uint32_t(mNumLongVMetrics)) {
+        glyph = mNumLongVMetrics - 1;
+    }
+
+    // glyph must be valid now, because we checked during initialization
+    // that mNumLongVMetrics is > 0, and that the metrics table is large enough
+    // to contain mNumLongVMetrics records
+    const GlyphMetrics* metrics =
+        reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mVmtxTable,
+                                                               nullptr));
+    return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
+                        uint16_t(metrics->metrics[glyph].advanceWidth));
 }
 
 /* static */
 hb_position_t
 gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
                                       hb_codepoint_t glyph, void *user_data)
 {
     const gfxHarfBuzzShaper::FontCallbackData *fcd =
@@ -227,16 +244,121 @@ gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb
     gfxFont *gfxfont = fcd->mShaper->GetFont();
     if (gfxfont->ProvidesGlyphWidths()) {
         return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
     } else {
         return fcd->mShaper->GetGlyphHAdvance(fcd->mContext, glyph);
     }
 }
 
+/* static */
+hb_position_t
+gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
+                                      hb_codepoint_t glyph, void *user_data)
+{
+    const gfxHarfBuzzShaper::FontCallbackData *fcd =
+        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+    gfxFont *gfxfont = fcd->mShaper->GetFont();
+    if (gfxfont->ProvidesGlyphWidths()) {
+        return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
+    } else {
+        return fcd->mShaper->GetGlyphVAdvance(fcd->mContext, glyph);
+    }
+}
+
+/* static */
+hb_bool_t
+gfxHarfBuzzShaper::HBGetGlyphHOrigin(hb_font_t *font, void *font_data,
+                                     hb_codepoint_t glyph,
+                                     hb_position_t *x, hb_position_t *y,
+                                     void *user_data)
+{
+    // We work in horizontal coordinates, so no origin adjustment needed here.
+    return true;
+}
+
+struct VORG {
+    AutoSwap_PRUint16 majorVersion;
+    AutoSwap_PRUint16 minorVersion;
+    AutoSwap_PRInt16  defaultVertOriginY;
+    AutoSwap_PRUint16 numVertOriginYMetrics;
+};
+
+struct VORGrec {
+    AutoSwap_PRUint16 glyphIndex;
+    AutoSwap_PRInt16  vertOriginY;
+};
+
+/* static */
+hb_bool_t
+gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
+                                     hb_codepoint_t glyph,
+                                     hb_position_t *x, hb_position_t *y,
+                                     void *user_data)
+{
+    const gfxHarfBuzzShaper::FontCallbackData *fcd =
+        static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
+    fcd->mShaper->GetGlyphVOrigin(fcd->mContext, glyph, x, y);
+    return true;
+}
+
+void
+gfxHarfBuzzShaper::GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
+                                   hb_position_t *aX, hb_position_t *aY) const
+{
+    *aX = -0.5 * GetGlyphHAdvance(aContext, aGlyph);
+
+    if (mVORGTable) {
+        // We checked in Initialize() that the VORG table is safely readable,
+        // so no length/bounds-check needed here.
+        const VORG* vorg =
+            reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
+
+        const VORGrec *lo = reinterpret_cast<const VORGrec*>(vorg + 1);
+        const VORGrec *hi = lo + uint16_t(vorg->numVertOriginYMetrics);
+        const VORGrec *limit = hi;
+        while (lo < hi) {
+            const VORGrec *mid = lo + (hi - lo) / 2;
+            if (uint16_t(mid->glyphIndex) < aGlyph) {
+                lo = mid + 1;
+            } else {
+                hi = mid;
+            }
+        }
+
+        if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
+            *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
+                                int16_t(lo->vertOriginY));
+        } else {
+            *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
+                                int16_t(vorg->defaultVertOriginY));
+        }
+        return;
+    }
+
+    // XXX should we consider using OS/2 sTypo* metrics if available?
+
+    gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
+                                      TRUETYPE_TAG('h','h','e','a'));
+    if (hheaTable) {
+        uint32_t len;
+        const MetricsHeader* hhea =
+            reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable,
+                                                                    &len));
+        if (len >= sizeof(MetricsHeader)) {
+            *aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
+                                int16_t(hhea->ascender));
+            return;
+        }
+    }
+
+    NS_NOTREACHED("we shouldn't be here!");
+    *aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
+}
+
 static hb_bool_t
 HBGetContourPoint(hb_font_t *font, void *font_data,
                   unsigned int point_index, hb_codepoint_t glyph,
                   hb_position_t *x, hb_position_t *y,
                   void *user_data)
 {
     /* not yet implemented - no support for used of hinted contour points
        to fine-tune anchor positions in GPOS AnchorFormat2 */
@@ -853,16 +975,25 @@ gfxHarfBuzzShaper::Initialize()
         // static function callback pointers, initialized by the first
         // harfbuzz shaper used
         sHBFontFuncs = hb_font_funcs_create();
         hb_font_funcs_set_glyph_func(sHBFontFuncs, HBGetGlyph,
                                      nullptr, nullptr);
         hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
                                                HBGetGlyphHAdvance,
                                                nullptr, nullptr);
+        hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs,
+                                               HBGetGlyphVAdvance,
+                                               nullptr, nullptr);
+        hb_font_funcs_set_glyph_h_origin_func(sHBFontFuncs,
+                                              HBGetGlyphHOrigin,
+                                              nullptr, nullptr);
+        hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
+                                              HBGetGlyphVOrigin,
+                                              nullptr, nullptr);
         hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
                                                    HBGetContourPoint,
                                                    nullptr, nullptr);
         hb_font_funcs_set_glyph_h_kerning_func(sHBFontFuncs,
                                                HBGetHKerning,
                                                nullptr, nullptr);
 
         sHBUnicodeFuncs =
@@ -905,78 +1036,154 @@ gfxHarfBuzzShaper::Initialize()
                                   &mSubtableOffset, &mUVSTableOffset,
                                   &symbol);
         if (mCmapFormat <= 0) {
             return false;
         }
     }
 
     if (!mUseFontGlyphWidths) {
-        // if font doesn't implement GetGlyphWidth, we will be reading
-        // the hmtx table directly;
-        // read mNumLongMetrics from hhea table without caching its blob,
-        // and preload/cache the hmtx table
-        gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
-        if (hheaTable) {
-            uint32_t len;
-            const HMetricsHeader* hhea =
-                reinterpret_cast<const HMetricsHeader*>
-                (hb_blob_get_data(hheaTable, &len));
-            if (len >= sizeof(HMetricsHeader)) {
-                mNumLongMetrics = hhea->numberOfHMetrics;
-                if (mNumLongMetrics > 0 &&
-                    int16_t(hhea->metricDataFormat) == 0) {
-                    // no point reading hmtx if number of entries is zero!
-                    // in that case, we won't be able to use this font
-                    // (this method will return FALSE below if mHmtx is null)
-                    mHmtxTable =
-                        entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
-                    if (hb_blob_get_length(mHmtxTable) <
-                        mNumLongMetrics * sizeof(HLongMetric)) {
-                        // hmtx table is not large enough for the claimed
-                        // number of entries: invalid, do not use.
-                        hb_blob_destroy(mHmtxTable);
-                        mHmtxTable = nullptr;
-                    }
-                }
-            }
-        }
-        if (!mHmtxTable) {
+        // If font doesn't implement GetGlyphWidth, we will be reading
+        // the metrics table directly, so make sure we can load it.
+        if (!LoadHmtxTable()) {
             return false;
         }
     }
 
     mHBFont = hb_font_create(mHBFace);
     hb_font_set_funcs(mHBFont, sHBFontFuncs, &mCallbackData, nullptr);
     hb_font_set_ppem(mHBFont, mFont->GetAdjustedSize(), mFont->GetAdjustedSize());
     uint32_t scale = FloatToFixed(mFont->GetAdjustedSize()); // 16.16 fixed-point
     hb_font_set_scale(mHBFont, scale, scale);
 
     return true;
 }
 
 bool
+gfxHarfBuzzShaper::LoadHmtxTable()
+{
+    // Read mNumLongHMetrics from metrics-head table without caching its
+    // blob, and preload/cache the metrics table.
+    gfxFontEntry *entry = mFont->GetFontEntry();
+    gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
+    if (hheaTable) {
+        uint32_t len;
+        const MetricsHeader* hhea =
+            reinterpret_cast<const MetricsHeader*>
+            (hb_blob_get_data(hheaTable, &len));
+        if (len >= sizeof(MetricsHeader)) {
+            mNumLongHMetrics = hhea->numOfLongMetrics;
+            if (mNumLongHMetrics > 0 &&
+                int16_t(hhea->metricDataFormat) == 0) {
+                // no point reading metrics if number of entries is zero!
+                // in that case, we won't be able to use this font
+                // (this method will return FALSE below if mHmtxTable
+                // is null)
+                mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
+                if (hb_blob_get_length(mHmtxTable) <
+                    mNumLongHMetrics * sizeof(LongMetric)) {
+                    // metrics table is not large enough for the claimed
+                    // number of entries: invalid, do not use.
+                    hb_blob_destroy(mHmtxTable);
+                    mHmtxTable = nullptr;
+                }
+            }
+        }
+    }
+    if (!mHmtxTable) {
+        return false;
+    }
+    return true;
+}
+
+bool
+gfxHarfBuzzShaper::InitializeVertical()
+{
+    if (!mHmtxTable) {
+        if (!LoadHmtxTable()) {
+            return false;
+        }
+    }
+
+    // Load vertical metrics if present in the font; if not, we'll synthesize
+    // vertical glyph advances based on (horizontal) ascent/descent metrics.
+    gfxFontEntry *entry = mFont->GetFontEntry();
+    gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v','h','e','a'));
+    if (vheaTable) {
+        uint32_t len;
+        const MetricsHeader* vhea =
+            reinterpret_cast<const MetricsHeader*>
+            (hb_blob_get_data(vheaTable, &len));
+        if (len >= sizeof(MetricsHeader)) {
+            mNumLongVMetrics = vhea->numOfLongMetrics;
+            if (mNumLongVMetrics > 0 &&
+                int16_t(vhea->metricDataFormat) == 0) {
+                mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v','m','t','x'));
+                if (hb_blob_get_length(mVmtxTable) <
+                    mNumLongVMetrics * sizeof(LongMetric)) {
+                    // metrics table is not large enough for the claimed
+                    // number of entries: invalid, do not use.
+                    hb_blob_destroy(mVmtxTable);
+                    mVmtxTable = nullptr;
+                }
+            }
+        }
+    }
+
+    // For CFF fonts only, load a VORG table if present.
+    if (entry->HasFontTable(TRUETYPE_TAG('C','F','F',' '))) {
+        mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V','O','R','G'));
+        if (mVORGTable) {
+            uint32_t len;
+            const VORG* vorg =
+                reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable,
+                                                               &len));
+            if (len < sizeof(VORG) ||
+                uint16_t(vorg->majorVersion) != 1 ||
+                uint16_t(vorg->minorVersion) != 0 ||
+                len < sizeof(VORG) + uint16_t(vorg->numVertOriginYMetrics) *
+                              sizeof(VORGrec)) {
+                // VORG table is an unknown version, or not large enough
+                // to be valid -- discard it.
+                NS_WARNING("discarding invalid VORG table");
+                hb_blob_destroy(mVORGTable);
+                mVORGTable = nullptr;
+            }
+        }
+    }
+
+    return true;
+}
+
+bool
 gfxHarfBuzzShaper::ShapeText(gfxContext      *aContext,
                              const char16_t *aText,
                              uint32_t         aOffset,
                              uint32_t         aLength,
                              int32_t          aScript,
+                             bool             aVertical,
                              gfxShapedText   *aShapedText)
 {
     // some font back-ends require this in order to get proper hinted metrics
     if (!mFont->SetupCairoFont(aContext)) {
         return false;
     }
 
     mCallbackData.mContext = aContext;
 
     if (!Initialize()) {
         return false;
     }
 
+    if (aVertical) {
+        if (!InitializeVertical()) {
+            return false;
+        }
+    }
+
     const gfxFontStyle *style = mFont->GetStyle();
 
     nsAutoTArray<hb_feature_t,20> features;
     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
 
     // determine whether petite-caps falls back to small-caps
     bool addSmallCaps = false;
     if (style->variantCaps != NS_FONT_VARIANT_CAPS_NORMAL) {
@@ -1002,18 +1209,21 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
     {
         // enumerate result and insert into hb_feature array
         mergedFeatures.Enumerate(AddOpenTypeFeature, &features);
     }
 
     bool isRightToLeft = aShapedText->IsRightToLeft();
     hb_buffer_t *buffer = hb_buffer_create();
     hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
-    hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
-                                                    HB_DIRECTION_LTR);
+
+    hb_buffer_set_direction(buffer,
+                            aVertical ? HB_DIRECTION_TTB :
+                                        (isRightToLeft ? HB_DIRECTION_RTL :
+                                                         HB_DIRECTION_LTR));
     hb_script_t scriptTag;
     if (aShapedText->GetFlags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
         scriptTag = sMathScript;
     } else {
         scriptTag = GetHBScriptUsedForShaping(aScript);
     }
     hb_buffer_set_script(buffer, scriptTag);
 
@@ -1037,35 +1247,36 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
 
     hb_shape(mHBFont, buffer, features.Elements(), features.Length());
 
     if (isRightToLeft) {
         hb_buffer_reverse(buffer);
     }
 
     nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
-                                   aText, buffer);
+                                   aText, buffer, aVertical);
 
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
     hb_buffer_destroy(buffer);
 
     return NS_SUCCEEDED(rv);
 }
 
 #define SMALL_GLYPH_RUN 128 // some testing indicates that 90%+ of text runs
                             // will fit without requiring separate allocation
                             // for charToGlyphArray
 
 nsresult
-gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext      *aContext,
-                                    gfxShapedText   *aShapedText,
-                                    uint32_t         aOffset,
-                                    uint32_t         aLength,
+gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext     *aContext,
+                                    gfxShapedText  *aShapedText,
+                                    uint32_t        aOffset,
+                                    uint32_t        aLength,
                                     const char16_t *aText,
-                                    hb_buffer_t     *aBuffer)
+                                    hb_buffer_t    *aBuffer,
+                                    bool            aVertical)
 {
     uint32_t numGlyphs;
     const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
     if (numGlyphs == 0) {
         return NS_OK;
     }
 
     nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
@@ -1087,19 +1298,23 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
         if (loc < wordLength) {
             charToGlyph[loc] = i;
         }
     }
 
     int32_t glyphStart = 0; // looking for a clump that starts at this glyph
     int32_t charStart = 0; // and this char index within the range of the run
 
-    bool roundX;
-    bool roundY;
-    aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
+    bool roundI;
+    bool roundB;
+    if (aVertical) {
+        aContext->GetRoundOffsetsToPixels(&roundB, &roundI);
+    } else {
+        aContext->GetRoundOffsetsToPixels(&roundI, &roundB);
+    }
 
     int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
     gfxShapedText::CompressedGlyph *charGlyphs =
         aShapedText->GetCharacterGlyphs() + aOffset;
 
     // factor to convert 16.16 fixed-point pixels to app units
     // (only used if not rounding)
     double hb2appUnits = FixedToFloat(aShapedText->GetAppUnitsPerDevUnit());
@@ -1109,20 +1324,20 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
     //
     // When rounding, the goal is to make the distance between glyphs and
     // their base glyph equal to the integral number of pixels closest to that
     // suggested by that shaper.
     // i.e. posInfo[n].x_advance - posInfo[n].x_offset + posInfo[n+1].x_offset
     //
     // The value of the residual is the part of the desired distance that has
     // not been included in integer offsets.
-    hb_position_t x_residual = 0;
+    hb_position_t residual = 0;
 
     // keep track of y-position to set glyph offsets if needed
-    nscoord yPos = 0;
+    nscoord bPos = 0;
 
     const hb_glyph_position_t *posInfo =
         hb_buffer_get_glyph_positions(aBuffer, nullptr);
 
     while (glyphStart < int32_t(numGlyphs)) {
 
         int32_t charEnd = ginfo[glyphStart].cluster;
         int32_t glyphEnd = glyphStart;
@@ -1207,86 +1422,109 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxC
         if (glyphsInClump == 1 && baseCharIndex + 1 == endCharIndex &&
             aShapedText->FilterIfIgnorable(aOffset + baseCharIndex,
                                            aText[baseCharIndex])) {
             glyphStart = glyphEnd;
             charStart = charEnd;
             continue;
         }
 
-        hb_position_t x_offset = posInfo[glyphStart].x_offset;
-        hb_position_t x_advance = posInfo[glyphStart].x_advance;
-        nscoord xOffset, advance;
-        if (roundX) {
-            xOffset =
-                appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
+        // HarfBuzz gives us physical x- and y-coordinates, but we will store
+        // them as logical inline- and block-direction values in the textrun.
+
+        hb_position_t i_offset, i_advance; // inline-direction offset/advance
+        hb_position_t b_offset, b_advance; // block-direction offset/advance
+        if (aVertical) {
+            i_offset = posInfo[glyphStart].y_offset;
+            i_advance = posInfo[glyphStart].y_advance;
+            b_offset = posInfo[glyphStart].x_offset;
+            b_advance = posInfo[glyphStart].x_advance;
+        } else {
+            i_offset = posInfo[glyphStart].x_offset;
+            i_advance = posInfo[glyphStart].x_advance;
+            b_offset = posInfo[glyphStart].y_offset;
+            b_advance = posInfo[glyphStart].y_advance;
+        }
+
+        nscoord iOffset, advance;
+        if (roundI) {
+            iOffset =
+                appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
             // Desired distance from the base glyph to the next reference point.
-            hb_position_t width = x_advance - x_offset;
+            hb_position_t width = i_advance - i_offset;
             int intWidth = FixedToIntRound(width);
-            x_residual = width - FloatToFixed(intWidth);
-            advance = appUnitsPerDevUnit * intWidth + xOffset;
+            residual = width - FloatToFixed(intWidth);
+            advance = appUnitsPerDevUnit * intWidth + iOffset;
         } else {
-            xOffset = floor(hb2appUnits * x_offset + 0.5);
-            advance = floor(hb2appUnits * x_advance + 0.5);
+            iOffset = floor(hb2appUnits * i_offset + 0.5);
+            advance = floor(hb2appUnits * i_advance + 0.5);
         }
         // Check if it's a simple one-to-one mapping
         if (glyphsInClump == 1 &&
             gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
             gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
             charGlyphs[baseCharIndex].IsClusterStart() &&
-            xOffset == 0 &&
-            posInfo[glyphStart].y_offset == 0 && yPos == 0)
+            iOffset == 0 && b_offset == 0 &&
+            b_advance == 0 && bPos == 0)
         {
             charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
                                                      ginfo[glyphStart].codepoint);
         } else {
             // collect all glyphs in a list to be assigned to the first char;
             // there must be at least one in the clump, and we already measured
             // its advance, hence the placement of the loop-exit test and the
             // measurement of the next glyph
             while (1) {
                 gfxTextRun::DetailedGlyph* details =
                     detailedGlyphs.AppendElement();
                 details->mGlyphID = ginfo[glyphStart].codepoint;
 
-                details->mXOffset = xOffset;
+                details->mXOffset = iOffset;
                 details->mAdvance = advance;
 
-                hb_position_t y_offset = posInfo[glyphStart].y_offset;
-                details->mYOffset = yPos -
-                    (roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
-                     : floor(hb2appUnits * y_offset + 0.5));
+                details->mYOffset = bPos -
+                    (roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
+                     : floor(hb2appUnits * b_offset + 0.5));
 
-                hb_position_t y_advance = posInfo[glyphStart].y_advance;
-                if (y_advance != 0) {
-                    yPos -=
-                        roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
-                        : floor(hb2appUnits * y_advance + 0.5);
+                if (b_advance != 0) {
+                    bPos -=
+                        roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
+                        : floor(hb2appUnits * b_advance + 0.5);
                 }
                 if (++glyphStart >= glyphEnd) {
                     break;
                 }
 
-                x_offset = posInfo[glyphStart].x_offset;
-                x_advance = posInfo[glyphStart].x_advance;
-                if (roundX) {
-                    xOffset = appUnitsPerDevUnit *
-                        FixedToIntRound(x_offset + x_residual);
+                if (aVertical) {
+                    i_offset = posInfo[glyphStart].y_offset;
+                    i_advance = posInfo[glyphStart].y_advance;
+                    b_offset = posInfo[glyphStart].x_offset;
+                    b_advance = posInfo[glyphStart].x_advance;
+                } else {
+                    i_offset = posInfo[glyphStart].x_offset;
+                    i_advance = posInfo[glyphStart].x_advance;
+                    b_offset = posInfo[glyphStart].y_offset;
+                    b_advance = posInfo[glyphStart].y_advance;
+                }
+
+                if (roundI) {
+                    iOffset = appUnitsPerDevUnit *
+                        FixedToIntRound(i_offset + residual);
                     // Desired distance to the next reference point.  The
                     // residual is considered here, and includes the residual
                     // from the base glyph offset and subsequent advances, so
                     // that the distance from the base glyph is optimized
                     // rather than the distance from combining marks.
-                    x_advance += x_residual;
-                    int intAdvance = FixedToIntRound(x_advance);
-                    x_residual = x_advance - FloatToFixed(intAdvance);
+                    i_advance += residual;
+                    int intAdvance = FixedToIntRound(i_advance);
+                    residual = i_advance - FloatToFixed(intAdvance);
                     advance = appUnitsPerDevUnit * intAdvance;
                 } else {
-                    xOffset = floor(hb2appUnits * x_offset + 0.5);
-                    advance = floor(hb2appUnits * x_advance + 0.5);
+                    iOffset = floor(hb2appUnits * i_offset + 0.5);
+                    advance = floor(hb2appUnits * i_advance + 0.5);
                 }
             }
 
             gfxShapedText::CompressedGlyph g;
             g.SetComplex(charGlyphs[baseCharIndex].IsClusterStart(),
                          true, detailedGlyphs.Length());
             aShapedText->SetGlyphs(aOffset + baseCharIndex,
                                    g, detailedGlyphs.Elements());
--- a/gfx/thebes/gfxHarfBuzzShaper.h
+++ b/gfx/thebes/gfxHarfBuzzShaper.h
@@ -26,34 +26,57 @@ public:
     };
 
     bool Initialize();
     virtual bool ShapeText(gfxContext      *aContext,
                            const char16_t *aText,
                            uint32_t         aOffset,
                            uint32_t         aLength,
                            int32_t          aScript,
+                           bool             aVertical,
                            gfxShapedText   *aShapedText);
 
     // get a given font table in harfbuzz blob form
     hb_blob_t * GetFontTable(hb_tag_t aTag) const;
 
     // map unicode character to glyph ID
     hb_codepoint_t GetGlyph(hb_codepoint_t unicode,
                             hb_codepoint_t variation_selector) const;
 
     // get harfbuzz glyph advance, in font design units
     hb_position_t GetGlyphHAdvance(gfxContext *aContext,
                                    hb_codepoint_t glyph) const;
 
+    hb_position_t GetGlyphVAdvance(gfxContext *aContext,
+                                   hb_codepoint_t glyph) const;
+
+    void GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
+                         hb_position_t *aX, hb_position_t *aY) const;
+
     // get harfbuzz horizontal advance in 16.16 fixed point format.
     static hb_position_t
     HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
                        hb_codepoint_t glyph, void *user_data);
 
+    // get harfbuzz vertical advance in 16.16 fixed point format.
+    static hb_position_t
+    HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
+                       hb_codepoint_t glyph, void *user_data);
+
+    static hb_bool_t
+    HBGetGlyphHOrigin(hb_font_t *font, void *font_data,
+                      hb_codepoint_t glyph,
+                      hb_position_t *x, hb_position_t *y,
+                      void *user_data);
+    static hb_bool_t
+    HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
+                      hb_codepoint_t glyph,
+                      hb_position_t *x, hb_position_t *y,
+                      void *user_data);
+
     hb_position_t GetHKerning(uint16_t aFirstGlyph,
                               uint16_t aSecondGlyph) const;
 
     static hb_script_t
     GetHBScriptUsedForShaping(int32_t aScript) {
         // Decide what harfbuzz script code will be used for shaping
         hb_script_t hbScript;
         if (aScript <= MOZ_SCRIPT_INHERITED) {
@@ -63,30 +86,34 @@ public:
         } else {
             hbScript =
                 hb_script_t(mozilla::unicode::GetScriptTagForCode(aScript));
         }
         return hbScript;
     }
 
 protected:
-    nsresult SetGlyphsFromRun(gfxContext      *aContext,
-                              gfxShapedText   *aShapedText,
-                              uint32_t         aOffset,
-                              uint32_t         aLength,
+    nsresult SetGlyphsFromRun(gfxContext     *aContext,
+                              gfxShapedText  *aShapedText,
+                              uint32_t        aOffset,
+                              uint32_t        aLength,
                               const char16_t *aText,
-                              hb_buffer_t     *aBuffer);
+                              hb_buffer_t    *aBuffer,
+                              bool            aVertical);
 
     // retrieve glyph positions, applying advance adjustments and attachments
     // returns results in appUnits
     nscoord GetGlyphPositions(gfxContext *aContext,
                               hb_buffer_t *aBuffer,
                               nsTArray<nsPoint>& aPositions,
                               uint32_t aAppUnitsPerDevUnit);
 
+    bool InitializeVertical();
+    bool LoadHmtxTable();
+
     // harfbuzz face object: we acquire a reference from the font entry
     // on shaper creation, and release it in our destructor
     hb_face_t         *mHBFace;
 
     // size-specific font object, owned by the gfxHarfBuzzShaper
     hb_font_t         *mHBFont;
 
     FontCallbackData   mCallbackData;
@@ -94,35 +121,44 @@ protected:
     // Following table references etc are declared "mutable" because the
     // harfbuzz callback functions take a const ptr to the shaper, but
     // wish to cache tables here to avoid repeatedly looking them up
     // in the font.
 
     // Old-style TrueType kern table, if we're not doing GPOS kerning
     mutable hb_blob_t *mKernTable;
 
-    // Cached copy of the hmtx table and numLongMetrics field from hhea,
-    // for use when looking up glyph metrics; initialized to 0 by the
-    // constructor so we can tell it hasn't been set yet.
-    // This is a signed value so that we can use -1 to indicate
-    // an error (if the hhea table was not available).
+    // Cached copy of the hmtx table.
     mutable hb_blob_t *mHmtxTable;
-    mutable int32_t    mNumLongMetrics;
+
+    // For vertical fonts, cached vmtx and VORG table, if present.
+    mutable hb_blob_t *mVmtxTable;
+    mutable hb_blob_t *mVORGTable;
 
     // Cached pointer to cmap subtable to be used for char-to-glyph mapping.
     // This comes from GetFontTablePtr; if it is non-null, our destructor
     // must call ReleaseFontTablePtr to avoid permanently caching the table.
     mutable hb_blob_t *mCmapTable;
     mutable int32_t    mCmapFormat;
     mutable uint32_t   mSubtableOffset;
     mutable uint32_t   mUVSTableOffset;
 
+    // Cached copy of numLongMetrics field from the hhea table,
+    // for use when looking up glyph metrics; initialized to 0 by the
+    // constructor so we can tell it hasn't been set yet.
+    // This is a signed value so that we can use -1 to indicate
+    // an error (if the hhea table was not available).
+    mutable int32_t    mNumLongHMetrics;
+    // Similarly for vhea if it's a vertical font.
+    mutable int32_t    mNumLongVMetrics;
+
     // Whether the font implements GetGlyph, or we should read tables
     // directly
     bool mUseFontGetGlyph;
     // Whether the font implements GetGlyphWidth, or we should read tables
     // directly to get ideal widths
     bool mUseFontGlyphWidths;
 
     bool mInitialized;
+    bool mVerticalInitialized;
 };
 
 #endif /* GFX_HARFBUZZSHAPER_H */
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -119,36 +119,40 @@ gfxMacFont::~gfxMacFont()
 }
 
 bool
 gfxMacFont::ShapeText(gfxContext     *aContext,
                       const char16_t *aText,
                       uint32_t        aOffset,
                       uint32_t        aLength,
                       int32_t         aScript,
+                      bool            aVertical,
                       gfxShapedText  *aShapedText)
 {
     if (!mIsValid) {
         NS_WARNING("invalid font! expect incorrect text rendering");
         return false;
     }
 
-    if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout()) {
+    // Currently, we don't support vertical shaping via CoreText,
+    // so we ignore RequiresAATLayout if vertical is requested.
+    if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout() &&
+        !aVertical) {
         if (!mCoreTextShaper) {
             mCoreTextShaper = new gfxCoreTextShaper(this);
         }
         if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength,
-                                       aScript, aShapedText)) {
+                                       aScript, aVertical, aShapedText)) {
             PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
             return true;
         }
     }
 
     return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
-                              aShapedText);
+                              aVertical, aShapedText);
 }
 
 bool
 gfxMacFont::SetupCairoFont(gfxContext *aContext)
 {
     if (cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) {
         // Don't cairo_set_scaled_font as that would propagate the error to
         // the cairo_t, precluding any further drawing.
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -52,16 +52,17 @@ protected:
     }
 
     // override to prefer CoreText shaping with fonts that depend on AAT
     virtual bool ShapeText(gfxContext     *aContext,
                            const char16_t *aText,
                            uint32_t        aOffset,
                            uint32_t        aLength,
                            int32_t         aScript,
+                           bool            aVertical,
                            gfxShapedText  *aShapedText);
 
     void InitMetrics();
     void InitMetricsFromPlatform();
 
     // Get width and glyph ID for a character; uses aConvFactor
     // to convert font units as returned by CG to actual dimensions
     gfxFloat GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -392,85 +392,115 @@ gfxTextRun::ShrinkToLigatureBoundaries(u
         }
     }
 }
 
 void
 gfxTextRun::DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
                        gfxPoint *aPt, PropertyProvider *aProvider,
                        uint32_t aSpacingStart, uint32_t aSpacingEnd,
-                       TextRunDrawParams& aParams)
+                       TextRunDrawParams& aParams, uint16_t aOrientation)
 {
     nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
         aSpacingStart, aSpacingEnd, &spacingBuffer);
     aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
-    aFont->Draw(this, aStart, aEnd, aPt, aParams);
+    aFont->Draw(this, aStart, aEnd, aPt, aParams, aOrientation);
 }
 
 static void
-ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
-                    gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
+ClipPartialLigature(const gfxTextRun* aTextRun,
+                    gfxFloat *aStart, gfxFloat *aEnd,
+                    gfxFloat aOrigin,
+                    gfxTextRun::LigatureData *aLigature)
 {
     if (aLigature->mClipBeforePart) {
         if (aTextRun->IsRightToLeft()) {
-            *aRight = std::min(*aRight, aXOrigin);
+            *aEnd = std::min(*aEnd, aOrigin);
         } else {
-            *aLeft = std::max(*aLeft, aXOrigin);
+            *aStart = std::max(*aStart, aOrigin);
         }
     }
     if (aLigature->mClipAfterPart) {
-        gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
+        gfxFloat endEdge =
+            aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
         if (aTextRun->IsRightToLeft()) {
-            *aLeft = std::max(*aLeft, endEdge);
+            *aStart = std::max(*aStart, endEdge);
         } else {
-            *aRight = std::min(*aRight, endEdge);
+            *aEnd = std::min(*aEnd, endEdge);
         }
     }    
 }
 
 void
 gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
                                 gfxPoint *aPt, PropertyProvider *aProvider,
-                                TextRunDrawParams& aParams)
+                                TextRunDrawParams& aParams, uint16_t aOrientation)
 {
-    if (aStart >= aEnd)
+    if (aStart >= aEnd) {
         return;
+    }
 
     // Draw partial ligature. We hack this by clipping the ligature.
     LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
     gfxRect clipExtents = aParams.context->GetClipExtents();
-    gfxFloat left = clipExtents.X() * mAppUnitsPerDevUnit;
-    gfxFloat right = clipExtents.XMost() * mAppUnitsPerDevUnit;
-    ClipPartialLigature(this, &left, &right, aPt->x, &data);
+    gfxFloat start, end;
+    if (aParams.isVerticalRun) {
+        start = clipExtents.Y() * mAppUnitsPerDevUnit;
+        end = clipExtents.YMost() * mAppUnitsPerDevUnit;
+        ClipPartialLigature(this, &start, &end, aPt->y, &data);
+    } else {
+        start = clipExtents.X() * mAppUnitsPerDevUnit;
+        end = clipExtents.XMost() * mAppUnitsPerDevUnit;
+        ClipPartialLigature(this, &start, &end, aPt->x, &data);
+    }
 
     {
       // Need to preserve the path, otherwise this can break canvas text-on-path;
       // in general it seems like a good thing, as naive callers probably won't
       // expect gfxTextRun::Draw to implicitly destroy the current path.
       gfxContextPathAutoSaveRestore savePath(aParams.context);
 
       // use division here to ensure that when the rect is aligned on multiples
       // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
       // Also, make sure we snap the rectangle to device pixels.
       aParams.context->Save();
       aParams.context->NewPath();
-      aParams.context->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
-                                         clipExtents.Y(),
-                                         (right - left) / mAppUnitsPerDevUnit,
-                                         clipExtents.Height()), true);
+      if (aParams.isVerticalRun) {
+          aParams.context->Rectangle(gfxRect(clipExtents.X(),
+                                             start / mAppUnitsPerDevUnit,
+                                             clipExtents.Width(),
+                                             (end - start) / mAppUnitsPerDevUnit),
+                                     true);
+      } else {
+          aParams.context->Rectangle(gfxRect(start / mAppUnitsPerDevUnit,
+                                             clipExtents.Y(),
+                                             (end - start) / mAppUnitsPerDevUnit,
+                                             clipExtents.Height()),
+                                     true);
+      }
       aParams.context->Clip();
     }
 
-    gfxPoint pt(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
+    gfxPoint pt;
+    if (aParams.isVerticalRun) {
+        pt = gfxPoint(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
+    } else {
+        pt = gfxPoint(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
+    }
+
     DrawGlyphs(aFont, data.mLigatureStart, data.mLigatureEnd, &pt,
-               aProvider, aStart, aEnd, aParams);
+               aProvider, aStart, aEnd, aParams, aOrientation);
     aParams.context->Restore();
 
-    aPt->x += aParams.direction * data.mPartWidth;
+    if (aParams.isVerticalRun) {
+        aPt->y += aParams.direction * data.mPartWidth;
+    } else {
+        aPt->x += aParams.direction * data.mPartWidth;
+    }
 }
 
 // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
 static bool
 HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
 {
     gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
     while (iter.NextRun()) {
@@ -572,26 +602,25 @@ gfxTextRun::Draw(gfxContext *aContext, g
         return;
     }
 
     // Set up parameters that will be constant across all glyph runs we need
     // to draw, regardless of the font used.
     TextRunDrawParams params;
     params.context = aContext;
     params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
+    params.isVerticalRun = IsVertical();
     params.isRTL = IsRightToLeft();
     params.direction = direction;
     params.drawMode = aDrawMode;
     params.callbacks = aCallbacks;
     params.runContextPaint = aContextPaint;
     params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
     params.dt = aContext->GetDrawTarget();
 
-    gfxPoint pt = aPt;
-
     // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
     // correctly unless first drawn without alpha
     BufferAlphaColor syntheticBoldBuffer(aContext);
     gfxRGBA currentColor;
     bool needToRestore = false;
 
     if (aDrawMode == DrawMode::GLYPH_FILL &&
         HasNonOpaqueColor(aContext, currentColor) &&
@@ -602,48 +631,60 @@ gfxTextRun::Draw(gfxContext *aContext, g
                                                   gfxFont::LOOSE_INK_EXTENTS,
                                                   aContext, aProvider);
         metrics.mBoundingBox.MoveBy(aPt);
         syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
                                            GetAppUnitsPerDevUnit());
     }
 
     GlyphRunIterator iter(this, aStart, aLength);
+    gfxFloat advance = 0.0;
+
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
         uint32_t start = iter.GetStringStart();
         uint32_t end = iter.GetStringEnd();
         uint32_t ligatureRunStart = start;
         uint32_t ligatureRunEnd = end;
         ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
 
         bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
                            (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
+        gfxPoint origPt = aPt;
 
         if (drawPartial) {
-            DrawPartialLigature(font, start, ligatureRunStart, &pt,
-                                aProvider, params);
+            DrawPartialLigature(font, start, ligatureRunStart, &aPt,
+                                aProvider, params,
+                                iter.GetGlyphRun()->mOrientation);
         }
 
-        DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &pt,
-                   aProvider, ligatureRunStart, ligatureRunEnd, params);
+        DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &aPt,
+                   aProvider, ligatureRunStart, ligatureRunEnd, params,
+                   iter.GetGlyphRun()->mOrientation);
 
         if (drawPartial) {
-            DrawPartialLigature(font, ligatureRunEnd, end, &pt,
-                                aProvider, params);
+            DrawPartialLigature(font, ligatureRunEnd, end, &aPt,
+                                aProvider, params,
+                                iter.GetGlyphRun()->mOrientation);
+        }
+
+        if (params.isVerticalRun) {
+            advance += (aPt.y - origPt.y) * params.direction;
+        } else {
+            advance += (aPt.x - origPt.x) * params.direction;
         }
     }
 
     // composite result when synthetic bolding used
     if (needToRestore) {
         syntheticBoldBuffer.PopAlpha();
     }
 
     if (aAdvanceWidth) {
-        *aAdvanceWidth = (pt.x - aPt.x)*direction;
+        *aAdvanceWidth = advance;
     }
 }
 
 void
 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
                                     uint32_t aStart, uint32_t aEnd,
                                     gfxFont::BoundingBoxType aBoundingBoxType,
                                     gfxContext *aRefContext,
@@ -1235,20 +1276,23 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont
     }
 
     aFont->InitWordCache();
     static const uint8_t space = ' ';
     uint32_t flags = gfxTextRunFactory::TEXT_IS_8BIT |
                      gfxTextRunFactory::TEXT_IS_ASCII |
                      gfxTextRunFactory::TEXT_IS_PERSISTENT |
                      aOrientation;
+    bool vertical =
+        (GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0;
     gfxShapedWord *sw = aFont->GetShapedWord(aContext,
                                              &space, 1,
                                              gfxShapedWord::HashMix(0, ' '), 
                                              MOZ_SCRIPT_LATIN,
+                                             vertical,
                                              mAppUnitsPerDevUnit,
                                              flags,
                                              nullptr);
     if (sw) {
         AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false,
                     aOrientation);
         CopyGlyphDataFrom(sw, aCharIndex);
     }
@@ -2219,29 +2263,31 @@ gfxFontGroup::InitScriptRun(gfxContext *
     ComputeRanges(fontRanges, aString, aLength, aRunScript,
                   aTextRun->GetFlags() & gfxTextRunFactory::TEXT_ORIENT_MASK);
     uint32_t numRanges = fontRanges.Length();
 
     for (uint32_t r = 0; r < numRanges; r++) {
         const gfxTextRange& range = fontRanges[r];
         uint32_t matchedLength = range.Length();
         gfxFont *matchedFont = range.font;
-
+        bool vertical =
+            range.orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
         // create the glyph run for this range
         if (matchedFont && mStyle.noFallbackVariantFeatures) {
             // common case - just do glyph layout and record the
             // resulting positioned glyphs
             aTextRun->AddGlyphRun(matchedFont, range.matchType,
                                   aOffset + runStart, (matchedLength > 0),
                                   range.orientation);
             if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
                                                   aString + runStart,
                                                   aOffset + runStart,
                                                   matchedLength,
-                                                  aRunScript)) {
+                                                  aRunScript,
+                                                  vertical)) {
                 // glyph layout failed! treat as missing glyphs
                 matchedFont = nullptr;
             }
         } else if (matchedFont) {
             // shape with some variant feature that requires fallback handling
             bool petiteToSmallCaps = false;
             bool syntheticLower = false;
             bool syntheticUpper = false;
@@ -2270,17 +2316,18 @@ gfxFontGroup::InitScriptRun(gfxContext *
                     matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
                 aTextRun->AddGlyphRun(subSuperFont, range.matchType,
                                       aOffset + runStart, (matchedLength > 0),
                                       range.orientation);
                 if (!subSuperFont->SplitAndInitTextRun(aContext, aTextRun,
                                                        aString + runStart,
                                                        aOffset + runStart,
                                                        matchedLength,
-                                                       aRunScript)) {
+                                                       aRunScript,
+                                                       vertical)) {
                     // glyph layout failed! treat as missing glyphs
                     matchedFont = nullptr;
                 }
             } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
                        !matchedFont->SupportsVariantCaps(aRunScript,
                                                          mStyle.variantCaps,
                                                          petiteToSmallCaps,
                                                          syntheticLower,
@@ -2314,17 +2361,18 @@ gfxFontGroup::InitScriptRun(gfxContext *
                 // do glyph layout and record the resulting positioned glyphs
                 aTextRun->AddGlyphRun(matchedFont, range.matchType,
                                       aOffset + runStart, (matchedLength > 0),
                                       range.orientation);
                 if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
                                                       aString + runStart,
                                                       aOffset + runStart,
                                                       matchedLength,
-                                                      aRunScript)) {
+                                                      aRunScript,
+                                                      vertical)) {
                     // glyph layout failed! treat as missing glyphs
                     matchedFont = nullptr;
                 }
             }
         } else {
             aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
                                   aOffset + runStart, (matchedLength > 0),
                                   range.orientation);
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -668,17 +668,17 @@ private:
 
     // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
     LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
                                      PropertyProvider *aProvider);
     gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
                                          PropertyProvider *aProvider);
     void DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
                              gfxPoint *aPt, PropertyProvider *aProvider,
-                             TextRunDrawParams& aParams);
+                             TextRunDrawParams& aParams, uint16_t aOrientation);
     // Advance aStart to the start of the nearest ligature; back up aEnd
     // to the nearest ligature end; may result in *aStart == *aEnd
     void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd);
     // result in appunits
     gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider);
     void AccumulatePartialLigatureMetrics(gfxFont *aFont,
                                           uint32_t aStart, uint32_t aEnd,
                                           gfxFont::BoundingBoxType aBoundingBoxType,
@@ -693,17 +693,17 @@ private:
                                  PropertyProvider *aProvider,
                                  uint32_t aSpacingStart, uint32_t aSpacingEnd,
                                  Metrics *aMetrics);
 
     // **** drawing helper ****
     void DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
                     gfxPoint *aPt, PropertyProvider *aProvider,
                     uint32_t aSpacingStart, uint32_t aSpacingEnd,
-                    TextRunDrawParams& aParams);
+                    TextRunDrawParams& aParams, uint16_t aOrientation);
 
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>        mGlyphRuns;
 
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed on creation, but our reference
                                   // may be released by ReleaseFontGroup()
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -726,21 +726,29 @@ MessageChannel::SendAndWait(Message* aMs
                 ReportConnectionError("MessageChannel::SendAndWait");
                 return false;
             }
 
             if (maybeTimedOut && !ShouldContinueFromTimeout())
                 return false;
         }
 
-        if (mPendingUrgentRequest && !ProcessPendingUrgentRequest())
-            return false;
+        // We need to make sure that all messages deposited in mPendingRPCCall
+        // and mPendingUrgentRequest are dispatched before we leave this
+        // function. Otherwise, there's nothing to wake us up and force us to
+        // dispatch them.
+        while (mPendingUrgentRequest) {
+            if (!ProcessPendingUrgentRequest())
+                return false;
+        }
 
-        if (mPendingRPCCall && !ProcessPendingRPCCall())
-            return false;
+        while (mPendingRPCCall) {
+            if (!ProcessPendingRPCCall())
+                return false;
+        }
 
         if (mRecvd) {
             NS_ABORT_IF_FALSE(mRecvd->is_reply(), "expected reply");
 
             if (mRecvd->is_reply_error()) {
                 mRecvd = nullptr;
                 return false;
             }
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -92,16 +92,19 @@ class JavaScriptBase : public WrapperOwn
     }
     bool AnswerObjectClassIs(const uint64_t &objId, const uint32_t &classValue,
                              bool *result) {
         return Answer::AnswerObjectClassIs(ObjectId::deserialize(objId), classValue, result);
     }
     bool AnswerClassName(const uint64_t &objId, nsString *result) {
         return Answer::AnswerClassName(ObjectId::deserialize(objId), result);
     }
+    bool AnswerRegExpToShared(const uint64_t &objId, ReturnStatus *rs, nsString *source, uint32_t *flags) {
+        return Answer::AnswerRegExpToShared(ObjectId::deserialize(objId), rs, source, flags);
+    }
 
     bool AnswerGetPropertyNames(const uint64_t &objId, const uint32_t &flags,
                                 ReturnStatus *rs, nsTArray<nsString> *names) {
         return Answer::AnswerGetPropertyNames(ObjectId::deserialize(objId), flags, rs, names);
     }
     bool AnswerInstanceOf(const uint64_t &objId, const JSIID &iid,
                           ReturnStatus *rs, bool *instanceof) {
         return Answer::AnswerInstanceOf(ObjectId::deserialize(objId), iid, rs, instanceof);
@@ -186,16 +189,21 @@ class JavaScriptBase : public WrapperOwn
     bool CallObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                            bool *result) {
         return Base::CallObjectClassIs(objId.serialize(), classValue, result);
     }
     bool CallClassName(const ObjectId &objId, nsString *result) {
         return Base::CallClassName(objId.serialize(), result);
     }
 
+    bool CallRegExpToShared(const ObjectId &objId, ReturnStatus *rs,
+                            nsString *source, uint32_t *flags) {
+        return Base::CallRegExpToShared(objId.serialize(), rs, source, flags);
+    }
+
     bool CallGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
                               ReturnStatus *rs, nsTArray<nsString> *names) {
         return Base::CallGetPropertyNames(objId.serialize(), flags, rs, names);
     }
     bool CallInstanceOf(const ObjectId &objId, const JSIID &iid,
                         ReturnStatus *rs, bool *instanceof) {
         return Base::CallInstanceOf(objId.serialize(), iid, rs, instanceof);
     }
--- a/js/ipc/JavaScriptShared.cpp
+++ b/js/ipc/JavaScriptShared.cpp
@@ -413,17 +413,17 @@ static const uint64_t UnknownPropertyOp 
 bool
 JavaScriptShared::fromDescriptor(JSContext *cx, Handle<JSPropertyDescriptor> desc,
                                  PPropertyDescriptor *out)
 {
     out->attrs() = desc.attributes();
     if (!toVariant(cx, desc.value(), &out->value()))
         return false;
 
-    JS_ASSERT(desc.object());
+    MOZ_ASSERT(desc.object());
     if (!toObjectVariant(cx, desc.object(), &out->obj()))
         return false;
 
     if (!desc.getter()) {
         out->getter() = 0;
     } else if (desc.hasGetterObject()) {
         JSObject *getter = desc.getterObject();
         ObjectVariant objVar;
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -35,16 +35,17 @@ both:
     rpc Get(uint64_t objId, ObjectVariant receiver, nsString id) returns (ReturnStatus rs, JSVariant result);
     rpc Set(uint64_t objId, ObjectVariant receiver, nsString id, bool strict, JSVariant value) returns (ReturnStatus rs, JSVariant result);
 
     rpc IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     rpc CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     rpc HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
     rpc ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
     rpc ClassName(uint64_t objId) returns (nsString name);
+    rpc RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
 
     rpc GetPropertyNames(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, nsString[] names);
     rpc InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
     rpc DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
 
     rpc IsCallable(uint64_t objId) returns (bool result);
     rpc IsConstructor(uint64_t objId) returns (bool result);
 
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -548,16 +548,41 @@ WrapperAnswer::AnswerClassName(const Obj
 
     LOG("%s.className()", ReceiverObj(objId));
 
     *name = NS_ConvertASCIItoUTF16(js_ObjectClassName(cx, obj));
     return true;
 }
 
 bool
+WrapperAnswer::AnswerRegExpToShared(const ObjectId &objId, ReturnStatus *rs,
+                                    nsString *source, uint32_t *flags)
+{
+    AutoSafeJSContext cx;
+    RootedObject obj(cx, findObjectById(cx, objId));
+    if (!obj)
+        return fail(cx, rs);
+
+    JSAutoCompartment ac(cx, obj);
+    MOZ_RELEASE_ASSERT(JS_ObjectIsRegExp(cx, obj));
+
+    RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
+    if (!sourceJSStr)
+        return fail(cx, rs);
+    nsAutoJSString sourceStr;
+    if (!sourceStr.init(cx, sourceJSStr))
+        return fail(cx, rs);
+    source->Assign(sourceStr);
+
+    *flags = JS_GetRegExpFlags(cx, obj);
+
+    return ok(rs);
+}
+
+bool
 WrapperAnswer::AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
 				      ReturnStatus *rs, nsTArray<nsString> *names)
 {
     AutoSafeJSContext cx;
     JSAutoRequest request(cx);
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -47,16 +47,17 @@ class WrapperAnswer : public virtual Jav
                             bool *result);
     bool AnswerCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
                                const bool &construct, ReturnStatus *rs, JSVariant *result,
                                nsTArray<JSParam> *outparams);
     bool AnswerHasInstance(const ObjectId &objId, const JSVariant &v, ReturnStatus *rs, bool *bp);
     bool AnswerObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                              bool *result);
     bool AnswerClassName(const ObjectId &objId, nsString *result);
+    bool AnswerRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source, uint32_t *flags);
 
     bool AnswerGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
                                 ReturnStatus *rs, nsTArray<nsString> *names);
     bool AnswerInstanceOf(const ObjectId &objId, const JSIID &iid,
                           ReturnStatus *rs, bool *instanceof);
     bool AnswerDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
                              ReturnStatus *rs, bool *instanceof);
 
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -86,16 +86,17 @@ class CPOWProxyHandler : public BaseProx
     virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) const MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy,
                              MutableHandleValue v, bool *bp) const MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
                                JSContext *cx) const MOZ_OVERRIDE;
     virtual const char* className(JSContext *cx, HandleObject proxy) const MOZ_OVERRIDE;
+    virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const MOZ_OVERRIDE;
     virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
     virtual void objectMoved(JSObject *proxy, const JSObject *old) const MOZ_OVERRIDE;
     virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE;
     virtual bool isConstructor(JSObject *obj) const MOZ_OVERRIDE;
 
     static const char family;
     static const CPOWProxyHandler singleton;
 };
@@ -646,16 +647,47 @@ WrapperOwner::className(JSContext *cx, H
     if (!CallClassName(objId, &name))
         return "<error>";
 
     LOG_STACK();
 
     return ToNewCString(name);
 }
 
+bool
+CPOWProxyHandler::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) const
+{
+    FORWARD(regexp_toShared, (cx, proxy, g));
+}
+
+bool
+WrapperOwner::regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g)
+{
+    ObjectId objId = idOf(proxy);
+
+    ReturnStatus status;
+    nsString source;
+    unsigned flags = 0;
+    if (!CallRegExpToShared(objId, &status, &source, &flags))
+        return ipcfail(cx);
+
+    LOG_STACK();
+
+    if (!ok(cx, status))
+        return false;
+
+    RootedObject regexp(cx);
+    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+    regexp = JS_NewUCRegExpObject(cx, global, source.get(), source.Length(), flags);
+    if (!regexp)
+        return false;
+
+    return js::RegExpToSharedNonInline(cx, regexp, g);
+}
+
 void
 CPOWProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy) const
 {
     OwnerOf(proxy)->drop(proxy);
 }
 
 void
 CPOWProxyHandler::objectMoved(JSObject *proxy, const JSObject *old) const
@@ -860,17 +892,17 @@ WrapperOwner::ok(JSContext *cx, const Re
     JS_SetPendingException(cx, exn);
     return false;
 }
 
 bool
 WrapperOwner::toObjectVariant(JSContext *cx, JSObject *objArg, ObjectVariant *objVarp)
 {
     RootedObject obj(cx, objArg);
-    JS_ASSERT(obj);
+    MOZ_ASSERT(obj);
 
     // We always save objects unwrapped in the CPOW table. If we stored
     // wrappers, then the wrapper might be GCed while the target remained alive.
     // Whenever operating on an object that comes from the table, we wrap it
     // in findObjectById.
     unsigned wrapperFlags = 0;
     obj = js::UncheckedUnwrap(obj, false, &wrapperFlags);
     if (obj && IsCPOW(obj) && OwnerOf(obj) == this) {
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_jsipc_WrapperOwner_h__
 #define mozilla_jsipc_WrapperOwner_h__
 
 #include "JavaScriptShared.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "js/Class.h"
+#include "jsproxy.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #undef GetClassInfo
 #endif
 
 namespace mozilla {
 namespace jsipc {
@@ -50,16 +51,17 @@ class WrapperOwner : public virtual Java
              JS::HandleId id, JS::MutableHandleValue vp);
     bool set(JSContext *cx, JS::HandleObject proxy, JS::HandleObject receiver,
              JS::HandleId id, bool strict, JS::MutableHandleValue vp);
     bool keys(JSContext *cx, JS::HandleObject proxy, JS::AutoIdVector &props);
     // We use "iterate" provided by the base class here.
 
     // SpiderMonkey Extensions.
     bool isExtensible(JSContext *cx, JS::HandleObject proxy, bool *extensible);
+    bool regexp_toShared(JSContext *cx, JS::HandleObject proxy, js::RegExpGuard *g);
     bool callOrConstruct(JSContext *cx, JS::HandleObject proxy, const JS::CallArgs &args,
                          bool construct);
     bool hasInstance(JSContext *cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool *bp);
     bool objectClassIs(JSContext *cx, JS::HandleObject obj, js::ESClassValue classValue);
     const char* className(JSContext *cx, JS::HandleObject proxy);
     bool isCallable(JSObject *obj);
     bool isConstructor(JSObject *obj);
 
@@ -135,16 +137,18 @@ class WrapperOwner : public virtual Java
     virtual bool CallCallOrConstruct(const ObjectId &objId, const nsTArray<JSParam> &argv,
                                      const bool &construct, ReturnStatus *rs, JSVariant *result,
                                      nsTArray<JSParam> *outparams) = 0;
     virtual bool CallHasInstance(const ObjectId &objId, const JSVariant &v,
                                  ReturnStatus *rs, bool *bp) = 0;
     virtual bool CallObjectClassIs(const ObjectId &objId, const uint32_t &classValue,
                                    bool *result) = 0;
     virtual bool CallClassName(const ObjectId &objId, nsString *result) = 0;
+    virtual bool CallRegExpToShared(const ObjectId &objId, ReturnStatus *rs, nsString *source,
+                                    uint32_t *flags) = 0;
 
     virtual bool CallGetPropertyNames(const ObjectId &objId, const uint32_t &flags,
                                       ReturnStatus *rs, nsTArray<nsString> *names) = 0;
     virtual bool CallInstanceOf(const ObjectId &objId, const JSIID &iid,
                                 ReturnStatus *rs, bool *instanceof) = 0;
     virtual bool CallDOMInstanceOf(const ObjectId &objId, const int &prototypeID, const int &depth,
                                    ReturnStatus *rs, bool *instanceof) = 0;
 
--- a/js/public/HashTable.h
+++ b/js/public/HashTable.h
@@ -954,17 +954,17 @@ class HashTable : private AllocPolicy
             this->mutationCount = table_.mutationCount;
 #endif
         }
 
         // Removes the |front()| element and re-inserts it into the table with
         // a new key at the new Lookup position.  |front()| is invalid after
         // this operation until the next call to |popFront()|.
         void rekeyFront(const Lookup &l, const Key &k) {
-            JS_ASSERT(&k != &HashPolicy::getKey(this->cur->get()));
+            MOZ_ASSERT(&k != &HashPolicy::getKey(this->cur->get()));
             Ptr p(*this->cur, table_);
             table_.rekeyWithoutRehash(p, l, k);
             rekeyed = true;
 #ifdef JS_DEBUG
             this->validEntry = false;
             this->mutationCount = table_.mutationCount;
 #endif
         }
--- a/js/public/HeapAPI.h
+++ b/js/public/HeapAPI.h
@@ -255,17 +255,17 @@ IsInsideNursery(const js::gc::Cell *cell
 {
 #ifdef JSGC_GENERATIONAL
     if (!cell)
         return false;
     uintptr_t addr = uintptr_t(cell);
     addr &= ~js::gc::ChunkMask;
     addr |= js::gc::ChunkLocationOffset;
     uint32_t location = *reinterpret_cast<uint32_t *>(addr);
-    JS_ASSERT(location != 0);
+    MOZ_ASSERT(location != 0);
     return location & ChunkLocationAnyNursery;
 #else
     return false;
 #endif
 }
 
 } /* namespace gc */
 
@@ -273,17 +273,17 @@ IsInsideNursery(const js::gc::Cell *cell
 
 namespace JS {
 
 static MOZ_ALWAYS_INLINE Zone *
 GetTenuredGCThingZone(void *thing)
 {
     MOZ_ASSERT(thing);
 #ifdef JSGC_GENERATIONAL
-    JS_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing));
+    MOZ_ASSERT(!js::gc::IsInsideNursery((js::gc::Cell *)thing));
 #endif
     return js::gc::GetGCThingArena(thing)->zone;
 }
 
 extern JS_PUBLIC_API(Zone *)
 GetObjectZone(JSObject *obj);
 
 static MOZ_ALWAYS_INLINE bool
--- a/js/public/Id.h
+++ b/js/public/Id.h
@@ -122,18 +122,18 @@ JSID_TO_SYMBOL(jsid id)
 }
 
 static MOZ_ALWAYS_INLINE jsid
 SYMBOL_TO_JSID(JS::Symbol *sym)
 {
     jsid id;
     MOZ_ASSERT(sym != nullptr);
     MOZ_ASSERT((size_t(sym) & JSID_TYPE_MASK) == 0);
-    JS_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(sym)));
-    JS_ASSERT(!JS::IsPoisonedPtr(sym));
+    MOZ_ASSERT(!js::gc::IsInsideNursery(JS::AsCell(sym)));
+    MOZ_ASSERT(!JS::IsPoisonedPtr(sym));
     JSID_BITS(id) = (size_t(sym) | JSID_TYPE_SYMBOL);
     return id;
 }
 
 static MOZ_ALWAYS_INLINE bool
 JSID_IS_GCTHING(jsid id)
 {
     return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id);
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -29,21 +29,21 @@ namespace JS {
 // unwound.
 class JS_PUBLIC_API(ProfilingFrameIterator)
 {
     js::Activation *activation_;
 
     static const unsigned StorageSpace = 6 * sizeof(void*);
     mozilla::AlignedStorage<StorageSpace> storage_;
     js::AsmJSProfilingFrameIterator &asmJSIter() {
-        JS_ASSERT(!done());
+        MOZ_ASSERT(!done());
         return *reinterpret_cast<js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
     const js::AsmJSProfilingFrameIterator &asmJSIter() const {
-        JS_ASSERT(!done());
+        MOZ_ASSERT(!done());
         return *reinterpret_cast<const js::AsmJSProfilingFrameIterator*>(storage_.addr());
     }
 
     void settle();
 
   public:
     struct RegisterState
     {
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -665,17 +665,17 @@ struct GCMethods<T *>
 template <>
 struct GCMethods<JSObject *>
 {
     static JSObject *initial() { return nullptr; }
     static bool poisoned(JSObject *v) { return JS::IsPoisonedPtr(v); }
     static gc::Cell *asGCThingOrNull(JSObject *v) {
         if (!v)
             return nullptr;
-        JS_ASSERT(uintptr_t(v) > 32);
+        MOZ_ASSERT(uintptr_t(v) > 32);
         return reinterpret_cast<gc::Cell *>(v);
     }
     static bool needsPostBarrier(JSObject *v) {
         return v != nullptr && gc::IsInsideNursery(reinterpret_cast<gc::Cell *>(v));
     }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JSObject **vp) {
         JS::HeapCellPostBarrier(reinterpret_cast<js::gc::Cell **>(vp));
--- a/js/public/Utility.h
+++ b/js/public/Utility.h
@@ -46,19 +46,16 @@ namespace js {}
 #define JS_FRESH_TENURED_PATTERN 0x4F
 #define JS_MOVED_TENURED_PATTERN 0x49
 #define JS_SWEPT_TENURED_PATTERN 0x4B
 #define JS_ALLOCATED_TENURED_PATTERN 0x4D
 #define JS_SWEPT_CODE_PATTERN 0x3b
 #define JS_SWEPT_FRAME_PATTERN 0x5b
 #define JS_POISONED_FORKJOIN_CHUNK 0xBD
 
-#define JS_ASSERT(expr)           MOZ_ASSERT(expr)
-#define JS_ASSERT_IF(cond, expr)  MOZ_ASSERT_IF(cond, expr)
-
 #define JS_STATIC_ASSERT(cond)           static_assert(cond, "JS_STATIC_ASSERT")
 #define JS_STATIC_ASSERT_IF(cond, expr)  MOZ_STATIC_ASSERT_IF(cond, expr, "JS_STATIC_ASSERT_IF")
 
 extern MOZ_NORETURN JS_PUBLIC_API(void)
 JS_Assert(const char *s, const char *file, int ln);
 
 /*
  * Custom allocator support for SpiderMonkey
--- a/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -49,61 +49,61 @@ AsmJSFrameIterator::AsmJSFrameIterator(c
     if (!fp_)
         return;
     settle();
 }
 
 void
 AsmJSFrameIterator::operator++()
 {
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
     DebugOnly<uint8_t*> oldfp = fp_;
     fp_ += callsite_->stackDepth();
-    JS_ASSERT_IF(module_->profilingEnabled(), fp_ == CallerFPFromFP(oldfp));
+    MOZ_ASSERT_IF(module_->profilingEnabled(), fp_ == CallerFPFromFP(oldfp));
     settle();
 }
 
 void
 AsmJSFrameIterator::settle()
 {
     void *returnAddress = ReturnAddressFromFP(fp_);
 
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(returnAddress);
-    JS_ASSERT(codeRange);
+    MOZ_ASSERT(codeRange);
     codeRange_ = codeRange;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Function:
         callsite_ = module_->lookupCallSite(returnAddress);
-        JS_ASSERT(callsite_);
+        MOZ_ASSERT(callsite_);
         break;
       case AsmJSModule::CodeRange::Entry:
         fp_ = nullptr;
-        JS_ASSERT(done());
+        MOZ_ASSERT(done());
         break;
       case AsmJSModule::CodeRange::IonFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         MOZ_CRASH("Should not encounter an exit during iteration");
     }
 }
 
 JSAtom *
 AsmJSFrameIterator::functionDisplayAtom() const
 {
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
     return reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_)->functionName(*module_);
 }
 
 unsigned
 AsmJSFrameIterator::computeLine(uint32_t *column) const
 {
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
     if (column)
         *column = callsite_->column();
     return callsite_->line();
 }
 
 /*****************************************************************************/
 // Prologue/epilogue code generation
 
@@ -171,24 +171,24 @@ GenerateProfilingPrologue(MacroAssembler
     {
 #if defined(JS_CODEGEN_ARM)
         AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
 #endif
         DebugOnly<uint32_t> offsetAtBegin = masm.currentOffset();
         masm.bind(begin);
 
         PushRetAddr(masm);
-        JS_ASSERT(PushedRetAddr == masm.currentOffset() - offsetAtBegin);
+        MOZ_ASSERT(PushedRetAddr == masm.currentOffset() - offsetAtBegin);
 
         masm.loadAsmJSActivation(scratch);
         masm.push(Address(scratch, AsmJSActivation::offsetOfFP()));
-        JS_ASSERT(PushedFP == masm.currentOffset() - offsetAtBegin);
+        MOZ_ASSERT(PushedFP == masm.currentOffset() - offsetAtBegin);
 
         masm.storePtr(StackPointer, Address(scratch, AsmJSActivation::offsetOfFP()));
-        JS_ASSERT(StoredFP == masm.currentOffset() - offsetAtBegin);
+        MOZ_ASSERT(StoredFP == masm.currentOffset() - offsetAtBegin);
     }
 
     if (reason != AsmJSExit::None)
         masm.store32_NoSecondScratch(Imm32(reason), Address(scratch, AsmJSActivation::offsetOfExitReason()));
 
 #if defined(JS_CODEGEN_ARM)
     masm.setSecondScratchReg(lr);
 #endif
@@ -285,17 +285,17 @@ js::GenerateAsmJSFunctionPrologue(MacroA
 // profiling and non-profiling epilogue a priori. When the profiling mode is
 // toggled, AsmJSModule::setProfilingEnabled patches the 'profiling jump' to
 // either be a nop (falling through to the normal prologue) or a jump (jumping
 // to the profiling epilogue).
 void
 js::GenerateAsmJSFunctionEpilogue(MacroAssembler &masm, unsigned framePushed,
                                   AsmJSFunctionLabels *labels)
 {
-    JS_ASSERT(masm.framePushed() == framePushed);
+    MOZ_ASSERT(masm.framePushed() == framePushed);
 
 #if defined(JS_CODEGEN_ARM)
     // Flush pending pools so they do not get dumped between the profilingReturn
     // and profilingJump/profilingEpilogue labels since the difference must be
     // less than UINT8_MAX.
     masm.flushBuffer();
 #endif
 
@@ -374,17 +374,17 @@ js::GenerateAsmJSExitPrologue(MacroAssem
     masm.setFramePushed(framePushed);
 }
 
 void
 js::GenerateAsmJSExitEpilogue(MacroAssembler &masm, unsigned framePushed, AsmJSExit::Reason reason,
                               Label *profilingReturn)
 {
     // Inverse of GenerateAsmJSExitPrologue:
-    JS_ASSERT(masm.framePushed() == framePushed);
+    MOZ_ASSERT(masm.framePushed() == framePushed);
     GenerateProfilingEpilogue(masm, framePushed, reason, profilingReturn);
     masm.setFramePushed(0);
 }
 
 /*****************************************************************************/
 // AsmJSProfilingFrameIterator
 
 AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation &activation)
@@ -399,56 +399,56 @@ AsmJSProfilingFrameIterator::AsmJSProfil
 }
 
 static inline void
 AssertMatchesCallSite(const AsmJSModule &module, const AsmJSModule::CodeRange *calleeCodeRange,
                       void *callerPC, void *callerFP, void *fp)
 {
 #ifdef DEBUG
     const AsmJSModule::CodeRange *callerCodeRange = module.lookupCodeRange(callerPC);
-    JS_ASSERT(callerCodeRange);
+    MOZ_ASSERT(callerCodeRange);
     if (callerCodeRange->isEntry()) {
-        JS_ASSERT(callerFP == nullptr);
+        MOZ_ASSERT(callerFP == nullptr);
         return;
     }
 
     const CallSite *callsite = module.lookupCallSite(callerPC);
     if (calleeCodeRange->isThunk()) {
-        JS_ASSERT(!callsite);
-        JS_ASSERT(callerCodeRange->isFunction());
+        MOZ_ASSERT(!callsite);
+        MOZ_ASSERT(callerCodeRange->isFunction());
     } else {
-        JS_ASSERT(callsite);
-        JS_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
+        MOZ_ASSERT(callsite);
+        MOZ_ASSERT(callerFP == (uint8_t*)fp + callsite->stackDepth());
     }
 #endif
 }
 
 void
 AsmJSProfilingFrameIterator::initFromFP(const AsmJSActivation &activation)
 {
     uint8_t *fp = activation.fp();
 
     // If a signal was handled while entering an activation, the frame will
     // still be null.
     if (!fp) {
-        JS_ASSERT(done());
+        MOZ_ASSERT(done());
         return;
     }
 
     // Since we don't have the pc for fp, start unwinding at the caller of fp,
     // whose pc we do have via fp->returnAddress. This means that the innermost
     // frame is skipped but this is fine because:
     //  - for FFI calls, the innermost frame is a thunk, so the first frame that
     //    shows up is the function calling the FFI;
     //  - for Math and other builtin calls, when profiling is activated, we
     //    patch all call sites to instead call through a thunk; and
     //  - for interrupts, we just accept that we'll lose the innermost frame.
     void *pc = ReturnAddressFromFP(fp);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(pc);
-    JS_ASSERT(codeRange);
+    MOZ_ASSERT(codeRange);
     codeRange_ = codeRange;
     stackAddress_ = fp;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         break;
@@ -472,17 +472,17 @@ AsmJSProfilingFrameIterator::initFromFP(
     // stored on all the paths leaving asm.js and the iterator logic treats this
     // reason as its own frame. If we have exited asm.js code without setting an
     // exit reason, the reason will be None and this means the code was
     // asynchronously interrupted.
     exitReason_ = activation.exitReason();
     if (exitReason_ == AsmJSExit::None)
         exitReason_ = AsmJSExit::Interrupt;
 
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
 }
 
 typedef JS::ProfilingFrameIterator::RegisterState RegisterState;
 
 AsmJSProfilingFrameIterator::AsmJSProfilingFrameIterator(const AsmJSActivation &activation,
                                                          const RegisterState &state)
   : module_(&activation.module()),
     callerFP_(nullptr),
@@ -491,17 +491,17 @@ AsmJSProfilingFrameIterator::AsmJSProfil
     codeRange_(nullptr)
 {
     // If profiling hasn't been enabled for this module, then CallerFPFromFP
     // will be trash, so ignore the entire activation. In practice, this only
     // happens if profiling is enabled while module->active() (in this case,
     // profiling will be enabled when the module becomes inactive and gets
     // called again).
     if (!module_->profilingEnabled()) {
-        JS_ASSERT(done());
+        MOZ_ASSERT(done());
         return;
     }
 
     // If pc isn't in the module, we must have exited the asm.js module via an
     // exit trampoline or signal handler.
     if (!module_->containsCodePC(state.pc)) {
         initFromFP(activation);
         return;
@@ -522,117 +522,117 @@ AsmJSProfilingFrameIterator::AsmJSProfil
         // activation.fp isn't always the AsmJSFrame for state.pc; during the
         // prologue/epilogue, activation.fp will point to the caller's frame.
         // Naively unwinding starting at activation.fp could thus lead to the
         // second-to-innermost function being skipped in the callstack which will
         // bork profiling stacks. Instead, we depend on the exact layout of the
         // prologue/epilogue, as generated by GenerateProfiling(Prologue|Epilogue)
         // below.
         uint32_t offsetInModule = ((uint8_t*)state.pc) - module_->codeBase();
-        JS_ASSERT(offsetInModule < module_->codeBytes());
-        JS_ASSERT(offsetInModule >= codeRange->begin());
-        JS_ASSERT(offsetInModule < codeRange->end());
+        MOZ_ASSERT(offsetInModule < module_->codeBytes());
+        MOZ_ASSERT(offsetInModule >= codeRange->begin());
+        MOZ_ASSERT(offsetInModule < codeRange->end());
         uint32_t offsetInCodeRange = offsetInModule - codeRange->begin();
         void **sp = (void**)state.sp;
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
         if (offsetInCodeRange < PushedRetAddr) {
             callerPC_ = state.lr;
             callerFP_ = fp;
             AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp - 2);
         } else
 #endif
         if (offsetInCodeRange < PushedFP || offsetInModule == codeRange->profilingReturn()) {
             callerPC_ = *sp;
             callerFP_ = fp;
             AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp - 1);
         } else if (offsetInCodeRange < StoredFP) {
-            JS_ASSERT(fp == CallerFPFromFP(sp));
+            MOZ_ASSERT(fp == CallerFPFromFP(sp));
             callerPC_ = ReturnAddressFromFP(sp);
             callerFP_ = CallerFPFromFP(sp);
             AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, sp);
         } else {
             callerPC_ = ReturnAddressFromFP(fp);
             callerFP_ = CallerFPFromFP(fp);
             AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         }
         break;
       }
       case AsmJSModule::CodeRange::Entry: {
         // The entry trampoline is the final frame in an AsmJSActivation. The entry
         // trampoline also doesn't GenerateAsmJSPrologue/Epilogue so we can't use
         // the general unwinding logic below.
-        JS_ASSERT(!fp);
+        MOZ_ASSERT(!fp);
         callerPC_ = nullptr;
         callerFP_ = nullptr;
         break;
       }
       case AsmJSModule::CodeRange::Inline: {
         // The throw stub clears AsmJSActivation::fp on it's way out.
         if (!fp) {
-            JS_ASSERT(done());
+            MOZ_ASSERT(done());
             return;
         }
 
         // Inline code ranges execute in the frame of the caller have no
         // prologue/epilogue and thus don't require the general unwinding logic
         // as below.
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         break;
       }
     }
 
     codeRange_ = codeRange;
     stackAddress_ = state.sp;
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
 }
 
 void
 AsmJSProfilingFrameIterator::operator++()
 {
     if (exitReason_ != AsmJSExit::None) {
-        JS_ASSERT(codeRange_);
+        MOZ_ASSERT(codeRange_);
         exitReason_ = AsmJSExit::None;
-        JS_ASSERT(!done());
+        MOZ_ASSERT(!done());
         return;
     }
 
     if (!callerPC_) {
-        JS_ASSERT(!callerFP_);
+        MOZ_ASSERT(!callerFP_);
         codeRange_ = nullptr;
-        JS_ASSERT(done());
+        MOZ_ASSERT(done());
         return;
     }
 
-    JS_ASSERT(callerPC_);
+    MOZ_ASSERT(callerPC_);
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(callerPC_);
-    JS_ASSERT(codeRange);
+    MOZ_ASSERT(codeRange);
     codeRange_ = codeRange;
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
-        JS_ASSERT(callerFP_ == nullptr);
-        JS_ASSERT(callerPC_ != nullptr);
+        MOZ_ASSERT(callerFP_ == nullptr);
+        MOZ_ASSERT(callerPC_ != nullptr);
         callerPC_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
       case AsmJSModule::CodeRange::IonFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
         break;
     }
 
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
 }
 
 static const char *
 BuiltinToName(AsmJSExit::BuiltinKind builtin)
 {
     // Note: this label is regexp-matched by
     // browser/devtools/profiler/cleopatra/js/parserWorker.js.
 
@@ -660,17 +660,17 @@ BuiltinToName(AsmJSExit::BuiltinKind bui
       case AsmJSExit::Builtin_Limit:     break;
     }
     MOZ_CRASH("Bad builtin kind");
 }
 
 const char *
 AsmJSProfilingFrameIterator::label() const
 {
-    JS_ASSERT(!done());
+    MOZ_ASSERT(!done());
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
     //
     // NB: these labels are regexp-matched by
     //     browser/devtools/profiler/cleopatra/js/parserWorker.js.
     const char *ionFFIDescription = "fast FFI trampoline (in asm.js)";
     const char *slowFFIDescription = "slow FFI trampoline (in asm.js)";
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -111,17 +111,17 @@ namespace AsmJSExit
     static const uint32_t Interrupt = Reason_Interrupt;
     static inline Reason Builtin(BuiltinKind builtin) {
         return uint16_t(Reason_Builtin) | (uint16_t(builtin) << 16);
     }
     static inline ReasonKind ExtractReasonKind(Reason reason) {
         return ReasonKind(uint16_t(reason));
     }
     static inline BuiltinKind ExtractBuiltinKind(Reason reason) {
-        JS_ASSERT(ExtractReasonKind(reason) == Reason_Builtin);
+        MOZ_ASSERT(ExtractReasonKind(reason) == Reason_Builtin);
         return BuiltinKind(uint16_t(reason >> 16));
     }
 }
 
 // Iterates over the frames of a single AsmJSActivation, given an
 // asynchrously-interrupted thread's state. If the activation's
 // module is not in profiling mode, the activation is skipped.
 class AsmJSProfilingFrameIterator
@@ -141,17 +141,17 @@ class AsmJSProfilingFrameIterator
   public:
     AsmJSProfilingFrameIterator() : codeRange_(nullptr) {}
     explicit AsmJSProfilingFrameIterator(const AsmJSActivation &activation);
     AsmJSProfilingFrameIterator(const AsmJSActivation &activation,
                                 const JS::ProfilingFrameIterator::RegisterState &state);
     void operator++();
     bool done() const { return !codeRange_; }
 
-    void *stackAddress() const { JS_ASSERT(!done()); return stackAddress_; }
+    void *stackAddress() const { MOZ_ASSERT(!done()); return stackAddress_; }
     const char *label() const;
 };
 
 /******************************************************************************/
 // Prologue/epilogue code generation.
 
 void
 GenerateAsmJSFunctionPrologue(jit::MacroAssembler &masm, unsigned framePushed,
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -124,17 +124,17 @@ HasPureCoercion(JSContext *cx, HandleVal
 
     return false;
 }
 
 static bool
 ValidateGlobalVariable(JSContext *cx, const AsmJSModule &module, AsmJSModule::Global &global,
                        HandleValue importVal)
 {
-    JS_ASSERT(global.which() == AsmJSModule::Global::Variable);
+    MOZ_ASSERT(global.which() == AsmJSModule::Global::Variable);
 
     void *datum = module.globalVarToGlobalDatum(global);
 
     switch (global.varInitKind()) {
       case AsmJSModule::Global::InitConstant: {
         const AsmJSNumLit &lit = global.varInitNumLit();
         switch (lit.which()) {
           case AsmJSNumLit::Fixnum:
@@ -430,17 +430,17 @@ LinkModuleToHeap(JSContext *cx, AsmJSMod
                         "valid length is 0x%x",
                         heapLength,
                         RoundUpToNextValidAsmJSHeapLength(heapLength)));
         return LinkFail(cx, msg.get());
     }
 
     // This check is sufficient without considering the size of the loaded datum because heap
     // loads and stores start on an aligned boundary and the heap byteLength has larger alignment.
-    JS_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
+    MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
     if (heapLength < module.minHeapLength()) {
         ScopedJSFreePtr<char> msg(
             JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the "
                         "largest constant heap access offset rounded up to the next valid "
                         "heap size).",
                         heapLength,
                         module.minHeapLength()));
         return LinkFail(cx, msg.get());
@@ -772,17 +772,17 @@ SendFunctionsToVTune(JSContext *cx, AsmJ
 {
     uint8_t *base = module.codeBase();
 
     for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
         const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
 
         uint8_t *start = base + func.pod.startCodeOffset;
         uint8_t *end   = base + func.pod.endCodeOffset;
-        JS_ASSERT(end >= start);
+        MOZ_ASSERT(end >= start);
 
         unsigned method_id = iJIT_GetNewMethodID();
         if (method_id == 0)
             return false;
 
         JSAutoByteString bytes;
         const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
         if (!method_name)
@@ -815,17 +815,17 @@ SendFunctionsToPerf(JSContext *cx, AsmJS
 
     uintptr_t base = (uintptr_t) module.codeBase();
     const char *filename = module.scriptSource()->filename();
 
     for (unsigned i = 0; i < module.numProfiledFunctions(); i++) {
         const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i);
         uintptr_t start = base + (unsigned long) func.pod.startCodeOffset;
         uintptr_t end   = base + (unsigned long) func.pod.endCodeOffset;
-        JS_ASSERT(end >= start);
+        MOZ_ASSERT(end >= start);
         size_t size = end - start;
 
         JSAutoByteString bytes;
         const char *name = AtomToPrintableString(cx, func.name, &bytes);
         if (!name)
             return false;
 
         writePerfSpewerAsmJSFunctionMap(start, size, filename, func.pod.lineno,
@@ -903,17 +903,17 @@ CreateExportObject(JSContext *cx, Handle
 
     for (unsigned i = 0; i < module.numExportedFunctions(); i++) {
         const AsmJSModule::ExportedFunction &func = module.exportedFunction(i);
 
         RootedFunction fun(cx, NewExportedFunction(cx, func, moduleObj, i));
         if (!fun)
             return nullptr;
 
-        JS_ASSERT(func.maybeFieldName() != nullptr);
+        MOZ_ASSERT(func.maybeFieldName() != nullptr);
         RootedId id(cx, NameToId(func.maybeFieldName()));
         RootedValue val(cx, ObjectValue(*fun));
         if (!DefineNativeProperty(cx, obj, id, val, nullptr, nullptr, JSPROP_ENUMERATE))
             return nullptr;
     }
 
     return obj;
 }
@@ -1169,28 +1169,28 @@ js::AsmJSFunctionToString(JSContext *cx,
     uint32_t begin = module.srcStart() + f.startOffsetInModule();
     uint32_t end = module.srcStart() + f.endOffsetInModule();
 
     ScriptSource *source = module.scriptSource();
     StringBuffer out(cx);
 
     // asm.js functions cannot have been created with a Function constructor
     // as they belong within a module.
-    JS_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
+    MOZ_ASSERT(!(begin == 0 && end == source->length() && source->argumentsNotIncluded()));
 
     if (!out.append("function "))
         return nullptr;
 
     if (module.strict()) {
         // AppendUseStrictSource expects its input to start right after the
         // function name, so split the source chars from the src into two parts:
         // the function name and the rest (arguments + body).
 
         // asm.js functions can't be anonymous
-        JS_ASSERT(fun->atom());
+        MOZ_ASSERT(fun->atom());
         if (!out.append(fun->atom()))
             return nullptr;
 
         size_t nameEnd = begin + fun->atom()->length();
         Rooted<JSFlatString*> src(cx, source->substring(cx, nameEnd, end));
         if (!AppendUseStrictSource(cx, fun, src, out))
             return nullptr;
     } else {
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -54,17 +54,17 @@ using mozilla::BinarySearch;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::Compression::LZ4;
 using mozilla::Swap;
 
 static uint8_t *
 AllocateExecutableMemory(ExclusiveContext *cx, size_t totalBytes)
 {
-    JS_ASSERT(totalBytes % AsmJSPageSize == 0);
+    MOZ_ASSERT(totalBytes % AsmJSPageSize == 0);
 
 #ifdef XP_WIN
     void *p = VirtualAlloc(nullptr, totalBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
     if (!p) {
         js_ReportOutOfMemory(cx);
         return nullptr;
     }
 #else  // assume Unix
@@ -199,17 +199,17 @@ struct CallSiteRetAddrOffset
     uint32_t operator[](size_t index) const {
         return callSites[index].returnAddressOffset();
     }
 };
 
 const CallSite *
 AsmJSModule::lookupCallSite(void *returnAddress) const
 {
-    JS_ASSERT(isFinished());
+    MOZ_ASSERT(isFinished());
 
     uint32_t target = ((uint8_t*)returnAddress) - code_;
     size_t lowerBound = 0;
     size_t upperBound = callSites_.length();
 
     size_t match;
     if (!BinarySearch(CallSiteRetAddrOffset(callSites_), lowerBound, upperBound, target, &match))
         return nullptr;
@@ -238,17 +238,17 @@ operator<(size_t pcOffset, const AsmJSMo
     return pcOffset < rhs.begin();
 }
 
 } // namespace js
 
 const AsmJSModule::CodeRange *
 AsmJSModule::lookupCodeRange(void *pc) const
 {
-    JS_ASSERT(isFinished());
+    MOZ_ASSERT(isFinished());
 
     uint32_t target = ((uint8_t*)pc) - code_;
     size_t lowerBound = 0;
     size_t upperBound = codeRanges_.length();
 
     size_t match;
     if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match))
         return nullptr;
@@ -263,67 +263,67 @@ struct HeapAccessOffset
     uintptr_t operator[](size_t index) const {
         return accesses[index].offset();
     }
 };
 
 const AsmJSHeapAccess *
 AsmJSModule::lookupHeapAccess(void *pc) const
 {
-    JS_ASSERT(isFinished());
-    JS_ASSERT(containsFunctionPC(pc));
+    MOZ_ASSERT(isFinished());
+    MOZ_ASSERT(containsFunctionPC(pc));
 
     uint32_t target = ((uint8_t*)pc) - code_;
     size_t lowerBound = 0;
     size_t upperBound = heapAccesses_.length();
 
     size_t match;
     if (!BinarySearch(HeapAccessOffset(heapAccesses_), lowerBound, upperBound, target, &match))
         return nullptr;
 
     return &heapAccesses_[match];
 }
 
 bool
 AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembler &masm,
                     const Label &interruptLabel)
 {
-    JS_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
+    MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
 
     uint32_t endBeforeCurly = tokenStream.currentToken().pos.end;
     uint32_t endAfterCurly = tokenStream.peekTokenPos().end;
-    JS_ASSERT(endBeforeCurly >= srcBodyStart_);
-    JS_ASSERT(endAfterCurly >= srcBodyStart_);
+    MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
+    MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
     pod.srcLength_ = endBeforeCurly - srcStart_;
     pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
 
     // The global data section sits immediately after the executable (and
     // other) data allocated by the MacroAssembler, so ensure it is
     // SIMD-aligned.
     pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), SimdStackAlignment);
 
     // The entire region is allocated via mmap/VirtualAlloc which requires
     // units of pages.
     pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize);
 
-    JS_ASSERT(!code_);
+    MOZ_ASSERT(!code_);
     code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
     if (!code_)
         return false;
 
     // Copy the code from the MacroAssembler into its final resting place in the
     // AsmJSModule.
-    JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
+    MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
     masm.executableCopy(code_);
 
     // c.f. JitCode::copyFrom
-    JS_ASSERT(masm.jumpRelocationTableBytes() == 0);
-    JS_ASSERT(masm.dataRelocationTableBytes() == 0);
-    JS_ASSERT(masm.preBarrierTableBytes() == 0);
-    JS_ASSERT(!masm.hasEnteredExitFrame());
+    MOZ_ASSERT(masm.jumpRelocationTableBytes() == 0);
+    MOZ_ASSERT(masm.dataRelocationTableBytes() == 0);
+    MOZ_ASSERT(masm.preBarrierTableBytes() == 0);
+    MOZ_ASSERT(!masm.hasEnteredExitFrame());
 
     // Copy over metadata, making sure to update all offsets on ARM.
 
     staticLinkData_.interruptExitOffset = masm.actualOffset(interruptLabel.offset());
 
     // Heap-access metadata used for link-time patching and fault-handling.
     heapAccesses_ = masm.extractAsmJSHeapAccesses();
 
@@ -342,22 +342,22 @@ AsmJSModule::finish(ExclusiveContext *cx
     for (unsigned i = 0; i < numExits(); i++)
         exit(i).updateOffsets(masm);
     for (size_t i = 0; i < callSites_.length(); i++) {
         CallSite &c = callSites_[i];
         c.setReturnAddressOffset(masm.actualOffset(c.returnAddressOffset()));
     }
     for (size_t i = 0; i < codeRanges_.length(); i++) {
         codeRanges_[i].updateOffsets(masm);
-        JS_ASSERT_IF(i > 0, codeRanges_[i - 1].end() <= codeRanges_[i].begin());
+        MOZ_ASSERT_IF(i > 0, codeRanges_[i - 1].end() <= codeRanges_[i].begin());
     }
     for (size_t i = 0; i < builtinThunkOffsets_.length(); i++)
         builtinThunkOffsets_[i] = masm.actualOffset(builtinThunkOffsets_[i]);
 #endif
-    JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
+    MOZ_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
 
     // Absolute link metadata: absolute addresses that refer to some fixed
     // address in the address space.
     AbsoluteLinkArray &absoluteLinks = staticLinkData_.absoluteLinks;
     for (size_t i = 0; i < masm.numAsmJSAbsoluteLinks(); i++) {
         AsmJSAbsoluteLink src = masm.asmJSAbsoluteLink(i);
         if (!absoluteLinks[src.target].append(masm.actualOffset(src.patchAt.offset())))
             return false;
@@ -447,17 +447,17 @@ AsmJSModule::finish(ExclusiveContext *cx
 #endif
 
     return true;
 }
 
 void
 AsmJSModule::setAutoFlushICacheRange()
 {
-    JS_ASSERT(isFinished());
+    MOZ_ASSERT(isFinished());
     AutoFlushICache::setRange(uintptr_t(code_), pod.codeBytes_);
 }
 
 static void
 AsmJSReportOverRecursed()
 {
     JSContext *cx = PerThreadData::innermostAsmJSActivation()->cx();
     js_ReportOverRecursed(cx);
@@ -698,18 +698,18 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
     }
 
     MOZ_CRASH("Bad AsmJSImmKind");
 }
 
 void
 AsmJSModule::staticallyLink(ExclusiveContext *cx)
 {
-    JS_ASSERT(isFinished());
-    JS_ASSERT(!isStaticallyLinked());
+    MOZ_ASSERT(isFinished());
+    MOZ_ASSERT(!isStaticallyLinked());
 
     // Process staticLinkData_
 
     interruptExit_ = code_ + staticLinkData_.interruptExitOffset;
 
     for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) {
         RelativeLink link = staticLinkData_.relativeLinks[i];
         uint8_t *patchAt = code_ + link.patchAtOffset;
@@ -734,39 +734,39 @@ AsmJSModule::staticallyLink(ExclusiveCon
 
     for (size_t i = 0; i < exits_.length(); i++) {
         AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
         exitDatum.exit = interpExitTrampoline(exits_[i]);
         exitDatum.fun = nullptr;
         exitDatum.ionScript = nullptr;
     }
 
-    JS_ASSERT(isStaticallyLinked());
+    MOZ_ASSERT(isStaticallyLinked());
 }
 
 void
 AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx)
 {
-    JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
-    JS_ASSERT(dynamicallyLinked_);
-    JS_ASSERT(!maybeHeap_);
+    MOZ_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
+    MOZ_ASSERT(dynamicallyLinked_);
+    MOZ_ASSERT(!maybeHeap_);
 
     maybeHeap_ = heap;
     heapDatum() = heap->dataPointer();
 
 #if defined(JS_CODEGEN_X86)
     uint8_t *heapOffset = heap->dataPointer();
     void *heapLength = (void*)heap->byteLength();
     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
         const jit::AsmJSHeapAccess &access = heapAccesses_[i];
         if (access.hasLengthCheck())
             X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
         void *addr = access.patchOffsetAt(code_);
         uint32_t disp = reinterpret_cast<uint32_t>(X86Assembler::getPointer(addr));
-        JS_ASSERT(disp <= INT32_MAX);
+        MOZ_ASSERT(disp <= INT32_MAX);
         X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
     }
 #elif defined(JS_CODEGEN_X64)
     int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
     if (usesSignalHandlersForOOB())
         return;
     // If we cannot use the signal handlers, we need to patch the heap length
     // checks at the right places. All accesses that have been recorded are the
@@ -820,17 +820,17 @@ AsmJSModule::restoreToInitialState(uint8
     if (maybePrevBuffer) {
 #if defined(JS_CODEGEN_X86)
         // Subtract out the base-pointer added by AsmJSModule::initHeap.
         uint8_t *ptrBase = maybePrevBuffer->dataPointer();
         for (unsigned i = 0; i < heapAccesses_.length(); i++) {
             const jit::AsmJSHeapAccess &access = heapAccesses_[i];
             void *addr = access.patchOffsetAt(code_);
             uint8_t *ptr = reinterpret_cast<uint8_t*>(X86Assembler::getPointer(addr));
-            JS_ASSERT(ptr >= ptrBase);
+            MOZ_ASSERT(ptr >= ptrBase);
             X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
         }
 #endif
     }
 }
 
 static void
 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
@@ -871,17 +871,17 @@ AsmJSModuleObject::create(ExclusiveConte
 
     obj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget()));
     return &obj->as<AsmJSModuleObject>();
 }
 
 AsmJSModule &
 AsmJSModuleObject::module() const
 {
-    JS_ASSERT(is<AsmJSModuleObject>());
+    MOZ_ASSERT(is<AsmJSModuleObject>());
     return *(AsmJSModule *)getReservedSlot(MODULE_SLOT).toPrivate();
 }
 
 static inline uint8_t *
 WriteBytes(uint8_t *dst, const void *src, size_t nbytes)
 {
     memcpy(dst, src, nbytes);
     return dst + nbytes;
@@ -923,17 +923,17 @@ size_t
 AsmJSModule::Name::serializedSize() const
 {
     return SerializedNameSize(name_);
 }
 
 static uint8_t *
 SerializeName(uint8_t *cursor, PropertyName *name)
 {
-    JS_ASSERT_IF(name, !name->empty());
+    MOZ_ASSERT_IF(name, !name->empty());
     if (name) {
         static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
         uint32_t length = name->length();
         uint32_t lengthAndEncoding = (length << 1) | uint32_t(name->hasLatin1Chars());
         cursor = WriteScalar<uint32_t>(cursor, lengthAndEncoding);
         JS::AutoCheckCannotGC nogc;
         if (name->hasLatin1Chars())
             cursor = WriteBytes(cursor, name->latin1Chars(nogc), length * sizeof(Latin1Char));
@@ -1202,69 +1202,69 @@ AsmJSModule::CodeRange::CodeRange(uint32
     lineNumber_(lineNumber),
     begin_(l.begin.offset()),
     profilingReturn_(l.profilingReturn.offset()),
     end_(l.end.offset())
 {
     u.kind_ = Function;
     setDeltas(l.entry.offset(), l.profilingJump.offset(), l.profilingEpilogue.offset());
 
-    JS_ASSERT(l.begin.offset() < l.entry.offset());
-    JS_ASSERT(l.entry.offset() < l.profilingJump.offset());
-    JS_ASSERT(l.profilingJump.offset() < l.profilingEpilogue.offset());
-    JS_ASSERT(l.profilingEpilogue.offset() < l.profilingReturn.offset());
-    JS_ASSERT(l.profilingReturn.offset() < l.end.offset());
+    MOZ_ASSERT(l.begin.offset() < l.entry.offset());
+    MOZ_ASSERT(l.entry.offset() < l.profilingJump.offset());
+    MOZ_ASSERT(l.profilingJump.offset() < l.profilingEpilogue.offset());
+    MOZ_ASSERT(l.profilingEpilogue.offset() < l.profilingReturn.offset());
+    MOZ_ASSERT(l.profilingReturn.offset() < l.end.offset());
 }
 
 void
 AsmJSModule::CodeRange::setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue)
 {
-    JS_ASSERT(entry - begin_ <= UINT8_MAX);
+    MOZ_ASSERT(entry - begin_ <= UINT8_MAX);
     u.func.beginToEntry_ = entry - begin_;
 
-    JS_ASSERT(profilingReturn_ - profilingJump <= UINT8_MAX);
+    MOZ_ASSERT(profilingReturn_ - profilingJump <= UINT8_MAX);
     u.func.profilingJumpToProfilingReturn_ = profilingReturn_ - profilingJump;
 
-    JS_ASSERT(profilingReturn_ - profilingEpilogue <= UINT8_MAX);
+    MOZ_ASSERT(profilingReturn_ - profilingEpilogue <= UINT8_MAX);
     u.func.profilingEpilogueToProfilingReturn_ = profilingReturn_ - profilingEpilogue;
 }
 
 AsmJSModule::CodeRange::CodeRange(Kind kind, uint32_t begin, uint32_t end)
   : begin_(begin),
     end_(end)
 {
     u.kind_ = kind;
 
-    JS_ASSERT(begin_ <= end_);
-    JS_ASSERT(u.kind_ == Entry || u.kind_ == Inline);
+    MOZ_ASSERT(begin_ <= end_);
+    MOZ_ASSERT(u.kind_ == Entry || u.kind_ == Inline);
 }
 
 AsmJSModule::CodeRange::CodeRange(Kind kind, uint32_t begin, uint32_t profilingReturn, uint32_t end)
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
     u.kind_ = kind;
 
-    JS_ASSERT(begin_ < profilingReturn_);
-    JS_ASSERT(profilingReturn_ < end_);
-    JS_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
+    MOZ_ASSERT(begin_ < profilingReturn_);
+    MOZ_ASSERT(profilingReturn_ < end_);
+    MOZ_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
 }
 
 AsmJSModule::CodeRange::CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
                                   uint32_t profilingReturn, uint32_t end)
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
     u.kind_ = Thunk;
     u.thunk.target_ = builtin;
 
-    JS_ASSERT(begin_ < profilingReturn_);
-    JS_ASSERT(profilingReturn_ < end_);
+    MOZ_ASSERT(begin_ < profilingReturn_);
+    MOZ_ASSERT(profilingReturn_ < end_);
 }
 
 void
 AsmJSModule::CodeRange::updateOffsets(jit::MacroAssembler &masm)
 {
     uint32_t entryBefore, profilingJumpBefore, profilingEpilogueBefore;
     if (isFunction()) {
         entryBefore = entry();
@@ -1551,17 +1551,17 @@ AsmJSModule::clone(JSContext *cx, Scoped
 
     out.restoreToInitialState(code_, maybeHeap_, cx);
     return true;
 }
 
 void
 AsmJSModule::setProfilingEnabled(bool enabled, JSContext *cx)
 {
-    JS_ASSERT(isDynamicallyLinked());
+    MOZ_ASSERT(isDynamicallyLinked());
 
     if (profilingEnabled_ == enabled)
         return;
 
     // When enabled, generate profiling labels for every name in names_ that is
     // the name of some Function CodeRange. This involves malloc() so do it now
     // since, once we start sampling, we'll be in a signal-handing context where
     // we cannot malloc.
@@ -1618,18 +1618,18 @@ AsmJSModule::setProfilingEnabled(bool en
 #endif
 
         const CodeRange *codeRange = lookupCodeRange(callee);
         if (codeRange->kind() != CodeRange::Function)
             continue;
 
         uint8_t *profilingEntry = code_ + codeRange->begin();
         uint8_t *entry = code_ + codeRange->entry();
-        JS_ASSERT_IF(profilingEnabled_, callee == profilingEntry);
-        JS_ASSERT_IF(!profilingEnabled_, callee == entry);
+        MOZ_ASSERT_IF(profilingEnabled_, callee == profilingEntry);
+        MOZ_ASSERT_IF(!profilingEnabled_, callee == entry);
         uint8_t *newCallee = enabled ? profilingEntry : entry;
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         X86Assembler::setRel32(callerRetAddr, newCallee);
 #elif defined(JS_CODEGEN_ARM)
         new (caller) InstBLImm(BOffImm(newCallee - caller), Assembler::Always);
 #elif defined(JS_CODEGEN_MIPS)
         Assembler::WriteLuiOriInstructions(instr, instr->next(),
@@ -1647,18 +1647,18 @@ AsmJSModule::setProfilingEnabled(bool en
     for (size_t i = 0; i < funcPtrTables_.length(); i++) {
         FuncPtrTable &funcPtrTable = funcPtrTables_[i];
         uint8_t **array = globalDataOffsetToFuncPtrTable(funcPtrTable.globalDataOffset());
         for (size_t j = 0; j < funcPtrTable.numElems(); j++) {
             void *callee = array[j];
             const CodeRange *codeRange = lookupCodeRange(callee);
             uint8_t *profilingEntry = code_ + codeRange->begin();
             uint8_t *entry = code_ + codeRange->entry();
-            JS_ASSERT_IF(profilingEnabled_, callee == profilingEntry);
-            JS_ASSERT_IF(!profilingEnabled_, callee == entry);
+            MOZ_ASSERT_IF(profilingEnabled_, callee == profilingEntry);
+            MOZ_ASSERT_IF(!profilingEnabled_, callee == entry);
             if (enabled)
                 array[j] = profilingEntry;
             else
                 array[j] = entry;
         }
     }
 
     // Replace all the nops in all the epilogues of asm.js functions with jumps
@@ -1669,34 +1669,34 @@ AsmJSModule::setProfilingEnabled(bool en
             continue;
         uint8_t *jump = code_ + cr.profilingJump();
         uint8_t *profilingEpilogue = code_ + cr.profilingEpilogue();
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         // An unconditional jump with a 1 byte offset immediate has the opcode
         // 0x90. The offset is relative to the address of the instruction after
         // the jump. 0x66 0x90 is the canonical two-byte nop.
         ptrdiff_t jumpImmediate = profilingEpilogue - jump - 2;
-        JS_ASSERT(jumpImmediate > 0 && jumpImmediate <= 127);
+        MOZ_ASSERT(jumpImmediate > 0 && jumpImmediate <= 127);
         if (enabled) {
-            JS_ASSERT(jump[0] == 0x66);
-            JS_ASSERT(jump[1] == 0x90);
+            MOZ_ASSERT(jump[0] == 0x66);
+            MOZ_ASSERT(jump[1] == 0x90);
             jump[0] = 0xeb;
             jump[1] = jumpImmediate;
         } else {
-            JS_ASSERT(jump[0] == 0xeb);
-            JS_ASSERT(jump[1] == jumpImmediate);
+            MOZ_ASSERT(jump[0] == 0xeb);
+            MOZ_ASSERT(jump[1] == jumpImmediate);
             jump[0] = 0x66;
             jump[1] = 0x90;
         }
 #elif defined(JS_CODEGEN_ARM)
         if (enabled) {
-            JS_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
+            MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstNOP>());
             new (jump) InstBImm(BOffImm(profilingEpilogue - jump), Assembler::Always);
         } else {
-            JS_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
+            MOZ_ASSERT(reinterpret_cast<Instruction*>(jump)->is<InstBImm>());
             new (jump) InstNOP();
         }
 #elif defined(JS_CODEGEN_MIPS)
         Instruction *instr = (Instruction *)jump;
         if (enabled) {
             Assembler::WriteLuiOriInstructions(instr, instr->next(),
                                                ScratchRegister, (uint32_t)profilingEpilogue);
             instr[2] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr);
@@ -1722,31 +1722,31 @@ AsmJSModule::setProfilingEnabled(bool en
         void *to = code_ + builtinThunkOffsets_[builtin];
         if (!enabled)
             Swap(from, to);
         for (size_t j = 0; j < offsets.length(); j++) {
             uint8_t *caller = code_ + offsets[j];
             const AsmJSModule::CodeRange *codeRange = lookupCodeRange(caller);
             if (codeRange->isThunk())
                 continue;
-            JS_ASSERT(codeRange->isFunction());
+            MOZ_ASSERT(codeRange->isFunction());
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(caller),
                                                PatchedImmPtr(to),
                                                PatchedImmPtr(from));
         }
     }
 
     profilingEnabled_ = enabled;
 }
 
 void
 AsmJSModule::protectCode(JSRuntime *rt) const
 {
-    JS_ASSERT(isDynamicallyLinked());
-    JS_ASSERT(rt->currentThreadOwnsInterruptLock());
+    MOZ_ASSERT(isDynamicallyLinked());
+    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
 
     codeIsProtected_ = true;
 
     if (!pod.functionBytes_)
         return;
 
     // Technically, we should be able to only take away the execute permissions,
     // however this seems to break our emulators which don't always check
@@ -1759,18 +1759,18 @@ AsmJSModule::protectCode(JSRuntime *rt) 
     if (mprotect(codeBase(), functionBytes(), PROT_NONE))
         MOZ_CRASH();
 #endif
 }
 
 void
 AsmJSModule::unprotectCode(JSRuntime *rt) const
 {
-    JS_ASSERT(isDynamicallyLinked());
-    JS_ASSERT(rt->currentThreadOwnsInterruptLock());
+    MOZ_ASSERT(isDynamicallyLinked());
+    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
 
     codeIsProtected_ = false;
 
     if (!pod.functionBytes_)
         return;
 
 #if defined(XP_WIN)
     DWORD oldProtect;
@@ -1780,18 +1780,18 @@ AsmJSModule::unprotectCode(JSRuntime *rt
     if (mprotect(codeBase(), functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC))
         MOZ_CRASH();
 #endif
 }
 
 bool
 AsmJSModule::codeIsProtected(JSRuntime *rt) const
 {
-    JS_ASSERT(isDynamicallyLinked());
-    JS_ASSERT(rt->currentThreadOwnsInterruptLock());
+    MOZ_ASSERT(isDynamicallyLinked());
+    MOZ_ASSERT(rt->currentThreadOwnsInterruptLock());
     return codeIsProtected_;
 }
 
 static bool
 GetCPUID(uint32_t *cpuId)
 {
     enum Arch {
         X86 = 0x1,
@@ -1905,17 +1905,17 @@ class ModuleChars
 class ModuleCharsForStore : ModuleChars
 {
     uint32_t uncompressedSize_;
     uint32_t compressedSize_;
     Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
 
   public:
     bool init(AsmJSParser &parser) {
-        JS_ASSERT(beginOffset(parser) < endOffset(parser));
+        MOZ_ASSERT(beginOffset(parser) < endOffset(parser));
 
         uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(char16_t);
         size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
         if (maxCompressedSize < uncompressedSize_)
             return false;
 
         if (!compressedBuffer_.resize(maxCompressedSize))
             return false;
@@ -1999,17 +1999,17 @@ class ModuleCharsForLookup : ModuleChars
             cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
 
         return cursor;
     }
 
     bool match(AsmJSParser &parser) const {
         const char16_t *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser);
         const char16_t *parseLimit = parser.tokenStream.rawLimit();
-        JS_ASSERT(parseLimit >= parseBegin);
+        MOZ_ASSERT(parseLimit >= parseBegin);
         if (uint32_t(parseLimit - parseBegin) < chars_.length())
             return false;
         if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
             return false;
         if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
             return false;
         if (isFunCtor_) {
             // For function statements, the closing } is included as the last
@@ -2089,17 +2089,17 @@ js::StoreAsmJSModuleInCache(AsmJSParser 
         return false;
     }
 
     uint8_t *cursor = entry.memory;
     cursor = machineId.serialize(cursor);
     cursor = moduleChars.serialize(cursor);
     cursor = module.serialize(cursor);
 
-    JS_ASSERT(cursor == entry.memory + serializedSize);
+    MOZ_ASSERT(cursor == entry.memory + serializedSize);
     return true;
 }
 
 struct ScopedCacheEntryOpenedForRead
 {
     ExclusiveContext *cx;
     size_t serializedSize;
     const uint8_t *memory;
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -129,58 +129,58 @@ class AsmJSNumLit
         jit::SimdConstant simd_;
     } value;
 
   public:
     static AsmJSNumLit Create(Which w, Value v) {
         AsmJSNumLit lit;
         lit.which_ = w;
         lit.value.scalar_ = v;
-        JS_ASSERT(!lit.isSimd());
+        MOZ_ASSERT(!lit.isSimd());
         return lit;
     }
 
     static AsmJSNumLit Create(Which w, jit::SimdConstant c) {
         AsmJSNumLit lit;
         lit.which_ = w;
         lit.value.simd_ = c;
-        JS_ASSERT(lit.isSimd());
+        MOZ_ASSERT(lit.isSimd());
         return lit;
     }
 
     Which which() const {
         return which_;
     }
 
     int32_t toInt32() const {
-        JS_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
+        MOZ_ASSERT(which_ == Fixnum || which_ == NegativeInt || which_ == BigUnsigned);
         return value.scalar_.toInt32();
     }
 
     double toDouble() const {
-        JS_ASSERT(which_ == Double);
+        MOZ_ASSERT(which_ == Double);
         return value.scalar_.toDouble();
     }
 
     float toFloat() const {
-        JS_ASSERT(which_ == Float);
+        MOZ_ASSERT(which_ == Float);
         return float(value.scalar_.toDouble());
     }
 
     Value scalarValue() const {
-        JS_ASSERT(which_ != OutOfRangeInt);
+        MOZ_ASSERT(which_ != OutOfRangeInt);
         return value.scalar_;
     }
 
     bool isSimd() const {
         return which_ == Int32x4 || which_ == Float32x4;
     }
 
     const jit::SimdConstant &simdValue() const {
-        JS_ASSERT(isSimd());
+        MOZ_ASSERT(isSimd());
         return value.simd_;
     }
 
     bool hasType() const {
         return which_ != OutOfRangeInt;
     }
 };
 
@@ -233,108 +233,108 @@ class AsmJSModule
         } pod;
         PropertyName *name_;
 
         friend class AsmJSModule;
 
         Global(Which which, PropertyName *name) {
             pod.which_ = which;
             name_ = name;
-            JS_ASSERT_IF(name_, name_->isTenured());
+            MOZ_ASSERT_IF(name_, name_->isTenured());
         }
 
         void trace(JSTracer *trc) {
             if (name_)
                 MarkStringUnbarriered(trc, &name_, "asm.js global name");
-            JS_ASSERT_IF(pod.which_ == Variable && pod.u.var.initKind_ == InitConstant,
-                         !pod.u.var.u.numLit_.scalarValue().isMarkable());
+            MOZ_ASSERT_IF(pod.which_ == Variable && pod.u.var.initKind_ == InitConstant,
+                          !pod.u.var.u.numLit_.scalarValue().isMarkable());
         }
 
       public:
         Global() {}
         Which which() const {
             return pod.which_;
         }
         uint32_t varIndex() const {
-            JS_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.which_ == Variable);
             return pod.u.var.index_;
         }
         VarInitKind varInitKind() const {
-            JS_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.which_ == Variable);
             return pod.u.var.initKind_;
         }
         const AsmJSNumLit &varInitNumLit() const {
-            JS_ASSERT(pod.which_ == Variable);
-            JS_ASSERT(pod.u.var.initKind_ == InitConstant);
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
             return pod.u.var.u.numLit_;
         }
         AsmJSCoercion varInitCoercion() const {
-            JS_ASSERT(pod.which_ == Variable);
-            JS_ASSERT(pod.u.var.initKind_ == InitImport);
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
             return pod.u.var.u.coercion_;
         }
         PropertyName *varImportField() const {
-            JS_ASSERT(pod.which_ == Variable);
-            JS_ASSERT(pod.u.var.initKind_ == InitImport);
+            MOZ_ASSERT(pod.which_ == Variable);
+            MOZ_ASSERT(pod.u.var.initKind_ == InitImport);
             return name_;
         }
         PropertyName *ffiField() const {
-            JS_ASSERT(pod.which_ == FFI);
+            MOZ_ASSERT(pod.which_ == FFI);
             return name_;
         }
         uint32_t ffiIndex() const {
-            JS_ASSERT(pod.which_ == FFI);
+            MOZ_ASSERT(pod.which_ == FFI);
             return pod.u.ffiIndex_;
         }
         PropertyName *viewName() const {
-            JS_ASSERT(pod.which_ == ArrayView);
+            MOZ_ASSERT(pod.which_ == ArrayView);
             return name_;
         }
         Scalar::Type viewType() const {
-            JS_ASSERT(pod.which_ == ArrayView);
+            MOZ_ASSERT(pod.which_ == ArrayView);
             return pod.u.viewType_;
         }
         PropertyName *mathName() const {
-            JS_ASSERT(pod.which_ == MathBuiltinFunction);
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
             return name_;
         }
         AsmJSMathBuiltinFunction mathBuiltinFunction() const {
-            JS_ASSERT(pod.which_ == MathBuiltinFunction);
+            MOZ_ASSERT(pod.which_ == MathBuiltinFunction);
             return pod.u.mathBuiltinFunc_;
         }
         AsmJSSimdType simdCtorType() const {
-            JS_ASSERT(pod.which_ == SimdCtor);
+            MOZ_ASSERT(pod.which_ == SimdCtor);
             return pod.u.simdCtorType_;
         }
         PropertyName *simdCtorName() const {
-            JS_ASSERT(pod.which_ == SimdCtor);
+            MOZ_ASSERT(pod.which_ == SimdCtor);
             return name_;
         }
         PropertyName *simdOperationName() const {
-            JS_ASSERT(pod.which_ == SimdOperation);
+            MOZ_ASSERT(pod.which_ == SimdOperation);
             return name_;
         }
         AsmJSSimdOperation simdOperation() const {
-            JS_ASSERT(pod.which_ == SimdOperation);
+            MOZ_ASSERT(pod.which_ == SimdOperation);
             return pod.u.simdOp.which_;
         }
         AsmJSSimdType simdOperationType() const {
-            JS_ASSERT(pod.which_ == SimdOperation);
+            MOZ_ASSERT(pod.which_ == SimdOperation);
             return pod.u.simdOp.type_;
         }
         PropertyName *constantName() const {
-            JS_ASSERT(pod.which_ == Constant);
+            MOZ_ASSERT(pod.which_ == Constant);
             return name_;
         }
         ConstantKind constantKind() const {
-            JS_ASSERT(pod.which_ == Constant);
+            MOZ_ASSERT(pod.which_ == Constant);
             return pod.u.constant.kind_;
         }
         double constantValue() const {
-            JS_ASSERT(pod.which_ == Constant);
+            MOZ_ASSERT(pod.which_ == Constant);
             return pod.u.constant.value_;
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, Global *out) const;
     };
@@ -356,21 +356,21 @@ class AsmJSModule
         {}
         unsigned ffiIndex() const {
             return ffiIndex_;
         }
         unsigned globalDataOffset() const {
             return globalDataOffset_;
         }
         void initInterpOffset(unsigned off) {
-            JS_ASSERT(!interpCodeOffset_);
+            MOZ_ASSERT(!interpCodeOffset_);
             interpCodeOffset_ = off;
         }
         void initIonOffset(unsigned off) {
-            JS_ASSERT(!ionCodeOffset_);
+            MOZ_ASSERT(!ionCodeOffset_);
             ionCodeOffset_ = off;
         }
         void updateOffsets(jit::MacroAssembler &masm) {
             interpCodeOffset_ = masm.actualOffset(interpCodeOffset_);
             ionCodeOffset_ = masm.actualOffset(ionCodeOffset_);
         }
 
         size_t serializedSize() const;
@@ -425,17 +425,17 @@ class AsmJSModule
         {
             name_ = name;
             maybeFieldName_ = maybeFieldName;
             argCoercions_ = mozilla::Move(argCoercions);
             pod.returnType_ = returnType;
             pod.codeOffset_ = UINT32_MAX;
             pod.startOffsetInModule_ = startOffsetInModule;
             pod.endOffsetInModule_ = endOffsetInModule;
-            JS_ASSERT_IF(maybeFieldName_, name_->isTenured());
+            MOZ_ASSERT_IF(maybeFieldName_, name_->isTenured());
         }
 
         void trace(JSTracer *trc) {
             MarkStringUnbarriered(trc, &name_, "asm.js export name");
             if (maybeFieldName_)
                 MarkStringUnbarriered(trc, &maybeFieldName_, "asm.js export field");
         }
 
@@ -444,17 +444,17 @@ class AsmJSModule
         ExportedFunction(ExportedFunction &&rhs) {
             name_ = rhs.name_;
             maybeFieldName_ = rhs.maybeFieldName_;
             argCoercions_ = mozilla::Move(rhs.argCoercions_);
             pod = rhs.pod;
         }
 
         void initCodeOffset(unsigned off) {
-            JS_ASSERT(pod.codeOffset_ == UINT32_MAX);
+            MOZ_ASSERT(pod.codeOffset_ == UINT32_MAX);
             pod.codeOffset_ = off;
         }
         void updateCodeOffset(jit::MacroAssembler &masm) {
             pod.codeOffset_ = masm.actualOffset(pod.codeOffset_);
         }
 
 
         PropertyName *name() const {
@@ -524,52 +524,52 @@ class AsmJSModule
         bool isFFI() const { return kind() == IonFFI || kind() == SlowFFI; }
         bool isInterrupt() const { return kind() == Interrupt; }
         bool isThunk() const { return kind() == Thunk; }
 
         uint32_t begin() const {
             return begin_;
         }
         uint32_t entry() const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return begin_ + u.func.beginToEntry_;
         }
         uint32_t end() const {
             return end_;
         }
         uint32_t profilingJump() const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return profilingReturn_ - u.func.profilingJumpToProfilingReturn_;
         }
         uint32_t profilingEpilogue() const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return profilingReturn_ - u.func.profilingEpilogueToProfilingReturn_;
         }
         uint32_t profilingReturn() const {
-            JS_ASSERT(isFunction() || isFFI() || isInterrupt() || isThunk());
+            MOZ_ASSERT(isFunction() || isFFI() || isInterrupt() || isThunk());
             return profilingReturn_;
         }
         uint32_t functionNameIndex() const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return nameIndex_;
         }
         PropertyName *functionName(const AsmJSModule &module) const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return module.names_[nameIndex_].name();
         }
         const char *functionProfilingLabel(const AsmJSModule &module) const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return module.profilingLabels_[nameIndex_].get();
         }
         uint32_t functionLineNumber() const {
-            JS_ASSERT(isFunction());
+            MOZ_ASSERT(isFunction());
             return lineNumber_;
         }
         AsmJSExit::BuiltinKind thunkTarget() const {
-            JS_ASSERT(isThunk());
+            MOZ_ASSERT(isThunk());
             return AsmJSExit::BuiltinKind(u.thunk.target_);
         }
     };
 
     class FuncPtrTable
     {
         uint32_t globalDataOffset_;
         uint32_t numElems_;
@@ -613,17 +613,17 @@ class AsmJSModule
         explicit ProfiledFunction()
           : name(nullptr)
         { }
 
         ProfiledFunction(PropertyName *name, unsigned start, unsigned end,
                          unsigned line = 0, unsigned column = 0)
           : name(name)
         {
-            JS_ASSERT(name->isTenured());
+            MOZ_ASSERT(name->isTenured());
 
             pod.startCodeOffset = start;
             pod.endCodeOffset = end;
             pod.lineno = line;
             pod.columnIndex = column;
         }
 
         void trace(JSTracer *trc) {
@@ -643,17 +643,17 @@ class AsmJSModule
         unsigned endInlineCodeOffset;
         jit::BasicBlocksVector blocks;
 
         ProfiledBlocksFunction(PropertyName *name, unsigned start, unsigned endInline, unsigned end,
                                jit::BasicBlocksVector &blocksVector)
           : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline),
             blocks(mozilla::Move(blocksVector))
         {
-            JS_ASSERT(name->isTenured());
+            MOZ_ASSERT(name->isTenured());
         }
 
         ProfiledBlocksFunction(ProfiledBlocksFunction &&copy)
           : ProfiledFunction(copy.name, copy.pod.startCodeOffset, copy.pod.endCodeOffset),
             endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::Move(copy.blocks))
         { }
     };
 #endif
@@ -672,17 +672,17 @@ class AsmJSModule
 
         explicit RelativeLink(Kind kind)
         {
 #if defined(JS_CODEGEN_MIPS)
             kind_ = kind;
 #elif defined(JS_CODEGEN_ARM)
             // On ARM, CodeLabels are only used to label raw pointers, so in
             // all cases on ARM, a RelativePatch means patching a raw pointer.
-            JS_ASSERT(kind == CodeLabel || kind == RawPointer);
+            MOZ_ASSERT(kind == CodeLabel || kind == RawPointer);
 #endif
             // On X64 and X86, all RelativePatch-es are patched as raw pointers.
         }
 
         bool isRawPointerPatch() {
 #if defined(JS_CODEGEN_MIPS)
             return kind_ == RawPointer;
 #else
@@ -702,21 +702,21 @@ class AsmJSModule
     typedef Vector<uint32_t, 0, SystemAllocPolicy> OffsetVector;
 
     class AbsoluteLinkArray
     {
         OffsetVector array_[jit::AsmJSImm_Limit];
 
       public:
         OffsetVector &operator[](size_t i) {
-            JS_ASSERT(i < jit::AsmJSImm_Limit);
+            MOZ_ASSERT(i < jit::AsmJSImm_Limit);
             return array_[i];
         }
         const OffsetVector &operator[](size_t i) const {
-            JS_ASSERT(i < jit::AsmJSImm_Limit);
+            MOZ_ASSERT(i < jit::AsmJSImm_Limit);
             return array_[i];
         }
 
         size_t serializedSize() const;
         uint8_t *serialize(uint8_t *cursor) const;
         const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor);
         bool clone(ExclusiveContext *cx, AbsoluteLinkArray *out) const;
 
@@ -810,17 +810,17 @@ class AsmJSModule
     bool isFinished() const { return !!code_; }
     bool isStaticallyLinked() const { return !!interruptExit_; }
     bool isDynamicallyLinked() const { return dynamicallyLinked_; }
 
     /*************************************************************************/
     // These functions may be used as soon as the module is constructed:
 
     ScriptSource *scriptSource() const {
-        JS_ASSERT(scriptSource_);
+        MOZ_ASSERT(scriptSource_);
         return scriptSource_;
     }
     bool strict() const {
         return pod.strict_;
     }
     bool usesSignalHandlersForInterrupt() const {
         return pod.usesSignalHandlers_;
     }
@@ -866,41 +866,41 @@ class AsmJSModule
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode,
                        size_t *asmJSModuleData);
 
     /*************************************************************************/
     // These functions build the global scope of the module while parsing the
     // module prologue (before the function bodies):
 
     void initGlobalArgumentName(PropertyName *n) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
-        JS_ASSERT_IF(n, n->isTenured());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT_IF(n, n->isTenured());
         globalArgumentName_ = n;
     }
     void initImportArgumentName(PropertyName *n) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
-        JS_ASSERT_IF(n, n->isTenured());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT_IF(n, n->isTenured());
         importArgumentName_ = n;
     }
     void initBufferArgumentName(PropertyName *n) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
-        JS_ASSERT_IF(n, n->isTenured());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT_IF(n, n->isTenured());
         bufferArgumentName_ = n;
     }
     PropertyName *globalArgumentName() const {
         return globalArgumentName_;
     }
     PropertyName *importArgumentName() const {
         return importArgumentName_;
     }
     PropertyName *bufferArgumentName() const {
         return bufferArgumentName_;
     }
     bool addGlobalVarInit(const AsmJSNumLit &lit, uint32_t *globalIndex) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Variable, nullptr);
         g.pod.u.var.initKind_ = Global::InitConstant;
         g.pod.u.var.u.numLit_ = lit;
 
         if (lit.isSimd()) {
             if (pod.numGlobalSimdVars_ == UINT32_MAX)
                 return false;
             *globalIndex = pod.numGlobalSimdVars_++;
@@ -921,48 +921,48 @@ class AsmJSModule
             return false;
           case AsmJS_ToInt32x4:
           case AsmJS_ToFloat32x4:
             return true;
         }
         MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("unexpected AsmJSCoercion");
     }
     bool addGlobalVarImport(PropertyName *name, AsmJSCoercion coercion, uint32_t *globalIndex) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Variable, name);
         g.pod.u.var.initKind_ = Global::InitImport;
         g.pod.u.var.u.coercion_ = coercion;
         *globalIndex = IsSimdCoercion(coercion) ? pod.numGlobalSimdVars_++
                                                 : pod.numGlobalScalarVars_++;
         g.pod.u.var.index_ = *globalIndex;
         return globals_.append(g);
     }
     bool addFFI(PropertyName *field, uint32_t *ffiIndex) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
         return globals_.append(g);
     }
     bool addArrayView(Scalar::Type vt, PropertyName *field) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         pod.hasArrayView_ = true;
         Global g(Global::ArrayView, field);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
     bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
         return globals_.append(g);
     }
     bool addMathBuiltinConstant(double value, PropertyName *field) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Constant, field);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::MathConstant;
         return globals_.append(g);
     }
     bool addSimdCtor(AsmJSSimdType type, PropertyName *field) {
         Global g(Global::SimdCtor, field);
         g.pod.u.simdCtorType_ = type;
@@ -970,148 +970,148 @@ class AsmJSModule
     }
     bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName *field) {
         Global g(Global::SimdOperation, field);
         g.pod.u.simdOp.type_ = type;
         g.pod.u.simdOp.which_ = op;
         return globals_.append(g);
     }
     bool addGlobalConstant(double value, PropertyName *name) {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         Global g(Global::Constant, name);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::GlobalConstant;
         return globals_.append(g);
     }
     unsigned numGlobals() const {
         return globals_.length();
     }
     Global &global(unsigned i) {
         return globals_[i];
     }
 
     /*************************************************************************/
 
     void startFunctionBodies() {
-        JS_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinishedWithModulePrologue());
         pod.funcPtrTableAndExitBytes_ = 0;
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
     }
 
     /*************************************************************************/
     // These functions are called while parsing/compiling function bodies:
 
     void requireHeapLengthToBeAtLeast(uint32_t len) {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         len = RoundUpToNextValidAsmJSHeapLength(len);
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
     }
     bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t end) {
         return codeRanges_.append(CodeRange(kind, begin, end));
     }
     bool addCodeRange(CodeRange::Kind kind, uint32_t begin, uint32_t pret, uint32_t end) {
         return codeRanges_.append(CodeRange(kind, begin, pret, end));
     }
     bool addFunctionCodeRange(PropertyName *name, uint32_t lineNumber,
                               const AsmJSFunctionLabels &labels)
     {
-        JS_ASSERT(!isFinished());
-        JS_ASSERT(name->isTenured());
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(name->isTenured());
         if (names_.length() >= UINT32_MAX)
             return false;
         uint32_t nameIndex = names_.length();
         return names_.append(name) && codeRanges_.append(CodeRange(nameIndex, lineNumber, labels));
     }
     bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
                                   uint32_t profilingReturn, uint32_t end)
     {
         return builtinThunkOffsets_.append(begin) &&
                codeRanges_.append(CodeRange(builtin, begin, profilingReturn, end));
     }
     bool addExit(unsigned ffiIndex, unsigned *exitIndex) {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum))
             return false;
         uint32_t globalDataOffset = globalDataBytes();
         JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0);
         pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum);
         *exitIndex = unsigned(exits_.length());
         return exits_.append(Exit(ffiIndex, globalDataOffset));
     }
     unsigned numExits() const {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_.length();
     }
     Exit &exit(unsigned i) {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_[i];
     }
     const Exit &exit(unsigned i) const {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_[i];
     }
     bool addFuncPtrTable(unsigned numElems, uint32_t *globalDataOffset) {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinished());
-        JS_ASSERT(IsPowerOfTwo(numElems));
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinished());
+        MOZ_ASSERT(IsPowerOfTwo(numElems));
         if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < numElems * sizeof(void*))
             return false;
         *globalDataOffset = globalDataBytes();
         if (!funcPtrTables_.append(FuncPtrTable(*globalDataOffset, numElems)))
             return false;
         pod.funcPtrTableAndExitBytes_ += numElems * sizeof(void*);
         return true;
     }
     bool addFunctionCounts(jit::IonScriptCounts *counts) {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         return functionCounts_.append(counts);
     }
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
     bool addProfiledFunction(PropertyName *name, unsigned codeStart, unsigned codeEnd,
                              unsigned line, unsigned column)
     {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         ProfiledFunction func(name, codeStart, codeEnd, line, column);
         return profiledFunctions_.append(func);
     }
     unsigned numProfiledFunctions() const {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return profiledFunctions_.length();
     }
     ProfiledFunction &profiledFunction(unsigned i) {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return profiledFunctions_[i];
     }
 #endif
 #ifdef JS_ION_PERF
     bool addProfiledBlocks(PropertyName *name, unsigned codeBegin, unsigned inlineEnd,
                            unsigned codeEnd, jit::BasicBlocksVector &basicBlocks)
     {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         ProfiledBlocksFunction func(name, codeBegin, inlineEnd, codeEnd, basicBlocks);
         return perfProfiledBlocksFunctions_.append(mozilla::Move(func));
     }
     unsigned numPerfBlocksFunctions() const {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return perfProfiledBlocksFunctions_.length();
     }
     ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) {
-        JS_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(isFinishedWithModulePrologue());
         return perfProfiledBlocksFunctions_[i];
     }
 #endif
 
     /*************************************************************************/
 
     // This function is called after compiling the function bodies (before
     // compiling entries/exits) to record the extent of compiled function code.
     void finishFunctionBodies(size_t functionBytes) {
-        JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
         pod.functionBytes_ = functionBytes;
-        JS_ASSERT(isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithFunctionBodies());
     }
 
     /*************************************************************************/
     // Exported functions are added after finishFunctionBodies() and before
     // finish(). The list of exported functions can be accessed any time after
     // the exported functions have been added.
 
     bool addExportedFunction(PropertyName *name,
@@ -1119,33 +1119,33 @@ class AsmJSModule
                              uint32_t funcSrcEnd,
                              PropertyName *maybeFieldName,
                              ArgCoercionVector &&argCoercions,
                              ReturnType returnType)
     {
         // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
         // (the entire file) and ExportedFunctions store offsets relative to
         // the beginning of the module (so that they are caching-invariant).
-        JS_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
-        JS_ASSERT(srcStart_ < funcSrcBegin);
-        JS_ASSERT(funcSrcBegin < funcSrcEnd);
+        MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
+        MOZ_ASSERT(srcStart_ < funcSrcBegin);
+        MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
         ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
                               maybeFieldName, mozilla::Move(argCoercions), returnType);
         return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
     }
     unsigned numExportedFunctions() const {
-        JS_ASSERT(isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_.length();
     }
     const ExportedFunction &exportedFunction(unsigned i) const {
-        JS_ASSERT(isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_[i];
     }
     ExportedFunction &exportedFunction(unsigned i) {
-        JS_ASSERT(isFinishedWithFunctionBodies());
+        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_[i];
     }
 
     /*************************************************************************/
 
     // finish() is called once the entire module has been parsed (via
     // tokenStream) and all function and entry/exit trampolines have been
     // generated (via masm). After this function, the module must still be
@@ -1154,60 +1154,60 @@ class AsmJSModule
                 frontend::TokenStream &tokenStream,
                 jit::MacroAssembler &masm,
                 const jit::Label &interruptLabel);
 
     /*************************************************************************/
     // These accessor functions can be used after finish():
 
     bool hasArrayView() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return pod.hasArrayView_;
     }
     unsigned numFFIs() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return pod.numFFIs_;
     }
     uint32_t srcEndBeforeCurly() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return srcStart_ + pod.srcLength_;
     }
     uint32_t srcEndAfterCurly() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return srcStart_ + pod.srcLengthWithRightBrace_;
     }
     uint8_t *codeBase() const {
-        JS_ASSERT(isFinished());
-        JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
+        MOZ_ASSERT(isFinished());
+        MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
         return code_;
     }
     size_t functionBytes() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return pod.functionBytes_;
     }
     size_t codeBytes() const {
-        JS_ASSERT(isFinished());
+        MOZ_ASSERT(isFinished());
         return pod.codeBytes_;
     }
     bool containsFunctionPC(void *pc) const {
-        JS_ASSERT(isFinished());