Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Sun, 30 Sep 2018 01:00:16 +0300
changeset 494652 787f0f511af260a998fc83951cc0cd738001c098
parent 494651 be070bf5d08a5fa7efb83751b7b27d5f1269e5da (current diff)
parent 494628 3632445ae3db388e7b214b83acec5a042391a7bb (diff)
child 494653 a902caa5f3209f558a58b11ce44cc4cce77a5b6a
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. a=merge CLOSED TREE
js/src/jit/arm/BaselineCompiler-arm.cpp
js/src/jit/arm/BaselineCompiler-arm.h
js/src/jit/arm64/BaselineCompiler-arm64.h
js/src/jit/mips-shared/BaselineCompiler-mips-shared.cpp
js/src/jit/mips-shared/BaselineCompiler-mips-shared.h
js/src/jit/mips32/BaselineCompiler-mips32.cpp
js/src/jit/mips32/BaselineCompiler-mips32.h
js/src/jit/mips64/BaselineCompiler-mips64.cpp
js/src/jit/mips64/BaselineCompiler-mips64.h
js/src/jit/none/BaselineCompiler-none.h
js/src/jit/shared/BaselineCompiler-shared.cpp
js/src/jit/shared/BaselineCompiler-shared.h
js/src/jit/x64/BaselineCompiler-x64.cpp
js/src/jit/x64/BaselineCompiler-x64.h
js/src/jit/x86-shared/BaselineCompiler-x86-shared.cpp
js/src/jit/x86-shared/BaselineCompiler-x86-shared.h
js/src/jit/x86/BaselineCompiler-x86.cpp
js/src/jit/x86/BaselineCompiler-x86.h
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -790,40 +790,51 @@ file, You can obtain one at http://mozil
         @return True if formatting was applied and false if not.
       -->
       <method name="_formatSearchAlias">
         <body><![CDATA[
           if (!this._formattingEnabled) {
             return false;
           }
 
-          let textNode = this.editor.rootElement.firstChild;
-          let value = textNode.textContent;
-          let trimmedValue = value.trimStart();
-
-          // If there's no alias in the results or the first word in the input
-          // value is not that alias, then there's no formatting to do.
-          if (!this.popup.searchAlias ||
-              (trimmedValue.length != this.popup.searchAlias.length &&
-               trimmedValue[this.popup.searchAlias.length] != " ") ||
-              !trimmedValue.startsWith(this.popup.searchAlias)) {
+          // There can only be an alias to highlight if the heuristic result is
+          // an alias searchengine result and it's either currently selected or
+          // was selected when the popup was closed.  We also need to check
+          // whether a one-off search button is selected because in that case
+          // there won't be a selection but the alias should not be highlighted.
+          let heuristicItem = this.popup.richlistbox.children[0] || null;
+          let alias =
+            (this.popup.selectedIndex == 0 ||
+             (this.popup.selectedIndex < 0 &&
+              this.popup._previousSelectedIndex == 0)) &&
+            !this.popup.oneOffSearchButtons.selectedButton &&
+            heuristicItem &&
+            heuristicItem.getAttribute("actiontype") == "searchengine" &&
+            this._parseActionUrl(heuristicItem.getAttribute("url")).params.alias;
+          if (!alias) {
             return false;
           }
 
-          let index = value.indexOf(this.popup.searchAlias);
+          let textNode = this.editor.rootElement.firstChild;
+          let value = textNode.textContent;
+
+          let index = value.indexOf(alias);
+          if (index < 0) {
+            return false;
+          }
 
           // We abuse the SELECTION_FIND selection type to do our highlighting.
           // It's the only type that works with Selection.setColors().
           let selection = this.editor.selectionController.getSelection(
             Ci.nsISelectionController.SELECTION_FIND
           );
 
           let range = document.createRange();
           range.setStart(textNode, index);
-          range.setEnd(textNode, index + this.popup.searchAlias.length);
+          range.setEnd(textNode, index + alias.length);
           selection.addRange(range);
 
           let fg = "#2362d7";
           let bg = "#d2e6fd";
 
           // Selection.setColors() will swap the given foreground and background
           // colors if it detects that the contrast between the background
           // color and the frame color is too low.  Normally we don't want that
@@ -2629,35 +2640,16 @@ file, You can obtain one at http://mozil
             this._invalidate();
           } else {
             this.closePopup();
           }
           ]]>
         </body>
       </method>
 
-      <method name="_selectedOneOffChanged">
-        <body><![CDATA[
-          // Update all searchengine result items to use the newly selected
-          // engine.
-          for (let item of this.richlistbox.children) {
-            if (item.collapsed) {
-              break;
-            }
-            let url = item.getAttribute("url");
-            if (url) {
-              let action = item._parseActionUrl(url);
-              if (action && action.type == "searchengine") {
-                item._adjustAcItem();
-              }
-            }
-          }
-        ]]></body>
-      </method>
-
       <!-- This handles keypress changes to the selection among the one-off
            search buttons and between the one-offs and the listbox.  It returns
            true if the keypress was consumed and false if not. -->
       <method name="handleKeyPress">
         <parameter name="aEvent"/>
         <body><![CDATA[
           this.oneOffSearchButtons.handleKeyPress(aEvent, this.matchCount,
                                                   !this._isFirstResultHeuristic,
@@ -2734,48 +2726,38 @@ file, You can obtain one at http://mozil
             Services.io.speculativeConnect2(uri, gBrowser.contentPrincipal, null);
           } catch (ex) {
             // Can't setup speculative connection for this uri string for some
             // reason, just ignore it.
           }
         ]]></body>
       </method>
 
-      <!--
-        The search alias of the first (heuristic) result in the popup, if any.
-      -->
-      <field name="searchAlias">null</field>
-
       <method name="onResultsAdded">
         <body>
           <![CDATA[
-            if (!this.input.gotResultForCurrentQuery) {
-              // This is the first result of a new search.  Cache its search
-              // alias, if any.  Do this now, when we get the first result, so
-              // that the cached alias remains available between the time the
-              // previous search ended and now.
-              //
-              // Calling _parseActionUrl and getting params.alias would be
-              // sufficient here, but as an optimization, first check whether
-              // the result is a searchengine result, which is a simple
-              // substring check, to avoid the more expensive _parseActionUrl.
-              let alias =
-                this.input.mController.getStyleAt(0).includes("searchengine") &&
-                this.input._parseActionUrl(this.input.mController.getFinalCompleteValueAt(0)).params.alias;
-              this.searchAlias = alias || null;
+            // If nothing is selected yet, select the first result if it is a
+            // pre-selected "heuristic" result.  (See UnifiedComplete.js.)
+            let selectHeuristic =
+              this.selectedIndex == -1 && this._isFirstResultHeuristic;
+            if (selectHeuristic) {
+              this.input.controller.setInitiallySelectedIndex(0);
+            }
 
-              // Format the alias or remove the formatting of the previous alias.
+            // If this is the heuristic result of a new search, format its
+            // search alias in the input or remove the formatting of the
+            // previous alias, as necessary.  We need to check selectHeuristic
+            // because the result may have already been added but only now is
+            // being selected, and we need to check gotResultForCurrentQuery
+            // because the result may be from the previous search and already
+            // selected and is now being reused.
+            if (selectHeuristic || !this.input.gotResultForCurrentQuery) {
               this.input.formatValue();
             }
 
-            // If nothing is selected yet, select the first result if it is a
-            // pre-selected "heuristic" result.  (See UnifiedComplete.js.)
-            if (this.selectedIndex == -1 && this._isFirstResultHeuristic) {
-              this.input.controller.setInitiallySelectedIndex(0);
-            }
             // If this is the first time we get the result from the current
             // search and we are not in the private context, we can speculatively
             // connect to the intended site as a performance optimization.
             if (!this.input.gotResultForCurrentQuery &&
                 this.input.speculativeConnectEnabled &&
                 !this.input.inPrivateContext &&
                 this.input.mController.matchCount > 0) {
               let firstStyle = this.input.mController.getStyleAt(0);
@@ -2917,17 +2899,43 @@ file, You can obtain one at http://mozil
           return iframe;
         ]]></body>
       </method>
 
     </implementation>
     <handlers>
 
       <handler event="SelectedOneOffButtonChanged"><![CDATA[
-        this._selectedOneOffChanged();
+        // Update all searchengine result items to use the newly selected
+        // engine.
+        for (let item of this.richlistbox.children) {
+          if (item.collapsed) {
+            break;
+          }
+          let url = item.getAttribute("url");
+          if (url) {
+            let action = item._parseActionUrl(url);
+            if (action && action.type == "searchengine") {
+              item._adjustAcItem();
+            }
+          }
+        }
+
+        // If the selection moved from the results to the one-off settings
+        // button, then call formatValue to remove the formatting of the search
+        // alias in the input, if any.  In all other cases the alias formatting
+        // is removed when the input's value setter calls formatValue, but in
+        // this specific case, at the time that formatValue is called,
+        // oneOffSearchButtons.selectedButton is still null, so the formatting
+        // is not removed.  The settings button is selected right after that.
+        if (this.oneOffSearchButtons.selectedButton ==
+              this.oneOffSearchButtons.settingsButtonCompact &&
+            (!event.detail || !event.detail.previousSelectedButton)) {
+          this.input.formatValue();
+        }
       ]]></handler>
 
       <handler event="mousedown"><![CDATA[
         // Required to make the xul:label.text-link elements in the search
         // suggestions notification work correctly when clicked on Linux.
         // This is copied from the mousedown handler in
         // browser-search-autocomplete-result-popup, which apparently had a
         // similar problem.
--- a/browser/components/payments/res/components/rich-select.css
+++ b/browser/components/payments/res/components/rich-select.css
@@ -2,17 +2,18 @@
  * 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/. */
 
 rich-select {
   /* Include the padding in the max-width calculation so that we truncate rather
      than grow wider than 100% of the parent. */
   box-sizing: border-box;
   display: block;
-  margin: 14px 0;
+  /* Has to be the same as `payment-method-picker > input`: */
+  margin: 10px 0;
   /* Padding for the dropmarker (copied from common.css) */
   padding-inline-end: 24px;
   position: relative;
   /* Don't allow the <rich-select> to grow wider than the container so that we
      truncate with text-overflow for long options instead. */
   max-width: 100%;
 }
 
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -39,20 +39,27 @@ export default class AddressForm extends
 
     this.saveButton = document.createElement("button");
     this.saveButton.className = "save-button primary";
     this.saveButton.addEventListener("click", this);
 
     this.persistCheckbox = new LabelledCheckbox();
     this.persistCheckbox.className = "persist-checkbox";
 
+    // Combination of AddressErrors and PayerErrorFields as keys
     this._errorFieldMap = {
       addressLine: "#street-address",
       city: "#address-level2",
       country: "#country",
+      email: "#email",
+      // Bug 1472283 is on file to support
+      // additional-name and family-name.
+      // XXX: For now payer name errors go on the family-name and address-errors
+      //      go on the given-name so they don't overwrite each other.
+      name: "#family-name",
       organization: "#organization",
       phone: "#tel",
       postalCode: "#postal-code",
       // Bug 1472283 is on file to support
       // additional-name and family-name.
       recipient: "#given-name",
       region: "#address-level1",
     };
@@ -77,17 +84,17 @@ export default class AddressForm extends
       xhr.send();
     });
   }
 
   connectedCallback() {
     this.promiseReady.then(form => {
       this.body.appendChild(form);
 
-      let record = {};
+      let record = undefined;
       this.formHandler = new EditAddress({
         form,
       }, record, {
         DEFAULT_REGION: PaymentDialogUtils.DEFAULT_REGION,
         getFormFormat: PaymentDialogUtils.getFormFormat,
         countries: PaymentDialogUtils.countries,
       });
 
@@ -116,17 +123,16 @@ export default class AddressForm extends
     });
   }
 
   render(state) {
     let record;
     let {
       page,
       "address-page": addressPage,
-      request,
     } = state;
 
     if (this.id && page && page.id !== this.id) {
       log.debug(`AddressForm: no need to further render inactive page: ${page.id}`);
       return;
     }
 
     let editing = !!addressPage.guid;
@@ -174,21 +180,24 @@ export default class AddressForm extends
     } else {
       this.form.dataset.addressFields = "mailing-address tel";
     }
     this.formHandler.loadRecord(record);
 
     // Add validation to some address fields
     this.updateRequiredState();
 
-    let shippingAddressErrors = request.paymentDetails.shippingAddressErrors;
+    // Show merchant errors for the appropriate address form.
+    let merchantFieldErrors = AddressForm.merchantFieldErrorsForForm(state,
+                                                                     addressPage.selectedStateKey);
     for (let [errorName, errorSelector] of Object.entries(this._errorFieldMap)) {
       let container = this.form.querySelector(errorSelector + "-container");
       let field = this.form.querySelector(errorSelector);
-      let errorText = (shippingAddressErrors && shippingAddressErrors[errorName]) || "";
+      // Never show errors on an 'add' screen as they would be for a different address.
+      let errorText = (editing && merchantFieldErrors && merchantFieldErrors[errorName]) || "";
       field.setCustomValidity(errorText);
       let span = PaymentDialog.maybeCreateFieldErrorElement(container);
       span.textContent = errorText;
     }
 
     this.updateSaveButtonState();
   }
 
@@ -364,11 +373,39 @@ export default class AddressForm extends
         page: {
           id: "address-page",
           onboardingWizard: page.onboardingWizard,
           error: this.dataset.errorGenericSave,
         },
       });
     }
   }
+
+  /**
+   * Get the dictionary of field-specific merchant errors relevant to the
+   * specific form identified by the state key.
+   * @param {object} state The application state
+   * @param {string[]} stateKey The key in state to return address errors for.
+   * @returns {object} with keys as PaymentRequest field names and values of
+   *                   merchant-provided error strings.
+   */
+  static merchantFieldErrorsForForm(state, stateKey) {
+    let {paymentDetails} = state.request;
+    switch (stateKey.join("|")) {
+      case "selectedShippingAddress": {
+        return paymentDetails.shippingAddressErrors;
+      }
+      case "selectedPayerAddress": {
+        return paymentDetails.payer;
+      }
+      case "basic-card-page|billingAddressGUID": {
+        // `paymentMethod` can be null.
+        return (paymentDetails.paymentMethod
+                && paymentDetails.paymentMethod.billingAddress) || {};
+      }
+      default: {
+        throw new Error("Unknown selectedStateKey");
+      }
+    }
+  }
 }
 
 customElements.define("address-form", AddressForm);
--- a/browser/components/payments/res/containers/address-picker.js
+++ b/browser/components/payments/res/containers/address-picker.js
@@ -1,12 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+import AddressForm from "./address-form.js";
 import AddressOption from "../components/address-option.js";
 import RichPicker from "./rich-picker.js";
 import paymentRequest from "../paymentRequest.js";
 
 /**
  * <address-picker></address-picker>
  * Container around add/edit links and <rich-select> with
  * <address-option> listening to savedAddresses & tempAddresses.
@@ -145,16 +146,34 @@ export default class AddressPicker exten
 
     super.render(state);
   }
 
   get selectedStateKey() {
     return this.getAttribute("selected-state-key");
   }
 
+  errorForSelectedOption(state) {
+    let superError = super.errorForSelectedOption(state);
+    if (superError) {
+      return superError;
+    }
+
+    if (!this.selectedOption) {
+      return "";
+    }
+
+    let merchantFieldErrors = AddressForm.merchantFieldErrorsForForm(state,
+                                                                     [this.selectedStateKey]);
+    // TODO: errors in priority order.
+    return Object.values(merchantFieldErrors).find(msg => {
+      return typeof(msg) == "string" && msg.length;
+    }) || "";
+  }
+
   handleEvent(event) {
     switch (event.type) {
       case "change": {
         this.onChange(event);
         break;
       }
       case "click": {
         this.onClick(event);
--- a/browser/components/payments/res/containers/completion-error-page.js
+++ b/browser/components/payments/res/containers/completion-error-page.js
@@ -45,17 +45,17 @@ export default class CompletionErrorPage
       return;
     }
 
     let {request} = this.requestStore.getState();
     let {displayHost} = request.topLevelPrincipal.URI;
     for (let key of [
       "pageTitle", "suggestion-heading", "suggestion-1", "suggestion-2", "suggestion-3",
     ]) {
-      if (this.dataset[key]) {
+      if (this.dataset[key] && displayHost) {
         this.dataset[key] = this.dataset[key].replace("**host-name**", displayHost);
       }
     }
 
     this.pageTitleHeading.textContent = this.dataset.pageTitle;
     this.suggestionHeading.textContent = this.dataset.suggestionHeading;
     this.brandingSpan.textContent = this.dataset.brandingLabel;
     this.doneButton.textContent = this.dataset.doneButtonLabel;
--- a/browser/components/payments/res/containers/payment-dialog.js
+++ b/browser/components/payments/res/containers/payment-dialog.js
@@ -150,16 +150,23 @@ export default class PaymentDialog exten
       case "fail":
       case "timeout":
       case "unknown":
         state.page = {
           id: `completion-${completeStatus}-error`,
         };
         state.changesPrevented = false;
         break;
+      case "": {
+        // When we get a DOM update for an updateWith() or retry() the completeStatus
+        // is "" when we need to show non-final screens. Don't set the page as we
+        // may be on a form instead of payment-summary
+        state.changesPrevented = false;
+        break;
+      }
     }
     return state;
   }
 
   /**
    * Set some state from the privileged parent process.
    * Other elements that need to set state should use their own `this.requestStore.setState`
    * method provided by the `PaymentStateSubscriberMixin`.
--- a/browser/components/payments/res/containers/payment-method-picker.js
+++ b/browser/components/payments/res/containers/payment-method-picker.js
@@ -79,35 +79,35 @@ export default class PaymentMethodPicker
     let securityCodeState = state[this.selectedStateKey + "SecurityCode"];
     if (securityCodeState && securityCodeState != this.securityCodeInput.value) {
       this.securityCodeInput.defaultValue = securityCodeState;
     }
 
     super.render(state);
   }
 
-  isSelectedOptionValid(state) {
-    let hasMissingFields = this.missingFieldsOfSelectedOption().length;
-    if (hasMissingFields) {
-      return false;
+  errorForSelectedOption(state) {
+    let superError = super.errorForSelectedOption(state);
+    if (superError) {
+      return superError;
     }
     let selectedOption = this.selectedOption;
     if (!selectedOption) {
-      return true;
+      return "";
     }
 
     let basicCardMethod = state.request.paymentMethods
       .find(method => method.supportedMethods == "basic-card");
     let merchantNetworks = basicCardMethod && basicCardMethod.data &&
                            basicCardMethod.data.supportedNetworks;
     let acceptedNetworks = merchantNetworks || PaymentDialogUtils.getCreditCardNetworks();
     let selectedCard = paymentRequest.getBasicCards(state)[selectedOption.value];
     let isSupported = selectedCard["cc-type"] &&
                       acceptedNetworks.includes(selectedCard["cc-type"]);
-    return isSupported;
+    return isSupported ? "" : this.dataset.invalidLabel;
   }
 
   get selectedStateKey() {
     return this.getAttribute("selected-state-key");
   }
 
   handleEvent(event) {
     switch (event.type) {
--- a/browser/components/payments/res/containers/rich-picker.css
+++ b/browser/components/payments/res/containers/rich-picker.css
@@ -4,16 +4,17 @@
 
 .rich-picker {
   display: grid;
   grid-template-columns: 5fr auto auto;
   grid-template-areas:
     "label    edit     add"
     "dropdown dropdown dropdown"
     "invalid  invalid  invalid";
+  padding-top: 8px;
 }
 
 .rich-picker > label {
   color: #0c0c0d;
   font-weight: 700;
   grid-area: label;
 }
 
@@ -57,13 +58,13 @@ payment-method-picker.rich-picker {
     "dropdown cvv    cvv  cvv"
     "invalid  invalid invalid invalid";
 }
 
 payment-method-picker > input {
   border: 1px solid #0C0C0D33;
   border-inline-start: none;
   grid-area: cvv;
-  margin: 14px 0; /* Has to be same as rich-select */
+  margin: 10px 0; /* Has to be same as rich-select */
   padding: 8px;
   /* So the error outline appears above the adjacent dropdown */
   z-index: 1;
 }
--- a/browser/components/payments/res/containers/rich-picker.js
+++ b/browser/components/payments/res/containers/rich-picker.js
@@ -31,17 +31,16 @@ export default class RichPicker extends 
     this.editLink.className = "edit-link";
     this.editLink.href = "javascript:void(0)";
     this.editLink.textContent = this.dataset.editLinkLabel;
     this.editLink.addEventListener("click", this);
 
     this.invalidLabel = document.createElement("label");
     this.invalidLabel.className = "invalid-label";
     this.invalidLabel.setAttribute("for", this.dropdown.popupBox.id);
-    this.invalidLabel.textContent = this.dataset.invalidLabel;
   }
 
   connectedCallback() {
     // The document order, by default, controls tab order so keep that in mind if changing this.
     this.appendChild(this.labelElement);
     this.appendChild(this.dropdown);
     this.appendChild(this.editLink);
     this.appendChild(this.addLink);
@@ -53,18 +52,20 @@ export default class RichPicker extends 
     if (name == "label") {
       this.labelElement.textContent = newValue;
     }
   }
 
   render(state) {
     this.editLink.hidden = !this.dropdown.value;
 
+    let errorText = this.errorForSelectedOption(state);
     this.classList.toggle("invalid-selected-option",
-                          !this.isSelectedOptionValid(state));
+                          !!errorText);
+    this.invalidLabel.textContent = errorText;
   }
 
   get selectedOption() {
     return this.dropdown.selectedOption;
   }
 
   get selectedRichOption() {
     return this.dropdown.selectedRichOption;
@@ -73,18 +74,28 @@ export default class RichPicker extends 
   get requiredFields() {
     return this.selectedOption ? this.selectedOption.requiredFields || [] : [];
   }
 
   get fieldNames() {
     return [];
   }
 
-  isSelectedOptionValid() {
-    return !this.missingFieldsOfSelectedOption().length;
+  /**
+   * @param {object} state Application state
+   * @returns {string} Containing an error message for the picker or "" for no error.
+   */
+  errorForSelectedOption(state) {
+    if (!this.selectedOption) {
+      return "";
+    }
+    if (!this.dataset.invalidLabel) {
+      throw new Error("data-invalid-label is required");
+    }
+    return this.missingFieldsOfSelectedOption().length ? this.dataset.invalidLabel : "";
   }
 
   missingFieldsOfSelectedOption() {
     let selectedOption = this.selectedOption;
     if (!selectedOption) {
       return [];
     }
 
--- a/browser/components/payments/res/debugging.html
+++ b/browser/components/payments/res/debugging.html
@@ -57,16 +57,19 @@
         </fieldset>
         <label class="block"><input type="checkbox" id="setChangesPrevented">Prevent changes</label>
 
 
         <section class="group">
           <fieldset>
             <legend>User Data Errors</legend>
             <button id="saveVisibleForm" title="Bypasses field validation">Save Visible Form</button>
+            <button id="setBasicCardErrors">Basic Card Errors</button>
+            <button id="setPayerErrors">Payer Errors</button>
             <button id="setShippingError">Shipping Error</button>
-            <button id="setAddressErrors">Address Errors</button>
+            <button id="setShippingAddressErrors">Shipping Address Errors</button>
+
           </fieldset>
         </section>
       </section>
     </div>
   </body>
 </html>
--- a/browser/components/payments/res/debugging.js
+++ b/browser/components/payments/res/debugging.js
@@ -48,16 +48,18 @@ let REQUEST_1 = {
       {
         label: "Square",
         amount: {
           currency: "USD",
           value: "5",
         },
       },
     ],
+    payer: {},
+    paymentMethod: {},
     shippingAddressErrors: {},
     shippingOptions: [
       {
         id: "std",
         label: "Standard (3-5 business days)",
         amount: {
           currency: "USD",
           value: 10,
@@ -80,17 +82,17 @@ let REQUEST_1 = {
   },
   paymentOptions: {
     requestPayerName: true,
     requestPayerEmail: false,
     requestPayerPhone: false,
     requestShipping: true,
     shippingType: "shipping",
   },
-  shippingOption: "456",
+  shippingOption: "std",
 };
 
 let REQUEST_2 = {
   tabId: 9,
   topLevelPrincipal: {URI: {displayHost: "example.com"}},
   requestId: "3797081f-a96b-c34b-a58b-1083c6e66e25",
   completeStatus: "",
   paymentMethods: [
@@ -128,16 +130,18 @@ let REQUEST_2 = {
         label: "Tax",
         type: "tax",
         amount: {
           currency: "USD",
           value: "1.50",
         },
       },
     ],
+    payer: {},
+    paymentMethod: {},
     shippingAddressErrors: {},
     shippingOptions: [
       {
         id: "123",
         label: "Fast (default)",
         amount: {
           currency: "USD",
           value: 10,
@@ -434,22 +438,82 @@ let buttonActions = {
   setDupesAddresses() {
     paymentDialog.setStateFromParent({savedAddresses: DUPED_ADDRESSES});
   },
 
   setBasicCards1() {
     paymentDialog.setStateFromParent({savedBasicCards: BASIC_CARDS_1});
   },
 
+  setBasicCardErrors() {
+    let request = Object.assign({}, requestStore.getState().request);
+    request.paymentDetails = Object.assign({}, requestStore.getState().request.paymentDetails);
+    request.paymentDetails.paymentMethod = {
+      cardNumber: "",
+      cardholderName: "",
+      cardSecurityCode: "",
+      expiryMonth: "",
+      expiryYear: "",
+      billingAddress: {
+        addressLine: "Can only buy from ROADS, not DRIVES, BOULEVARDS, or STREETS",
+        city: "Can only buy from CITIES, not TOWNSHIPS or VILLAGES",
+        country: "Can only buy from US, not CA",
+        organization: "Can only buy from CORPORATIONS, not CONSORTIUMS",
+        phone: "Only allowed to buy from area codes that start with 9",
+        postalCode: "Only allowed to buy from postalCodes that start with 0",
+        recipient: "Can only buy from names that start with J",
+        region: "Can only buy from regions that start with M",
+      },
+    };
+    requestStore.setState({
+      request,
+    });
+  },
+
+
   setChangesPrevented(evt) {
     requestStore.setState({
       changesPrevented: evt.target.checked,
     });
   },
 
+  setCompleteStatus() {
+    let input = document.querySelector("[name='setCompleteStatus']:checked");
+    let completeStatus = input.value;
+    let request = requestStore.getState().request;
+    paymentDialog.setStateFromParent({
+      request: Object.assign({}, request, { completeStatus }),
+    });
+  },
+
+  setPayerErrors() {
+    let request = Object.assign({}, requestStore.getState().request);
+    request.paymentDetails = Object.assign({}, requestStore.getState().request.paymentDetails);
+    request.paymentDetails.payer = {
+      email: "Only @mozilla.com emails are supported",
+      name: "Payer name must start with M",
+      phone: "Payer area codes must start with 1",
+    };
+    requestStore.setState({
+      request,
+    });
+  },
+
+  setPaymentOptions() {
+    let options = {};
+    let checkboxes = document.querySelectorAll("#paymentOptions input[type='checkbox']");
+    for (let input of checkboxes) {
+      options[input.name] = input.checked;
+    }
+    let req = Object.assign({}, requestStore.getState().request, {
+      paymentOptions: options,
+    });
+    requestStore.setState({ request: req });
+  },
+
   setRequest1() {
     paymentDialog.setStateFromParent({request: REQUEST_1});
   },
 
   setRequest2() {
     paymentDialog.setStateFromParent({request: REQUEST_2});
   },
 
@@ -461,39 +525,27 @@ let buttonActions = {
   },
   setRequestPayerPhone() {
     buttonActions.setPaymentOptions();
   },
   setRequestShipping() {
     buttonActions.setPaymentOptions();
   },
 
-  setPaymentOptions() {
-    let options = {};
-    let checkboxes = document.querySelectorAll("#paymentOptions input[type='checkbox']");
-    for (let input of checkboxes) {
-      options[input.name] = input.checked;
-    }
-    let req = Object.assign({}, requestStore.getState().request, {
-      paymentOptions: options,
-    });
-    requestStore.setState({ request: req });
-  },
-
   setShippingError() {
     let request = Object.assign({}, requestStore.getState().request);
     request.paymentDetails = Object.assign({}, requestStore.getState().request.paymentDetails);
-    request.paymentDetails.error = "Error!";
+    request.paymentDetails.error = "Shipping Error!";
     request.paymentDetails.shippingOptions = [];
     requestStore.setState({
       request,
     });
   },
 
-  setAddressErrors() {
+  setShippingAddressErrors() {
     let request = Object.assign({}, requestStore.getState().request);
     request.paymentDetails = Object.assign({}, requestStore.getState().request.paymentDetails);
     request.paymentDetails.shippingAddressErrors = {
       addressLine: "Can only ship to ROADS, not DRIVES, BOULEVARDS, or STREETS",
       city: "Can only ship to CITIES, not TOWNSHIPS or VILLAGES",
       country: "Can only ship to USA, not CA",
       organization: "Can only ship to CORPORATIONS, not CONSORTIUMS",
       phone: "Only allowed to ship to area codes that start with 9",
@@ -501,25 +553,16 @@ let buttonActions = {
       recipient: "Can only ship to names that start with J",
       region: "Can only ship to regions that start with M",
     };
     requestStore.setState({
       request,
     });
   },
 
-  setCompleteStatus() {
-    let input = document.querySelector("[name='setCompleteStatus']:checked");
-    let completeStatus = input.value;
-    let request = requestStore.getState().request;
-    paymentDialog.setStateFromParent({
-      request: Object.assign({}, request, { completeStatus }),
-    });
-  },
-
   toggleDirectionality() {
     let body = paymentDialog.ownerDocument.body;
     body.dir = body.dir == "rtl" ? "ltr" : "rtl";
   },
 
   toggleBranding() {
     for (let container of paymentDialog.querySelectorAll("accepted-cards")) {
       container.classList.toggle("branded");
--- a/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js
+++ b/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js
@@ -37,16 +37,18 @@ export let requestStore = new PaymentsSt
     tabId: null,
     topLevelPrincipal: {URI: {displayHost: null}},
     requestId: null,
     paymentMethods: [],
     paymentDetails: {
       id: null,
       totalItem: {label: null, amount: {currency: null, value: 0}},
       displayItems: [],
+      payer: {},
+      paymentMethod: null,
       shippingAddressErrors: {},
       shippingOptions: [],
       modifiers: null,
       error: "",
     },
     paymentOptions: {
       requestPayerName: false,
       requestPayerEmail: false,
--- a/browser/components/payments/res/paymentRequest.css
+++ b/browser/components/payments/res/paymentRequest.css
@@ -91,16 +91,21 @@ payment-dialog > header > .page-error {
 .page > .page-body > h2:empty {
   display: none;
 }
 
 .page-error {
   color: #D70022;
 }
 
+.manage-text {
+  margin: 0;
+  padding: 18px 0;
+}
+
 .page > footer {
   align-items: center;
   justify-content: end;
   background-color: #eaeaee;
   display: flex;
   /* from visual spec: */
   padding-top: 20px;
   padding-bottom: 18px;
@@ -135,46 +140,54 @@ payment-dialog > header > .page-error {
 #total > div {
   color: GrayText;
 }
 
 #view-all {
   flex: 0 1 auto;
 }
 
+payment-dialog[complete-status="processing"] #pay {
+  /* Force opacity to 1 even when disabled in the processing state. */
+  opacity: 1;
+}
+
 payment-dialog #pay::before {
   -moz-context-properties: fill;
   content: url(chrome://browser/skin/connection-secure.svg);
   fill: currentColor;
   height: 16px;
   margin-inline-end: 0.5em;
   vertical-align: text-bottom;
   width: 16px;
 }
 
 payment-dialog[changes-prevented][complete-status="fail"] #pay,
 payment-dialog[changes-prevented][complete-status="unknown"] #pay,
 payment-dialog[changes-prevented][complete-status="processing"] #pay,
 payment-dialog[changes-prevented][complete-status="success"] #pay {
   /* Show the pay button above #disabled-overlay */
   position: relative;
-  z-index: 1;
+  z-index: 51;
 }
 
 #disabled-overlay {
   background: white;
   grid-area: disabled-overlay;
   opacity: 0.6;
   width: 100%;
   height: 100%;
   position: absolute;
   top: 0;
   right: 0;
   bottom: 0;
   left: 0;
+  /* z-index must be greater than some positioned fields and #pay with z-index
+     but less than 99, the z-index of the debugging console. */
+  z-index: 50;
 }
 
 .persist-checkbox {
   padding: 5px 0;
 }
 
 .persist-checkbox > label {
   display: flex;
--- a/browser/components/payments/res/paymentRequest.js
+++ b/browser/components/payments/res/paymentRequest.js
@@ -83,19 +83,20 @@ var paymentRequest = {
   },
 
   onChromeToContent({detail}) {
     let {messageType} = detail;
     log.debug("onChromeToContent:", messageType);
 
     switch (messageType) {
       case "responseSent": {
+        let {request} = document.querySelector("payment-dialog").requestStore.getState();
         document.querySelector("payment-dialog").requestStore.setState({
           changesPrevented: true,
-          completionState: "processing",
+          request: Object.assign({}, request, { completeStatus: "processing" }),
         });
         break;
       }
       case "showPaymentRequest": {
         this.onShowPaymentRequest(detail);
         break;
       }
       case "updateState": {
--- a/browser/components/payments/test/PaymentTestUtils.jsm
+++ b/browser/components/payments/test/PaymentTestUtils.jsm
@@ -1,10 +1,12 @@
 "use strict";
 
+/* global info */
+
 var EXPORTED_SYMBOLS = ["PaymentTestUtils"];
 
 var PaymentTestUtils = {
   /**
    * Common content tasks functions to be used with ContentTask.spawn.
    */
   ContentTasks: {
     /* eslint-env mozilla/frame-script */
@@ -17,29 +19,58 @@ var PaymentTestUtils = {
       let completeException;
 
       // delay the given # milliseconds
       await new Promise(resolve => content.setTimeout(resolve, delayMs));
 
       try {
         await response.complete(result);
       } catch (ex) {
+        info(`Complete error: ${ex}`);
         completeException = {
           name: ex.name,
           message: ex.message,
         };
       }
       return {
         completeException,
         response: response.toJSON(),
         // XXX: Bug NNN: workaround for `details` not being included in `toJSON`.
         methodDetails: response.details,
       };
     },
 
+    /**
+     * Add a retry handler to the existing `showPromise` to call .retry().
+     * @returns {Object} representing the PaymentResponse
+     */
+    addRetryHandler: async ({validationErrors, delayMs = 0}) => {
+      let response = await content.showPromise;
+      let retryException;
+
+      // delay the given # milliseconds
+      await new Promise(resolve => content.setTimeout(resolve, delayMs));
+
+      try {
+        await response.retry(Cu.cloneInto(validationErrors, content));
+      } catch (ex) {
+        info(`Retry error: ${ex}`);
+        retryException = {
+          name: ex.name,
+          message: ex.message,
+        };
+      }
+      return {
+        retryException,
+        response: response.toJSON(),
+        // XXX: Bug NNN: workaround for `details` not being included in `toJSON`.
+        methodDetails: response.details,
+      };
+    },
+
     ensureNoPaymentRequestEvent: ({eventName}) => {
       content.rq.addEventListener(eventName, (event) => {
         ok(false, `Unexpected ${eventName}`);
       });
     },
 
     promisePaymentRequestEvent: ({eventName}) => {
       content[eventName + "Promise"] =
--- a/browser/components/payments/test/browser/browser.ini
+++ b/browser/components/payments/test/browser/browser.ini
@@ -14,12 +14,13 @@ skip-if = os == 'linux' && debug # bug 1
 [browser_dropdowns.js]
 [browser_host_name.js]
 [browser_openPreferences.js]
 [browser_payment_completion.js]
 [browser_payments_onboarding_wizard.js]
 [browser_profile_storage.js]
 [browser_request_serialization.js]
 [browser_request_shipping.js]
+[browser_retry.js]
 [browser_shippingaddresschange_error.js]
 [browser_show_dialog.js]
 skip-if = os == 'win' && debug # bug 1418385
 [browser_total.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/payments/test/browser/browser_retry.js
@@ -0,0 +1,95 @@
+"use strict";
+
+/**
+  * Test the merchant calling .retry().
+  */
+
+async function setup() {
+  await setupFormAutofillStorage();
+  await cleanupFormAutofillStorage();
+  let billingAddressGUID = await addAddressRecord(PTU.Addresses.TimBL);
+  let card = Object.assign({}, PTU.BasicCards.JohnDoe,
+                           { billingAddressGUID });
+  await addCardRecord(card);
+}
+
+add_task(async function test_retry_with_genericError() {
+  await setup();
+  await BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: BLANK_PAGE_URL,
+  }, async browser => {
+    let {win, frame} = await setupPaymentDialog(browser, {
+      methodData: [PTU.MethodData.basicCard],
+      details: Object.assign({}, PTU.Details.total60USD),
+      merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
+    });
+
+    await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
+      securityCode: "123",
+    });
+
+    info("clicking the button to try pay the 1st time");
+    await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
+
+    let retryUpdatePromise = spawnPaymentDialogTask(frame, async function checkDialog() {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+      let state = await PTU.DialogContentUtils.waitForState(content, ({request}) => {
+        return request.completeStatus === "processing";
+      }, "Wait for completeStatus from pay button click");
+
+      is(state.request.completeStatus, "processing", "Check completeStatus is processing");
+      is(state.request.paymentDetails.error, "", "Check error string is empty");
+      ok(state.changesPrevented, "Changes prevented");
+
+      state = await PTU.DialogContentUtils.waitForState(content, ({request}) => {
+        return request.completeStatus === "";
+      }, "Wait for completeStatus from DOM update");
+
+      is(state.request.completeStatus, "", "Check completeStatus");
+      is(state.request.paymentDetails.error, "My generic error", "Check error string in state");
+      ok(!state.changesPrevented, "Changes no longer prevented");
+      is(state.page.id, "payment-summary", "Check still on payment-summary");
+
+      ok(content.document.querySelector("payment-dialog").innerText.includes("My generic error"),
+         "Check error visibility");
+    });
+
+
+    // Add a handler to retry the payment above.
+    info("Tell merchant page to retry with an error string");
+    let retryPromise = ContentTask.spawn(browser,
+                                         {
+                                           delayMs: 1000,
+                                           validationErrors: {
+                                             error: "My generic error",
+                                           },
+                                         },
+                                         PTU.ContentTasks.addRetryHandler);
+
+    await retryUpdatePromise;
+
+    spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
+
+    // We can only check the retry response after the closing as it only resolves upon complete.
+    let {retryException} = await retryPromise;
+    ok(!retryException, "Expect no exception to be thrown when calling retry()");
+
+    // Add a handler to complete the payment above.
+    info("acknowledging the completion from the merchant page");
+    let result = await ContentTask.spawn(browser, {}, PTU.ContentTasks.addCompletionHandler);
+
+    // Verify response has the expected properties
+    let expectedDetails = Object.assign({
+      "cc-security-code": "123",
+    }, PTU.BasicCards.JohnDoe);
+
+    checkPaymentMethodDetailsMatchesCard(result.response.details, expectedDetails,
+                                         "Check response payment details");
+
+    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
+  });
+});
--- a/browser/components/payments/test/browser/head.js
+++ b/browser/components/payments/test/browser/head.js
@@ -407,42 +407,64 @@ async function navigateToAddAddressPage(
       return state.page.id == "address-page" && !state.page.guid;
     }, "Check add page state");
   }, aOptions);
 }
 
 async function fillInBillingAddressForm(frame, aAddress) {
   // For now billing and shipping address forms have the same fields but that may
   // change so use separarate helpers.
-  return fillInShippingAddressForm(frame, aAddress);
+  return fillInShippingAddressForm(frame, aAddress, {
+    expectedSelectedStateKey: ["basic-card-page", "billingAddressGUID"],
+  });
 }
 
 async function fillInShippingAddressForm(frame, aAddress, aOptions) {
   let address = Object.assign({}, aAddress);
   // Email isn't used on address forms, only payer/contact ones.
   delete address.email;
-  return fillInAddressForm(frame, address, aOptions);
+  return fillInAddressForm(frame, address, {
+    expectedSelectedStateKey: ["selectedShippingAddress"],
+    ...aOptions,
+  });
 }
 
 async function fillInPayerAddressForm(frame, aAddress) {
   let address = Object.assign({}, aAddress);
   let payerFields = ["given-name", "additional-name", "family-name", "tel", "email"];
   for (let fieldName of Object.keys(address)) {
     if (payerFields.includes(fieldName)) {
       continue;
     }
     delete address[fieldName];
   }
-  return fillInAddressForm(frame, address);
+  return fillInAddressForm(frame, address, {
+    expectedSelectedStateKey: ["selectedPayerAddress"],
+  });
 }
 
+/**
+ * @param {HTMLElement} frame
+ * @param {object} aAddress
+ * @param {object} [aOptions = {}]
+ * @param {boolean} [aOptions.setPersistCheckedValue = undefined] How to set the persist checkbox.
+ * @param {string[]} [expectedSelectedStateKey = undefined] The expected selectedStateKey for
+                                                            address-page.
+ */
 async function fillInAddressForm(frame, aAddress, aOptions = {}) {
   await spawnPaymentDialogTask(frame, async (args) => {
     let {address, options = {}} = args;
 
+    if (options.expectedSelectedStateKey) {
+      let store = Cu.waiveXrays(content.document.querySelector("address-form")).requestStore;
+      Assert.deepEqual(store.getState()["address-page"].selectedStateKey,
+                       options.expectedSelectedStateKey,
+                       "Check address page selectedStateKey");
+    }
+
     if (typeof(address.country) != "undefined") {
       // Set the country first so that the appropriate fields are visible.
       let countryField = content.document.getElementById("country");
       ok(!countryField.disabled, "Country Field shouldn't be disabled");
       await content.fillField(countryField, address.country);
       is(countryField.value, address.country, "country value is correct after fillField");
     }
 
--- a/browser/components/payments/test/mochitest/mochitest.ini
+++ b/browser/components/payments/test/mochitest/mochitest.ini
@@ -2,16 +2,19 @@
 prefs =
    dom.webcomponents.customelements.enabled=false
 support-files =
    !/browser/extensions/formautofill/content/editAddress.xhtml
    !/browser/extensions/formautofill/content/editCreditCard.xhtml
    ../../../../../browser/extensions/formautofill/content/autofillEditForms.js
    ../../../../../browser/extensions/formautofill/skin/shared/editDialog-shared.css
    ../../../../../testing/modules/sinon-2.3.2.js
+   # paymentRequest.xhtml is needed for `importDialogDependencies` so that the relative paths of
+   # formautofill/edit*.xhtml work from the *-form elements in paymentRequest.xhtml.
+   ../../res/paymentRequest.xhtml
    ../../res/**
    payments_common.js
 skip-if = !e10s
 
 [test_accepted_cards.html]
 [test_address_form.html]
 [test_address_option.html]
 skip-if = os == "linux" || os == "win" # Bug 1493216
--- a/browser/components/payments/test/mochitest/payments_common.js
+++ b/browser/components/payments/test/mochitest/payments_common.js
@@ -45,17 +45,19 @@ function promiseContentToChromeMessage(m
 
 /**
  * Import the templates and stylesheets from the real shipping dialog to avoid
  * duplication in the tests.
  * @param {HTMLIFrameElement} templateFrame - Frame to copy the resources from
  * @param {HTMLElement} destinationEl - Where to append the copied resources
  */
 function importDialogDependencies(templateFrame, destinationEl) {
-  for (let template of templateFrame.contentDocument.querySelectorAll("template")) {
+  let templates = templateFrame.contentDocument.querySelectorAll("template");
+  isnot(templates, null, "Check some templates found");
+  for (let template of templates) {
     let imported = document.importNode(template, true);
     destinationEl.appendChild(imported);
   }
 
   let baseURL = new URL("../../res/", window.location.href);
   let stylesheetLinks = templateFrame.contentDocument.querySelectorAll("link[rel~='stylesheet']");
   for (let stylesheet of stylesheetLinks) {
     let imported = document.importNode(stylesheet, true);
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -61,16 +61,24 @@ function checkAddressForm(customEl, expe
 function sendStringAndCheckValidity(element, string, isValid) {
   fillField(element, string);
   ok(element.checkValidity() == isValid,
      `${element.id} should be ${isValid ? "valid" : "invalid"} (${string})`);
 }
 
 add_task(async function test_initialState() {
   let form = new AddressForm();
+
+  await form.requestStore.setState({
+    "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
+      title: "Sample page title",
+    },
+  });
+
   let {page} = form.requestStore.getState();
   is(page.id, "payment-summary", "Check initial page");
   await form.promiseReady;
   display.appendChild(form);
   await asyncElementRendered();
   is(page.id, "payment-summary", "Check initial page after appending");
 
   // :-moz-ui-invalid, unlike :invalid, only applies to fields showing the error outline.
@@ -83,25 +91,27 @@ add_task(async function test_initialStat
 add_task(async function test_backButton() {
   let form = new AddressForm();
   form.dataset.backButtonLabel = "Back";
   await form.requestStore.setState({
     page: {
       id: "address-page",
     },
     "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
       title: "Sample page title",
     },
   });
+
   await form.promiseReady;
   display.appendChild(form);
   await asyncElementRendered();
 
   let stateChangePromise = promiseStateChange(form.requestStore);
-  is(form.pageTitleHeading.textContent, "Sample page title", "Check label");
+  is(form.pageTitleHeading.textContent, "Sample page title", "Check title");
 
   is(form.backButton.textContent, "Back", "Check label");
   form.backButton.scrollIntoView();
   synthesizeMouseAtCenter(form.backButton, {});
 
   let {page} = await stateChangePromise;
   is(page.id, "payment-summary", "Check initial page after appending");
 
@@ -203,16 +213,17 @@ add_task(async function test_edit() {
   address1.guid = "9864798564";
 
   await form.requestStore.setState({
     page: {
       id: "address-page",
     },
     "address-page": {
       guid: address1.guid,
+      selectedStateKey: ["selectedShippingAddress"],
     },
     savedAddresses: {
       [address1.guid]: deepClone(address1),
     },
   });
   await asyncElementRendered();
   is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
      "Check no fields are visibly invalid on an 'edit' form with a complete address");
@@ -226,16 +237,17 @@ add_task(async function test_edit() {
     guid: "9gnjdhen46",
   };
   await form.requestStore.setState({
     page: {
       id: "address-page",
     },
     "address-page": {
       guid: minimalAddress.guid,
+      selectedStateKey: ["selectedShippingAddress"],
     },
     savedAddresses: {
       [minimalAddress.guid]: deepClone(minimalAddress),
     },
   });
   await asyncElementRendered();
   is(form.saveButton.textContent, "Update", "Check label");
   checkAddressForm(form, minimalAddress);
@@ -245,17 +257,19 @@ add_task(async function test_edit() {
   ok(form.querySelectorAll("#country:-moz-ui-invalid").length, 1,
      "Check that the country `select` is marked as invalid");
 
   info("change to no selected address");
   await form.requestStore.setState({
     page: {
       id: "address-page",
     },
-    "address-page": {},
+    "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
+    },
   });
   await asyncElementRendered();
   is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
      "Check no fields are visibly invalid on an empty 'add' form after being an edit form");
   checkAddressForm(form, {
     country: "US",
   });
   ok(form.saveButton.disabled, "Save button should be disabled for an empty form");
@@ -267,16 +281,17 @@ add_task(async function test_restricted_
   let form = new AddressForm();
   form.dataset.nextButtonLabel = "Next";
   form.dataset.errorGenericSave = "Generic error";
   await form.promiseReady;
   display.appendChild(form);
   await form.requestStore.setState({
     "address-page": {
       addressFields: "name email tel",
+      selectedStateKey: ["selectedPayerAddress"],
     },
   });
   await asyncElementRendered();
 
   ok(form.saveButton.disabled, "Save button should be disabled due to empty fields");
 
   ok(!isHidden(form.form.querySelector("#given-name")),
      "given-name should be visible");
@@ -307,17 +322,19 @@ add_task(async function test_restricted_
   fillField(form.form.querySelector("#email"), "john@example.com");
   todo(form.saveButton.disabled,
        "Save button should be disabled due to empty fields - Bug 1483412");
   fillField(form.form.querySelector("#tel"), "+15555555555");
   ok(!form.saveButton.disabled, "Save button should be enabled with all required fields filled");
 
   form.remove();
   await form.requestStore.setState({
-    "address-page": {},
+    "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
+    },
   });
 });
 
 add_task(async function test_field_validation() {
   let form = new AddressForm();
   form.dataset.fieldRequiredSymbol = "*";
   await form.promiseReady;
   display.appendChild(form);
@@ -375,40 +392,50 @@ add_task(async function test_field_valid
   sendStringAndCheckValidity(addressLevel1Input, "", false);
   sendStringAndCheckValidity(postalCodeInput, "11109", false);
   sendStringAndCheckValidity(addressLevel1Input, "Nova Scotia", true);
   sendStringAndCheckValidity(postalCodeInput, "06390-0001", false);
 
   form.remove();
 });
 
-add_task(async function test_customMerchantValidity() {
+add_task(async function test_merchantShippingAddressErrors() {
   let form = new AddressForm();
   await form.promiseReady;
+
+  // Merchant errors only make sense when editing a record so add one.
+  let address1 = deepClone(PTU.Addresses.TimBL);
+  address1.guid = "9864798564";
+
   const state = {
     page: {
       id: "address-page",
     },
     "address-page": {
+      guid: address1.guid,
+      selectedStateKey: ["selectedShippingAddress"],
       title: "Sample page title",
     },
     request: {
       paymentDetails: {
         shippingAddressErrors: {
           addressLine: "Street address needs to start with a D",
           city: "City needs to start with a B",
           country: "Country needs to start with a C",
           organization: "organization needs to start with an A",
           phone: "Telephone needs to start with a 9",
           postalCode: "Postal code needs to start with a 0",
           recipient: "Name needs to start with a Z",
           region: "Region needs to start with a Y",
         },
       },
     },
+    savedAddresses: {
+      [address1.guid]: deepClone(address1),
+    },
   };
   await form.requestStore.setState(state);
   display.appendChild(form);
   await asyncElementRendered();
 
   function checkValidationMessage(selector, property) {
     is(form.form.querySelector(selector).validationMessage,
        state.request.paymentDetails.shippingAddressErrors[property],
@@ -429,37 +456,47 @@ add_task(async function test_customMerch
   // TODO: bug 1482808 - the save button should be enabled after editing the fields
 
   form.remove();
 });
 
 add_task(async function test_customMerchantValidity_reset() {
   let form = new AddressForm();
   await form.promiseReady;
+
+  // Merchant errors only make sense when editing a record so add one.
+  let address1 = deepClone(PTU.Addresses.TimBL);
+  address1.guid = "9864798564";
+
   const state = {
     page: {
       id: "address-page",
     },
     "address-page": {
+      guid: address1.guid,
+      selectedStateKey: ["selectedShippingAddress"],
       title: "Sample page title",
     },
     request: {
       paymentDetails: {
         shippingAddressErrors: {
           addressLine: "Street address needs to start with a D",
           city: "City needs to start with a B",
           country: "Country needs to start with a C",
           organization: "organization needs to start with an A",
           phone: "Telephone needs to start with a 9",
           postalCode: "Postal code needs to start with a 0",
           recipient: "Name needs to start with a Z",
           region: "Region needs to start with a Y",
         },
       },
     },
+    savedAddresses: {
+      [address1.guid]: deepClone(address1),
+    },
   };
   await form.requestStore.setState(state);
   display.appendChild(form);
   await asyncElementRendered();
 
   ok(form.querySelectorAll(":-moz-ui-invalid").length > 0, "Check fields are visibly invalid");
   info("merchant cleared the errors");
   await form.requestStore.setState({
@@ -467,20 +504,178 @@ add_task(async function test_customMerch
       paymentDetails: {
         shippingAddressErrors: {},
       },
     },
   });
   await asyncElementRendered();
   is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
      "Check fields are visibly valid - custom validity cleared");
+
+  form.remove();
+});
+
+add_task(async function test_customMerchantValidity_correctAddressForm() {
+  let form = new AddressForm();
+  await form.promiseReady;
+
+  // Merchant errors only make sense when editing a record so add one.
+  let address1 = deepClone(PTU.Addresses.TimBL);
+  address1.guid = "9864798564";
+
+  const state = {
+    page: {
+      id: "address-page",
+    },
+    "address-page": {
+      guid: address1.guid,
+      selectedStateKey: ["selectedShippingAddress"],
+      title: "Edit Shipping Address",
+    },
+    request: {
+      paymentDetails: {
+        shippingAddressErrors: {
+          addressLine: "Street address needs to start with a D",
+          city: "City needs to start with a B",
+          country: "Country needs to start with a C",
+          organization: "organization needs to start with an A",
+          phone: "Telephone needs to start with a 9",
+          postalCode: "Postal code needs to start with a 0",
+          recipient: "Name needs to start with a Z",
+          region: "Region needs to start with a Y",
+        },
+      },
+    },
+    savedAddresses: {
+      [address1.guid]: deepClone(address1),
+    },
+  };
+  await form.requestStore.setState(state);
+  display.appendChild(form);
+  await asyncElementRendered();
+
+  ok(form.querySelectorAll(":-moz-ui-invalid").length >= 8, "Check fields are visibly invalid");
+
+  info("change to a biling address form");
+  await form.requestStore.setState({
+    "address-page": {
+      guid: address1.guid,
+      selectedStateKey: ["basic-card-page", "billingAddressGUID"],
+      title: "Edit Billing Address",
+    },
+  });
+  await asyncElementRendered();
+  is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
+     "Check fields are visibly valid - shipping errors are not relevant to a billing address form");
+
+  info("change errors to billing address errors");
+  await form.requestStore.setState({
+    request: {
+      paymentDetails: {
+        paymentMethod: {
+          billingAddress: {
+            addressLine: "Billing Street address needs to start with a D",
+            city: "Billing City needs to start with a B",
+            country: "Billing Country needs to start with a C",
+            organization: "Billing organization needs to start with an A",
+            phone: "Billing Telephone needs to start with a 9",
+            postalCode: "Billing Postal code needs to start with a 0",
+            recipient: "Billing Name needs to start with a Z",
+            region: "Billing Region needs to start with a Y",
+          },
+        },
+      },
+    },
+  });
+  await asyncElementRendered();
+  ok(form.querySelectorAll(":-moz-ui-invalid").length >= 8,
+     "Check billing fields are visibly invalid");
+
+  info("change to an ADD shipping address form");
+  await form.requestStore.setState({
+    "address-page": {
+      guid: null,
+      selectedStateKey: ["selectedShippingAddress"],
+      title: "Edit Billing Address",
+    },
+  });
+  await asyncElementRendered();
+  is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
+     "Check fields are visibly valid - merchant errors are not relevant to an 'add' form");
+
+  form.remove();
+});
+
+add_task(async function test_merchantPayerAddressErrors() {
+  let form = new AddressForm();
+  await form.promiseReady;
+
+  // Merchant errors only make sense when editing a record so add one.
+  let address1 = deepClone(PTU.Addresses.TimBL);
+  address1.guid = "9864798564";
+
+  const state = {
+    page: {
+      id: "address-page",
+    },
+    "address-page": {
+      addressFields: "name email tel",
+      guid: address1.guid,
+      selectedStateKey: ["selectedPayerAddress"],
+      title: "Edit Payer Address",
+    },
+    request: {
+      paymentDetails: {
+        payer: {
+          email: "Email must be @mozilla.org",
+          name: "Name needs to start with a W",
+          phone: "Telephone needs to start with a 1",
+        },
+      },
+    },
+    savedAddresses: {
+      [address1.guid]: deepClone(address1),
+    },
+  };
+  await form.requestStore.setState(state);
+  display.appendChild(form);
+  await asyncElementRendered();
+
+  function checkValidationMessage(selector, property) {
+    is(form.form.querySelector(selector).validationMessage,
+       state.request.paymentDetails.payer[property],
+       "Validation message should match for " + selector);
+  }
+
+  ok(form.saveButton.disabled, "Save button should be disabled due to validation errors");
+
+  checkValidationMessage("#tel", "phone");
+  checkValidationMessage("#family-name", "name");
+  checkValidationMessage("#email", "email");
+
+  is(form.querySelectorAll(":-moz-ui-invalid").length, 3, "Check payer fields are visibly invalid");
+
+  await form.requestStore.setState({
+    request: {
+      paymentDetails: {
+        payer: {},
+      },
+    },
+  });
+  await asyncElementRendered();
+
+  is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
+     "Check payer fields are visibly valid after clearing merchant errors");
+
+  form.remove();
 });
 
 add_task(async function test_field_validation() {
-  sinon.stub(PaymentDialogUtils, "getFormFormat").returns({
+  let getFormFormatStub = sinon.stub(PaymentDialogUtils, "getFormFormat");
+  getFormFormatStub.returns({
     addressLevel1Label: "state",
     postalCodeLabel: "US",
     fieldsOrder: [
       {fieldId: "name", newLine: true},
       {fieldId: "organization", newLine: true},
       {fieldId: "street-address", newLine: true},
       {fieldId: "address-level2"},
     ],
@@ -488,16 +683,17 @@ add_task(async function test_field_valid
 
   let form = new AddressForm();
   await form.promiseReady;
   const state = {
     page: {
       id: "address-page",
     },
     "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
       title: "Sample page title",
     },
     request: {
       paymentDetails: {
         shippingAddressErrors: {},
       },
     },
   };
@@ -511,36 +707,39 @@ add_task(async function test_field_valid
   let addressLevel1Input = form.form.querySelector("#address-level1");
   ok(!postalCodeInput.value, "postal-code should be empty by default");
   ok(!addressLevel1Input.value, "address-level1 should be empty by default");
   ok(postalCodeInput.checkValidity(),
      "postal-code should be valid by default when it is not visible");
   ok(addressLevel1Input.checkValidity(),
      "address-level1 should be valid by default when it is not visible");
 
+  getFormFormatStub.restore();
   form.remove();
 });
 
-add_task(async function test_field_validation_dom_errors() {
+add_task(async function test_field_validation_dom_popup() {
   let form = new AddressForm();
   await form.promiseReady;
   const state = {
     page: {
       id: "address-page",
     },
     "address-page": {
+      selectedStateKey: ["selectedShippingAddress"],
       title: "Sample page title",
     },
   };
+
   await form.requestStore.setState(state);
   display.appendChild(form);
   await asyncElementRendered();
 
   const BAD_POSTAL_CODE = "hi mom";
-  let postalCode = document.getElementById("postal-code");
+  let postalCode = form.querySelector("#postal-code");
   postalCode.focus();
   sendString(BAD_POSTAL_CODE, window);
   postalCode.blur();
   let errorTextSpan = postalCode.parentNode.querySelector(".error-text");
   is(errorTextSpan.textContent, "Please match the requested format.",
      "DOM validation messages should be reflected in the error-text #1");
 
   postalCode.focus();
--- a/browser/components/payments/test/mochitest/test_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_address_picker.html
@@ -18,16 +18,17 @@ Test the address-picker component
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <address-picker id="picker1"
                     data-field-separator=", "
+                    data-invalid-label="Picker1: Missing or Invalid"
                     selected-state-key="selectedShippingAddress"></address-picker>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
@@ -145,16 +146,17 @@ add_task(async function test_change_sele
   let selectedOption = picker1.dropdown.selectedOption;
   is(selectedOption, options[2], "Selected option should now be the third option");
   selectedShippingAddress = picker1.requestStore.getState().selectedShippingAddress;
   is(selectedShippingAddress, selectedOption.getAttribute("guid"),
      "store should have third option selected");
   // The third option is missing some fields. Make sure that it is marked as such.
   ok(picker1.classList.contains("invalid-selected-option"), "The third option is missing fields");
   ok(!isHidden(picker1.invalidLabel), "The invalid label should be visible");
+  is(picker1.invalidLabel.innerText, picker1.dataset.invalidLabel, "Check displayed error text");
 
   picker1.dropdown.popupBox.focus();
   synthesizeKey(options[1].getAttribute("name"), {});
   await asyncElementRendered();
 
   selectedOption = picker1.dropdown.selectedOption;
   is(selectedOption, options[1], "Selected option should now be the second option");
   selectedShippingAddress = picker1.requestStore.getState().selectedShippingAddress;
@@ -204,12 +206,72 @@ add_task(async function test_delete() {
       },
     },
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 1, "Check dropdown has one remaining address");
   ok(options[0].textContent.includes("Mrs. Bar"), "Check remaining address");
 });
+
+add_task(async function test_merchantError() {
+  picker1.requestStore.setState({
+    selectedShippingAddress: "68gjdh354j",
+  });
+  await asyncElementRendered();
+
+  is(picker1.selectedStateKey, "selectedShippingAddress", "Check selectedStateKey");
+
+  let state = picker1.requestStore.getState();
+  let {
+    request,
+  } = state;
+  ok(!picker1.classList.contains("invalid-selected-option"), "No validation on a valid option");
+  ok(isHidden(picker1.invalidLabel), "The invalid label should be hidden");
+
+  let requestWithShippingAddressErrors = deepClone(request);
+  Object.assign(requestWithShippingAddressErrors.paymentDetails, {
+    shippingAddressErrors: {
+      country: "Your country is not supported",
+    },
+  });
+  picker1.requestStore.setState({
+    request: requestWithShippingAddressErrors,
+  });
+  await asyncElementRendered();
+
+  ok(picker1.classList.contains("invalid-selected-option"), "The merchant error applies");
+  ok(!isHidden(picker1.invalidLabel), "The merchant error should be visible");
+  is(picker1.invalidLabel.innerText, "Your country is not supported", "Check displayed error text");
+
+  info("update the request to remove the errors");
+  picker1.requestStore.setState({
+    request,
+  });
+  await asyncElementRendered();
+  ok(!picker1.classList.contains("invalid-selected-option"),
+     "No errors visible when merchant errors cleared");
+  ok(isHidden(picker1.invalidLabel), "The invalid label should be hidden");
+
+  info("Set billing address and payer errors which aren't relevant to this picker");
+  let requestWithNonShippingAddressErrors = deepClone(request);
+  Object.assign(requestWithNonShippingAddressErrors.paymentDetails, {
+    payer: {
+      name: "Your name is too short",
+    },
+    paymentMethod: {
+      billingAddress: {
+        country: "Your billing country is not supported",
+      },
+    },
+    shippingAddressErrors: {},
+  });
+  picker1.requestStore.setState({
+    request: requestWithNonShippingAddressErrors,
+  });
+  await asyncElementRendered();
+  ok(!picker1.classList.contains("invalid-selected-option"), "No errors on a shipping picker");
+  ok(isHidden(picker1.invalidLabel), "The invalid label should still be hidden");
+});
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -16,17 +16,17 @@ Test the basic-card-form element
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/accepted-cards.css"/>
 </head>
 <body>
   <p id="display" style="height: 100vh; margin: 0;">
-    <iframe id="templateFrame" src="../../res/paymentRequest.xhtml" width="0" height="0"
+    <iframe id="templateFrame" src="paymentRequest.xhtml" width="0" height="0"
             style="float: left;"></iframe>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
--- a/browser/components/payments/test/mochitest/test_payment_dialog.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog.html
@@ -16,17 +16,17 @@ Test the payment-dialog custom element
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
 </head>
 <body>
   <p id="display" style="height: 100vh; margin: 0;">
-    <iframe id="templateFrame" src="../../res/paymentRequest.xhtml" width="0" height="0"
+    <iframe id="templateFrame" src="paymentRequest.xhtml" width="0" height="0"
             style="float: left;"></iframe>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
@@ -187,16 +187,33 @@ add_task(async function test_processing_
     changesPrevented: true,
     request: Object.assign({}, request, {completeStatus: "processing"}),
   });
   await asyncElementRendered();
 
   let primaryButtons = document.querySelectorAll("footer button.primary");
   ok(Array.from(primaryButtons).every(el => isHidden(el) || el.disabled),
      "all primary footer buttons are hidden or disabled");
+
+  info("Got an update from the parent process with an error from .retry()");
+  request = el1.requestStore.getState().request;
+  let paymentDetails = deepClone(request.paymentDetails);
+  paymentDetails.error = "Sample retry error";
+  await el1.setStateFromParent({
+    request: Object.assign({}, request, {
+      completeStatus: "",
+      paymentDetails,
+    }),
+  });
+  await asyncElementRendered();
+
+  let {changesPrevented, page} = el1.requestStore.getState();
+  ok(!changesPrevented, "Changes should no longer be prevented");
+  is(page.id, "payment-summary", "Check back on payment-summary");
+  ok(el1.innerText.includes("Sample retry error"), "Check error text is visible");
 });
 
 add_task(async function test_success_unknown_completeStatus() {
   // in the "success" and "unknown" completion states the dialog would normally be closed
   // so just ensure it is left in a good state
   for (let completeStatus of ["success", "unknown"]) {
     await setup();
     let {request} = el1.requestStore.getState();
--- a/browser/components/payments/test/mochitest/test_payment_dialog_required_top_level_items.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog_required_top_level_items.html
@@ -15,17 +15,17 @@ Test the payment-dialog custom element
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
 </head>
 <body>
   <p id="display" style="height: 100vh; margin: 0;">
-    <iframe id="templateFrame" src="../../res/paymentRequest.xhtml" width="0" height="0"
+    <iframe id="templateFrame" src="paymentRequest.xhtml" width="0" height="0"
             style="float: left;"></iframe>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
--- a/browser/components/payments/test/mochitest/test_payment_method_picker.html
+++ b/browser/components/payments/test/mochitest/test_payment_method_picker.html
@@ -16,16 +16,17 @@ Test the payment-method-picker component
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/basic-card-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <payment-method-picker id="picker1"
+                           data-invalid-label="picker1: Missing or invalid"
                            selected-state-key="selectedPaymentCard"></payment-method-picker>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
@@ -148,16 +149,17 @@ add_task(async function test_change_sele
   is(selectedOption, options[2], "Selected option should now be the third option");
   selectedPaymentCard = picker1.requestStore.getState().selectedPaymentCard;
   is(selectedPaymentCard, selectedOption.getAttribute("guid"),
      "store should have third option selected");
   selectedPaymentCardSecurityCode = picker1.requestStore.getState().selectedPaymentCardSecurityCode;
   is(selectedPaymentCardSecurityCode, null, "store should have empty security code");
   ok(picker1.classList.contains("invalid-selected-option"), "Missing fields for the third option");
   ok(!isHidden(picker1.invalidLabel), "The invalid label should be visible");
+  is(picker1.invalidLabel.innerText, picker1.dataset.invalidLabel, "Check displayed error text");
 
   await SimpleTest.promiseFocus();
   picker1.dropdown.popupBox.focus();
   synthesizeKey("visa", {});
   await asyncElementRendered();
   ok(true, "Focused the security code field");
   ok(!picker1.open, "Picker should be closed");
 
@@ -244,16 +246,17 @@ add_task(async function test_supportedNe
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 1, "Check dropdown has one card");
   ok(options[0].textContent.includes("J Smith"), "Check remaining card #1");
 
   ok(picker1.classList.contains("invalid-selected-option"),
      "Check discover is recognized as not supported");
+  is(picker1.invalidLabel.innerText, picker1.dataset.invalidLabel, "Check displayed error text");
 
   info("change the card to be a visa");
   await picker1.requestStore.setState({
     tempBasicCards: {
       "68gjdh354j": {
         "cc-exp": "2017-08",
         "cc-exp-month": 8,
         "cc-exp-year": 2017,
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -733,32 +733,34 @@
         <getter><![CDATA[
           return this._selectedButton;
         ]]></getter>
         <setter><![CDATA[
           if (val && val.classList.contains("dummy")) {
             // Never select dummy buttons.
             val = null;
           }
-          if (this._selectedButton) {
-            this._selectedButton.removeAttribute("selected");
+          let previousButton = this._selectedButton;
+          if (previousButton) {
+            previousButton.removeAttribute("selected");
           }
           if (val) {
             val.setAttribute("selected", "true");
           }
           this._selectedButton = val;
           this._updateStateForButton(null);
           if (val && !val.engine) {
             // If the button doesn't have an engine, then clear the popup's
             // selection to indicate that pressing Return while the button is
             // selected will do the button's command, not search.
             this.popup.selectedIndex = -1;
           }
-          let event = document.createEvent("Events");
-          event.initEvent("SelectedOneOffButtonChanged", true, false);
+          let event = new CustomEvent("SelectedOneOffButtonChanged", {
+            previousSelectedButton: previousButton,
+          });
           this.dispatchEvent(event);
           return val;
         ]]></setter>
       </property>
 
       <!-- The index of the selected one-off, including the add-engine button
            and the search-settings button.  -1 if no one-off is selected. -->
       <property name="selectedButtonIndex">
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -122,17 +122,17 @@ var WindowHelper = {
     WINDOW_EVENTS.forEach(function(event) {
       window.removeEventListener(event, _handleEvent);
     });
 
     let messageManager = window.getGroupMessageManager("browsers");
     messageManager.removeMessageListener("Browser:Init", _handleMessage);
   },
 
-  onActivate(window, hasFocus) {
+  onActivate(window) {
     // If this window was the last focused window, we don't need to do anything
     if (window == _trackedWindows[0])
       return;
 
     _untrackWindowOrder(window);
     _trackWindowOrder(window);
 
     _updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -379,16 +379,17 @@ nsDocShell::nsDocShell()
   , mIsExecutingOnLoadHandler(false)
   , mIsPrintingOrPP(false)
   , mSavingOldViewer(false)
   , mDynamicallyCreated(false)
   , mAffectPrivateSessionLifetime(true)
   , mInvisible(false)
   , mHasLoadedNonBlankURI(false)
   , mBlankTiming(false)
+  , mTitleValidForCurrentURI(false)
 {
   mHistoryID.m0 = 0;
   mHistoryID.m1 = 0;
   mHistoryID.m2 = 0;
   AssertOriginAttributesMatchPrivateBrowsing();
 
   nsContentUtils::GenerateUUIDInPlace(mHistoryID);
 
@@ -1326,16 +1327,22 @@ nsDocShell::SetCurrentURI(nsIURI* aURI, 
            this, aURI ? aURI->GetSpecOrDefault().get() : ""));
 
   // We don't want to send a location change when we're displaying an error
   // page, and we don't want to change our idea of "current URI" either
   if (mLoadType == LOAD_ERROR_PAGE) {
     return false;
   }
 
+  bool uriIsEqual = false;
+  if (!mCurrentURI || !aURI ||
+      NS_FAILED(mCurrentURI->Equals(aURI, &uriIsEqual)) || !uriIsEqual) {
+    mTitleValidForCurrentURI = false;
+  }
+
   mCurrentURI = aURI;
 
   if (!NS_IsAboutBlank(mCurrentURI)) {
     mHasLoadedNonBlankURI = true;
   }
 
   bool isRoot = false;  // Is this the root docshell
   bool isSubFrame = false;  // Is this a subframe navigation?
@@ -5981,18 +5988,25 @@ nsDocShell::GetTitle(nsAString& aTitle)
 {
   aTitle = mTitle;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetTitle(const nsAString& aTitle)
 {
+  // Avoid unnecessary updates of the title if the URI and the title haven't
+  // changed.
+  if (mTitleValidForCurrentURI && mTitle == aTitle) {
+    return NS_OK;
+  }
+
   // Store local title
   mTitle = aTitle;
+  mTitleValidForCurrentURI = true;
 
   nsCOMPtr<nsIDocShellTreeItem> parent;
   GetSameTypeParent(getter_AddRefs(parent));
 
   // When title is set on the top object it should then be passed to the
   // tree owner.
   if (!parent) {
     nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -1168,11 +1168,14 @@ private: // data members
   bool mAffectPrivateSessionLifetime : 1;
   bool mInvisible : 1;
   bool mHasLoadedNonBlankURI : 1;
 
   // This flag means that mTiming has been initialized but nulled out.
   // We will check the innerWin's timing before creating a new one
   // in MaybeInitTiming()
   bool mBlankTiming : 1;
+
+  // This flag indicates when the title is valid for the current URI.
+  bool mTitleValidForCurrentURI : 1;
 };
 
 #endif /* nsDocShell_h__ */
--- a/dom/media/ReaderProxy.cpp
+++ b/dom/media/ReaderProxy.cpp
@@ -57,18 +57,19 @@ RefPtr<ReaderProxy::AudioDataPromise>
 ReaderProxy::OnAudioDataRequestCompleted(RefPtr<AudioData> aAudio)
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
 
   // Subtract the start time and add the looping-offset time.
   int64_t offset =
     StartTime().ToMicroseconds() - mLoopingOffset.ToMicroseconds();
   aAudio->AdjustForStartTime(offset);
-  if (aAudio->mTime.IsValid()) {
-    mLastAudioEndTime = aAudio->mTime;
+  if (aAudio->mTime.IsValid() && aAudio->GetEndTime().IsValid() &&
+      CorrectTimeOfAudioDataIfNeeded(aAudio)) {
+    UpdateLastAudioEndTime(aAudio);
     return AudioDataPromise::CreateAndResolve(aAudio.forget(), __func__);
   }
   return AudioDataPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_OVERFLOW_ERR,
                                            __func__);
 }
 
 RefPtr<ReaderProxy::AudioDataPromise>
 ReaderProxy::OnAudioDataRequestFailed(const MediaResult& aError)
@@ -109,16 +110,38 @@ ReaderProxy::OnAudioDataRequestFailed(co
            [self](RefPtr<AudioData> aAudio) {
              return self->OnAudioDataRequestCompleted(aAudio.forget());
            },
            [](const MediaResult& aError) {
              return AudioDataPromise::CreateAndReject(aError, __func__);
            });
 }
 
+bool
+ReaderProxy::CorrectTimeOfAudioDataIfNeeded(const RefPtr<AudioData>& aAudio)
+{
+  MOZ_ASSERT(aAudio->mTime.IsValid() && mLastAudioEndTime.IsValid());
+  // The start time of the current audio data should be greater than the end
+  // time of the previous audio data.
+  if (aAudio->mTime < mLastAudioEndTime) {
+    aAudio->mTime = mLastAudioEndTime;
+  }
+  return aAudio->GetEndTime().IsValid();
+}
+
+void
+ReaderProxy::UpdateLastAudioEndTime(const AudioData* aAudio)
+{
+  MOZ_ASSERT(aAudio);
+  MOZ_ASSERT(aAudio->GetEndTime().IsValid() && mLastAudioEndTime.IsValid());
+  // Make sure the end time of the audio data are non-decreasing.
+  MOZ_ASSERT(aAudio->GetEndTime() >= mLastAudioEndTime);
+  mLastAudioEndTime = aAudio->GetEndTime();
+}
+
 RefPtr<ReaderProxy::AudioDataPromise>
 ReaderProxy::RequestAudioData()
 {
   MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
   MOZ_ASSERT(!mShutdown);
 
   mSeamlessLoopingBlocked = false;
   return InvokeAsync(mReader->OwnerThread(),
--- a/dom/media/ReaderProxy.h
+++ b/dom/media/ReaderProxy.h
@@ -95,16 +95,22 @@ private:
   void UpdateDuration();
   RefPtr<SeekPromise> SeekInternal(const SeekTarget& aTarget);
 
   RefPtr<ReaderProxy::AudioDataPromise> OnAudioDataRequestCompleted(
     RefPtr<AudioData> aAudio);
   RefPtr<ReaderProxy::AudioDataPromise> OnAudioDataRequestFailed(
     const MediaResult& aError);
 
+  // Make sure the timestamp of the audio data increase monotonically by
+  // adjusting it according to mLastAudioEndTime. Returns true if the
+  // endtime is valid after correction and false otherwise.
+  bool CorrectTimeOfAudioDataIfNeeded(const RefPtr<AudioData>& aAudio);
+  void UpdateLastAudioEndTime(const AudioData* aAudio);
+
   const RefPtr<AbstractThread> mOwnerThread;
   const RefPtr<MediaFormatReader> mReader;
 
   bool mShutdown = false;
   Maybe<media::TimeUnit> mStartTime;
 
   // State-watching manager.
   WatchManager<ReaderProxy> mWatchManager;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -37,20 +37,43 @@
 #include "vm/TypeInference-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
 using mozilla::AssertedCast;
 
 BaselineCompiler::BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCompilerSpecific(cx, alloc, script),
+  : cx(cx),
+    script(script),
+    pc(script->code()),
+    ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
+    compileDebugInstrumentation_(script->isDebuggee()),
+    alloc_(alloc),
+    analysis_(alloc, script),
+    frame(script, masm),
+    stubSpace_(),
+    icEntries_(),
+    pcMappingEntries_(),
+    icLoadLabels_(),
+    pushedBeforeCall_(0),
+#ifdef DEBUG
+    inCall_(false),
+#endif
+    profilerPushToggleOffset_(),
+    profilerEnterFrameToggleOffset_(),
+    profilerExitFrameToggleOffset_(),
+    traceLoggerToggleOffsets_(cx),
+    traceLoggerScriptTextIdOffset_(),
     yieldAndAwaitOffsets_(cx),
     modifiesArguments_(false)
 {
+#ifdef JS_CODEGEN_NONE
+    MOZ_CRASH();
+#endif
 }
 
 bool
 BaselineCompiler::init()
 {
     if (!analysis_.init(alloc_, cx->caches().gsnCache)) {
         return false;
     }
@@ -556,16 +579,119 @@ BaselineCompiler::emitIC(ICStub* stub, I
     entry->setReturnOffset(CodeOffset(masm.currentOffset()));
     if (!addICLoadLabel(patchOffset)) {
         return false;
     }
 
     return true;
 }
 
+void
+BaselineCompiler::prepareVMCall()
+{
+    pushedBeforeCall_ = masm.framePushed();
+#ifdef DEBUG
+    inCall_ = true;
+#endif
+
+    // Ensure everything is synced.
+    frame.syncStack(0);
+
+    // Save the frame pointer.
+    masm.Push(BaselineFrameReg);
+}
+
+bool
+BaselineCompiler::callVM(const VMFunction& fun, CallVMPhase phase)
+{
+    TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
+
+#ifdef DEBUG
+    // Assert prepareVMCall() has been called.
+    MOZ_ASSERT(inCall_);
+    inCall_ = false;
+
+    // Assert the frame does not have an override pc when we're executing JIT code.
+    {
+        Label ok;
+        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
+                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
+        masm.assumeUnreachable("BaselineFrame shouldn't override pc when executing JIT code");
+        masm.bind(&ok);
+    }
+#endif
+
+    // Compute argument size. Note that this include the size of the frame pointer
+    // pushed by prepareVMCall.
+    uint32_t argSize = fun.explicitStackSlots() * sizeof(void*) + sizeof(void*);
+
+    // Assert all arguments were pushed.
+    MOZ_ASSERT(masm.framePushed() - pushedBeforeCall_ == argSize);
+
+    Address frameSizeAddress(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize());
+    uint32_t frameVals = frame.nlocals() + frame.stackDepth();
+    uint32_t frameBaseSize = BaselineFrame::FramePointerOffset + BaselineFrame::Size();
+    uint32_t frameFullSize = frameBaseSize + (frameVals * sizeof(Value));
+    if (phase == POST_INITIALIZE) {
+        masm.store32(Imm32(frameFullSize), frameSizeAddress);
+        uint32_t descriptor = MakeFrameDescriptor(frameFullSize + argSize, FrameType::BaselineJS,
+                                                  ExitFrameLayout::Size());
+        masm.push(Imm32(descriptor));
+
+    } else if (phase == PRE_INITIALIZE) {
+        masm.store32(Imm32(frameBaseSize), frameSizeAddress);
+        uint32_t descriptor = MakeFrameDescriptor(frameBaseSize + argSize, FrameType::BaselineJS,
+                                                  ExitFrameLayout::Size());
+        masm.push(Imm32(descriptor));
+
+    } else {
+        MOZ_ASSERT(phase == CHECK_OVER_RECURSED);
+        Label afterWrite;
+        Label writePostInitialize;
+
+        // If OVER_RECURSED is set, then frame locals haven't been pushed yet.
+        masm.branchTest32(Assembler::Zero,
+                          frame.addressOfFlags(),
+                          Imm32(BaselineFrame::OVER_RECURSED),
+                          &writePostInitialize);
+
+        masm.move32(Imm32(frameBaseSize), ICTailCallReg);
+        masm.jump(&afterWrite);
+
+        masm.bind(&writePostInitialize);
+        masm.move32(Imm32(frameFullSize), ICTailCallReg);
+
+        masm.bind(&afterWrite);
+        masm.store32(ICTailCallReg, frameSizeAddress);
+        masm.add32(Imm32(argSize), ICTailCallReg);
+        masm.makeFrameDescriptor(ICTailCallReg, FrameType::BaselineJS, ExitFrameLayout::Size());
+        masm.push(ICTailCallReg);
+    }
+    MOZ_ASSERT(fun.expectTailCall == NonTailCall);
+    // Perform the call.
+    masm.call(code);
+    uint32_t callOffset = masm.currentOffset();
+    masm.pop(BaselineFrameReg);
+
+#ifdef DEBUG
+    // Assert the frame does not have an override pc when we're executing JIT code.
+    {
+        Label ok;
+        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
+                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
+        masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call");
+        masm.bind(&ok);
+    }
+#endif
+
+    // Add a fake ICEntry (without stubs), so that the return offset to
+    // pc mapping works.
+    return appendICEntry(ICEntry::Kind_CallVM, callOffset);
+}
+
 typedef bool (*CheckOverRecursedWithExtraFn)(JSContext*, BaselineFrame*, uint32_t, uint32_t);
 static const VMFunction CheckOverRecursedWithExtraInfo =
     FunctionInfo<CheckOverRecursedWithExtraFn>(CheckOverRecursedWithExtra,
                                                "CheckOverRecursedWithExtra");
 
 bool
 BaselineCompiler::emitStackCheck(bool earlyCheck)
 {
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -2,34 +2,21 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_BaselineCompiler_h
 #define jit_BaselineCompiler_h
 
+#include "jit/BaselineFrameInfo.h"
+#include "jit/BaselineIC.h"
+#include "jit/BytecodeAnalysis.h"
 #include "jit/FixedList.h"
-#if defined(JS_CODEGEN_X86)
-# include "jit/x86/BaselineCompiler-x86.h"
-#elif defined(JS_CODEGEN_X64)
-# include "jit/x64/BaselineCompiler-x64.h"
-#elif defined(JS_CODEGEN_ARM)
-# include "jit/arm/BaselineCompiler-arm.h"
-#elif defined(JS_CODEGEN_ARM64)
-# include "jit/arm64/BaselineCompiler-arm64.h"
-#elif defined(JS_CODEGEN_MIPS32)
-# include "jit/mips32/BaselineCompiler-mips32.h"
-#elif defined(JS_CODEGEN_MIPS64)
-# include "jit/mips64/BaselineCompiler-mips64.h"
-#elif defined(JS_CODEGEN_NONE)
-# include "jit/none/BaselineCompiler-none.h"
-#else
-# error "Unknown architecture!"
-#endif
+#include "jit/MacroAssembler.h"
 
 namespace js {
 namespace jit {
 
 #define OPCODE_LIST(_)         \
     _(JSOP_NOP)                \
     _(JSOP_NOP_DESTRUCTURING)  \
     _(JSOP_LABEL)              \
@@ -255,21 +242,71 @@ namespace jit {
     _(JSOP_INITHOMEOBJECT)     \
     _(JSOP_BUILTINPROTO)       \
     _(JSOP_OBJWITHPROTO)       \
     _(JSOP_FUNWITHPROTO)       \
     _(JSOP_CLASSCONSTRUCTOR)   \
     _(JSOP_DERIVEDCONSTRUCTOR) \
     _(JSOP_IMPORTMETA)
 
-class BaselineCompiler : public BaselineCompilerSpecific
+class BaselineCompiler final
 {
-    FixedList<Label>            labels_;
-    NonAssertingLabel           return_;
-    NonAssertingLabel           postBarrierSlot_;
+    JSContext* cx;
+    JSScript* script;
+    jsbytecode* pc;
+    StackMacroAssembler masm;
+    bool ionCompileable_;
+    bool compileDebugInstrumentation_;
+
+    TempAllocator& alloc_;
+    BytecodeAnalysis analysis_;
+    FrameInfo frame;
+
+    FallbackICStubSpace stubSpace_;
+    js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
+
+    // Stores the native code offset for a bytecode pc.
+    struct PCMappingEntry
+    {
+        uint32_t pcOffset;
+        uint32_t nativeOffset;
+        PCMappingSlotInfo slotInfo;
+
+        // If set, insert a PCMappingIndexEntry before encoding the
+        // current entry.
+        bool addIndexEntry;
+    };
+
+    js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;
+
+    // Labels for the 'movWithPatch' for loading IC entry pointers in
+    // the generated IC-calling code in the main jitcode.  These need
+    // to be patched with the actual icEntry offsets after the BaselineScript
+    // has been allocated.
+    struct ICLoadLabel {
+        size_t icEntry;
+        CodeOffset label;
+    };
+    js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;
+
+    uint32_t pushedBeforeCall_;
+#ifdef DEBUG
+    bool inCall_;
+#endif
+
+    CodeOffset profilerPushToggleOffset_;
+    CodeOffset profilerEnterFrameToggleOffset_;
+    CodeOffset profilerExitFrameToggleOffset_;
+
+    Vector<CodeOffset> traceLoggerToggleOffsets_;
+    CodeOffset traceLoggerScriptTextIdOffset_;
+
+    FixedList<Label> labels_;
+    NonAssertingLabel return_;
+    NonAssertingLabel postBarrierSlot_;
 
     // Native code offset right before the scope chain is initialized.
     CodeOffset prologueOffset_;
 
     // Native code offset right before the frame is popped and the method
     // returned from.
     CodeOffset epilogueOffset_;
 
@@ -296,17 +333,112 @@ class BaselineCompiler : public Baseline
     }
 
   public:
     BaselineCompiler(JSContext* cx, TempAllocator& alloc, JSScript* script);
     MOZ_MUST_USE bool init();
 
     MethodStatus compile();
 
+    void setCompileDebugInstrumentation() {
+        compileDebugInstrumentation_ = true;
+    }
+
   private:
+    ICEntry* allocateICEntry(ICStub* stub, ICEntry::Kind kind) {
+        if (!stub) {
+            return nullptr;
+        }
+
+        // Create the entry and add it to the vector.
+        if (!icEntries_.append(ICEntry(script->pcToOffset(pc), kind))) {
+            ReportOutOfMemory(cx);
+            return nullptr;
+        }
+        ICEntry& vecEntry = icEntries_.back();
+
+        // Set the first stub for the IC entry to the fallback stub
+        vecEntry.setFirstStub(stub);
+
+        // Return pointer to the IC entry
+        return &vecEntry;
+    }
+
+    // Append an ICEntry without a stub.
+    bool appendICEntry(ICEntry::Kind kind, uint32_t returnOffset) {
+        ICEntry entry(script->pcToOffset(pc), kind);
+        entry.setReturnOffset(CodeOffset(returnOffset));
+        if (!icEntries_.append(entry)) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+        return true;
+    }
+
+    bool addICLoadLabel(CodeOffset label) {
+        MOZ_ASSERT(!icEntries_.empty());
+        ICLoadLabel loadLabel;
+        loadLabel.label = label;
+        loadLabel.icEntry = icEntries_.length() - 1;
+        if (!icLoadLabels_.append(loadLabel)) {
+            ReportOutOfMemory(cx);
+            return false;
+        }
+        return true;
+    }
+
+    JSFunction* function() const {
+        // Not delazifying here is ok as the function is guaranteed to have
+        // been delazified before compilation started.
+        return script->functionNonDelazifying();
+    }
+
+    ModuleObject* module() const {
+        return script->module();
+    }
+
+    PCMappingSlotInfo getStackTopSlotInfo() {
+        MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
+        switch (frame.numUnsyncedSlots()) {
+          case 0:
+            return PCMappingSlotInfo::MakeSlotInfo();
+          case 1:
+            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)));
+          case 2:
+          default:
+            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)),
+                                                   PCMappingSlotInfo::ToSlotLocation(frame.peek(-2)));
+        }
+    }
+
+    template <typename T>
+    void pushArg(const T& t) {
+        masm.Push(t);
+    }
+    void prepareVMCall();
+
+    enum CallVMPhase {
+        POST_INITIALIZE,
+        PRE_INITIALIZE,
+        CHECK_OVER_RECURSED
+    };
+    bool callVM(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE);
+
+    bool callVMNonOp(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE) {
+        if (!callVM(fun, phase)) {
+            return false;
+        }
+        icEntries_.back().setFakeKind(ICEntry::Kind_NonOpCallVM);
+        return true;
+    }
+
+    BytecodeAnalysis& analysis() {
+        return analysis_;
+    }
+
     MethodStatus emitBody();
 
     MOZ_MUST_USE bool emitCheckThis(ValueOperand val, bool reinit=false);
     void emitLoadReturnValue(ValueOperand val);
 
     void emitInitializeLocals();
     MOZ_MUST_USE bool emitPrologue();
     MOZ_MUST_USE bool emitEpilogue();
deleted file mode 100644
--- a/js/src/jit/arm/BaselineCompiler-arm.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/arm/BaselineCompiler-arm.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerARM::BaselineCompilerARM(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCompilerShared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/arm/BaselineCompiler-arm.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_arm_BaselineCompiler_arm_h
-#define jit_arm_BaselineCompiler_arm_h
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerARM : public BaselineCompilerShared
-{
-  protected:
-    BaselineCompilerARM(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-typedef BaselineCompilerARM BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_arm_BaselineCompiler_arm_h */
deleted file mode 100644
--- a/js/src/jit/arm64/BaselineCompiler-arm64.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_arm64_BaselineCompiler_arm64_h
-#define jit_arm64_BaselineCompiler_arm64_h
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerARM64 : public BaselineCompilerShared
-{
-  protected:
-    BaselineCompilerARM64(JSContext* cx, TempAllocator& alloc, JSScript* script)
-      : BaselineCompilerShared(cx, alloc, script)
-    { }
-};
-
-typedef BaselineCompilerARM64 BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_arm64_BaselineCompiler_arm64_h */
deleted file mode 100644
--- a/js/src/jit/mips-shared/BaselineCompiler-mips-shared.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/mips-shared/BaselineCompiler-mips-shared.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerMIPSShared::BaselineCompilerMIPSShared(JSContext* cx, TempAllocator& alloc,
-                                                       JSScript* script)
-  : BaselineCompilerShared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/mips-shared/BaselineCompiler-mips-shared.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_mips_shared_BaselineCompiler_mips_shared_h
-#define jit_mips_shared_BaselineCompiler_mips_shared_h
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerMIPSShared : public BaselineCompilerShared
-{
-  protected:
-    BaselineCompilerMIPSShared(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_mips_shared_BaselineCompiler_mips_shared_h */
deleted file mode 100644
--- a/js/src/jit/mips32/BaselineCompiler-mips32.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/mips32/BaselineCompiler-mips32.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerMIPS::BaselineCompilerMIPS(JSContext* cx, TempAllocator& alloc,
-                                           JSScript* script)
-  : BaselineCompilerMIPSShared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/mips32/BaselineCompiler-mips32.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_mips32_BaselineCompiler_mips32_h
-#define jit_mips32_BaselineCompiler_mips32_h
-
-#include "jit/mips-shared/BaselineCompiler-mips-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerMIPS : public BaselineCompilerMIPSShared
-{
-  protected:
-    BaselineCompilerMIPS(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-typedef BaselineCompilerMIPS BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_mips32_BaselineCompiler_mips32_h */
deleted file mode 100644
--- a/js/src/jit/mips64/BaselineCompiler-mips64.cpp
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/mips64/BaselineCompiler-mips64.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerMIPS64::BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc,
-                                               JSScript* script)
-  : BaselineCompilerMIPSShared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/mips64/BaselineCompiler-mips64.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_mips64_BaselineCompiler_mips64_h
-#define jit_mips64_BaselineCompiler_mips64_h
-
-#include "jit/mips-shared/BaselineCompiler-mips-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerMIPS64 : public BaselineCompilerMIPSShared
-{
-  protected:
-    BaselineCompilerMIPS64(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-typedef BaselineCompilerMIPS64 BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_mips64_BaselineCompiler_mips64_h */
deleted file mode 100644
--- a/js/src/jit/none/BaselineCompiler-none.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_none_BaselineCompiler_none_h
-#define jit_none_BaselineCompiler_none_h
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerNone : public BaselineCompilerShared
-{
-  protected:
-    BaselineCompilerNone(JSContext* cx, TempAllocator& alloc, JSScript* script)
-      : BaselineCompilerShared(cx, alloc, script)
-    {
-        MOZ_CRASH();
-    }
-};
-
-typedef BaselineCompilerNone BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_none_BaselineCompiler_none_h */
deleted file mode 100644
--- a/js/src/jit/shared/BaselineCompiler-shared.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-#include "jit/BaselineIC.h"
-#include "jit/VMFunctions.h"
-
-#include "jit/MacroAssembler-inl.h"
-#include "vm/JSScript-inl.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerShared::BaselineCompilerShared(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : cx(cx),
-    script(script),
-    pc(script->code()),
-    ionCompileable_(jit::IsIonEnabled(cx) && CanIonCompileScript(cx, script)),
-    compileDebugInstrumentation_(script->isDebuggee()),
-    alloc_(alloc),
-    analysis_(alloc, script),
-    frame(script, masm),
-    stubSpace_(),
-    icEntries_(),
-    pcMappingEntries_(),
-    icLoadLabels_(),
-    pushedBeforeCall_(0),
-#ifdef DEBUG
-    inCall_(false),
-#endif
-    profilerPushToggleOffset_(),
-    profilerEnterFrameToggleOffset_(),
-    profilerExitFrameToggleOffset_(),
-    traceLoggerToggleOffsets_(cx),
-    traceLoggerScriptTextIdOffset_()
-{ }
-
-void
-BaselineCompilerShared::prepareVMCall()
-{
-    pushedBeforeCall_ = masm.framePushed();
-#ifdef DEBUG
-    inCall_ = true;
-#endif
-
-    // Ensure everything is synced.
-    frame.syncStack(0);
-
-    // Save the frame pointer.
-    masm.Push(BaselineFrameReg);
-}
-
-bool
-BaselineCompilerShared::callVM(const VMFunction& fun, CallVMPhase phase)
-{
-    TrampolinePtr code = cx->runtime()->jitRuntime()->getVMWrapper(fun);
-
-#ifdef DEBUG
-    // Assert prepareVMCall() has been called.
-    MOZ_ASSERT(inCall_);
-    inCall_ = false;
-
-    // Assert the frame does not have an override pc when we're executing JIT code.
-    {
-        Label ok;
-        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
-                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
-        masm.assumeUnreachable("BaselineFrame shouldn't override pc when executing JIT code");
-        masm.bind(&ok);
-    }
-#endif
-
-    // Compute argument size. Note that this include the size of the frame pointer
-    // pushed by prepareVMCall.
-    uint32_t argSize = fun.explicitStackSlots() * sizeof(void*) + sizeof(void*);
-
-    // Assert all arguments were pushed.
-    MOZ_ASSERT(masm.framePushed() - pushedBeforeCall_ == argSize);
-
-    Address frameSizeAddress(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize());
-    uint32_t frameVals = frame.nlocals() + frame.stackDepth();
-    uint32_t frameBaseSize = BaselineFrame::FramePointerOffset + BaselineFrame::Size();
-    uint32_t frameFullSize = frameBaseSize + (frameVals * sizeof(Value));
-    if (phase == POST_INITIALIZE) {
-        masm.store32(Imm32(frameFullSize), frameSizeAddress);
-        uint32_t descriptor = MakeFrameDescriptor(frameFullSize + argSize, FrameType::BaselineJS,
-                                                  ExitFrameLayout::Size());
-        masm.push(Imm32(descriptor));
-
-    } else if (phase == PRE_INITIALIZE) {
-        masm.store32(Imm32(frameBaseSize), frameSizeAddress);
-        uint32_t descriptor = MakeFrameDescriptor(frameBaseSize + argSize, FrameType::BaselineJS,
-                                                  ExitFrameLayout::Size());
-        masm.push(Imm32(descriptor));
-
-    } else {
-        MOZ_ASSERT(phase == CHECK_OVER_RECURSED);
-        Label afterWrite;
-        Label writePostInitialize;
-
-        // If OVER_RECURSED is set, then frame locals haven't been pushed yet.
-        masm.branchTest32(Assembler::Zero,
-                          frame.addressOfFlags(),
-                          Imm32(BaselineFrame::OVER_RECURSED),
-                          &writePostInitialize);
-
-        masm.move32(Imm32(frameBaseSize), ICTailCallReg);
-        masm.jump(&afterWrite);
-
-        masm.bind(&writePostInitialize);
-        masm.move32(Imm32(frameFullSize), ICTailCallReg);
-
-        masm.bind(&afterWrite);
-        masm.store32(ICTailCallReg, frameSizeAddress);
-        masm.add32(Imm32(argSize), ICTailCallReg);
-        masm.makeFrameDescriptor(ICTailCallReg, FrameType::BaselineJS, ExitFrameLayout::Size());
-        masm.push(ICTailCallReg);
-    }
-    MOZ_ASSERT(fun.expectTailCall == NonTailCall);
-    // Perform the call.
-    masm.call(code);
-    uint32_t callOffset = masm.currentOffset();
-    masm.pop(BaselineFrameReg);
-
-#ifdef DEBUG
-    // Assert the frame does not have an override pc when we're executing JIT code.
-    {
-        Label ok;
-        masm.branchTest32(Assembler::Zero, frame.addressOfFlags(),
-                          Imm32(BaselineFrame::HAS_OVERRIDE_PC), &ok);
-        masm.assumeUnreachable("BaselineFrame shouldn't override pc after VM call");
-        masm.bind(&ok);
-    }
-#endif
-
-    // Add a fake ICEntry (without stubs), so that the return offset to
-    // pc mapping works.
-    return appendICEntry(ICEntry::Kind_CallVM, callOffset);
-}
deleted file mode 100644
--- a/js/src/jit/shared/BaselineCompiler-shared.h
+++ /dev/null
@@ -1,173 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_shared_BaselineCompiler_shared_h
-#define jit_shared_BaselineCompiler_shared_h
-
-#include "jit/BaselineFrameInfo.h"
-#include "jit/BaselineIC.h"
-#include "jit/BytecodeAnalysis.h"
-#include "jit/MacroAssembler.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerShared
-{
-  protected:
-    JSContext* cx;
-    JSScript* script;
-    jsbytecode* pc;
-    StackMacroAssembler masm;
-    bool ionCompileable_;
-    bool compileDebugInstrumentation_;
-
-    TempAllocator& alloc_;
-    BytecodeAnalysis analysis_;
-    FrameInfo frame;
-
-    FallbackICStubSpace stubSpace_;
-    js::Vector<ICEntry, 16, SystemAllocPolicy> icEntries_;
-
-    // Stores the native code offset for a bytecode pc.
-    struct PCMappingEntry
-    {
-        uint32_t pcOffset;
-        uint32_t nativeOffset;
-        PCMappingSlotInfo slotInfo;
-
-        // If set, insert a PCMappingIndexEntry before encoding the
-        // current entry.
-        bool addIndexEntry;
-    };
-
-    js::Vector<PCMappingEntry, 16, SystemAllocPolicy> pcMappingEntries_;
-
-    // Labels for the 'movWithPatch' for loading IC entry pointers in
-    // the generated IC-calling code in the main jitcode.  These need
-    // to be patched with the actual icEntry offsets after the BaselineScript
-    // has been allocated.
-    struct ICLoadLabel {
-        size_t icEntry;
-        CodeOffset label;
-    };
-    js::Vector<ICLoadLabel, 16, SystemAllocPolicy> icLoadLabels_;
-
-    uint32_t pushedBeforeCall_;
-#ifdef DEBUG
-    bool inCall_;
-#endif
-
-    CodeOffset profilerPushToggleOffset_;
-    CodeOffset profilerEnterFrameToggleOffset_;
-    CodeOffset profilerExitFrameToggleOffset_;
-
-    Vector<CodeOffset> traceLoggerToggleOffsets_;
-    CodeOffset traceLoggerScriptTextIdOffset_;
-
-    BaselineCompilerShared(JSContext* cx, TempAllocator& alloc, JSScript* script);
-
-    ICEntry* allocateICEntry(ICStub* stub, ICEntry::Kind kind) {
-        if (!stub) {
-            return nullptr;
-        }
-
-        // Create the entry and add it to the vector.
-        if (!icEntries_.append(ICEntry(script->pcToOffset(pc), kind))) {
-            ReportOutOfMemory(cx);
-            return nullptr;
-        }
-        ICEntry& vecEntry = icEntries_.back();
-
-        // Set the first stub for the IC entry to the fallback stub
-        vecEntry.setFirstStub(stub);
-
-        // Return pointer to the IC entry
-        return &vecEntry;
-    }
-
-    // Append an ICEntry without a stub.
-    bool appendICEntry(ICEntry::Kind kind, uint32_t returnOffset) {
-        ICEntry entry(script->pcToOffset(pc), kind);
-        entry.setReturnOffset(CodeOffset(returnOffset));
-        if (!icEntries_.append(entry)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        return true;
-    }
-
-    bool addICLoadLabel(CodeOffset label) {
-        MOZ_ASSERT(!icEntries_.empty());
-        ICLoadLabel loadLabel;
-        loadLabel.label = label;
-        loadLabel.icEntry = icEntries_.length() - 1;
-        if (!icLoadLabels_.append(loadLabel)) {
-            ReportOutOfMemory(cx);
-            return false;
-        }
-        return true;
-    }
-
-    JSFunction* function() const {
-        // Not delazifying here is ok as the function is guaranteed to have
-        // been delazified before compilation started.
-        return script->functionNonDelazifying();
-    }
-
-    ModuleObject* module() const {
-        return script->module();
-    }
-
-    PCMappingSlotInfo getStackTopSlotInfo() {
-        MOZ_ASSERT(frame.numUnsyncedSlots() <= 2);
-        switch (frame.numUnsyncedSlots()) {
-          case 0:
-            return PCMappingSlotInfo::MakeSlotInfo();
-          case 1:
-            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)));
-          case 2:
-          default:
-            return PCMappingSlotInfo::MakeSlotInfo(PCMappingSlotInfo::ToSlotLocation(frame.peek(-1)),
-                                                   PCMappingSlotInfo::ToSlotLocation(frame.peek(-2)));
-        }
-    }
-
-    template <typename T>
-    void pushArg(const T& t) {
-        masm.Push(t);
-    }
-    void prepareVMCall();
-
-    enum CallVMPhase {
-        POST_INITIALIZE,
-        PRE_INITIALIZE,
-        CHECK_OVER_RECURSED
-    };
-    bool callVM(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE);
-
-    bool callVMNonOp(const VMFunction& fun, CallVMPhase phase=POST_INITIALIZE) {
-        if (!callVM(fun, phase)) {
-            return false;
-        }
-        icEntries_.back().setFakeKind(ICEntry::Kind_NonOpCallVM);
-        return true;
-    }
-
-  public:
-    BytecodeAnalysis& analysis() {
-        return analysis_;
-    }
-
-    void setCompileDebugInstrumentation() {
-        compileDebugInstrumentation_ = true;
-    }
-};
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_shared_BaselineCompiler_shared_h */
deleted file mode 100644
--- a/js/src/jit/x64/BaselineCompiler-x64.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/x64/BaselineCompiler-x64.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerX64::BaselineCompilerX64(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCompilerX86Shared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/x64/BaselineCompiler-x64.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_x64_BaselineCompiler_x64_h
-#define jit_x64_BaselineCompiler_x64_h
-
-#include "jit/x86-shared/BaselineCompiler-x86-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerX64 : public BaselineCompilerX86Shared
-{
-  protected:
-    BaselineCompilerX64(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-typedef BaselineCompilerX64 BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_x64_BaselineCompiler_x64_h */
deleted file mode 100644
--- a/js/src/jit/x86-shared/BaselineCompiler-x86-shared.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/x86-shared/BaselineCompiler-x86-shared.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerX86Shared::BaselineCompilerX86Shared(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCompilerShared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/x86-shared/BaselineCompiler-x86-shared.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_x86_shared_BaselineCompiler_x86_shared_h
-#define jit_x86_shared_BaselineCompiler_x86_shared_h
-
-#include "jit/shared/BaselineCompiler-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerX86Shared : public BaselineCompilerShared
-{
-  protected:
-    BaselineCompilerX86Shared(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_x86_shared_BaselineCompiler_x86_shared_h */
deleted file mode 100644
--- a/js/src/jit/x86/BaselineCompiler-x86.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "jit/x86/BaselineCompiler-x86.h"
-
-using namespace js;
-using namespace js::jit;
-
-BaselineCompilerX86::BaselineCompilerX86(JSContext* cx, TempAllocator& alloc, JSScript* script)
-  : BaselineCompilerX86Shared(cx, alloc, script)
-{
-}
deleted file mode 100644
--- a/js/src/jit/x86/BaselineCompiler-x86.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef jit_x86_BaselineCompiler_x86_h
-#define jit_x86_BaselineCompiler_x86_h
-
-#include "jit/x86-shared/BaselineCompiler-x86-shared.h"
-
-namespace js {
-namespace jit {
-
-class BaselineCompilerX86 : public BaselineCompilerX86Shared
-{
-  protected:
-    BaselineCompilerX86(JSContext* cx, TempAllocator& alloc, JSScript* script);
-};
-
-typedef BaselineCompilerX86 BaselineCompilerSpecific;
-
-} // namespace jit
-} // namespace js
-
-#endif /* jit_x86_BaselineCompiler_x86_h */
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -321,17 +321,16 @@ UNIFIED_SOURCES += [
     'jit/ProcessExecutableMemory.cpp',
     'jit/RangeAnalysis.cpp',
     'jit/Recover.cpp',
     'jit/RegisterAllocator.cpp',
     'jit/RematerializedFrame.cpp',
     'jit/Safepoints.cpp',
     'jit/ScalarReplacement.cpp',
     'jit/shared/Assembler-shared.cpp',
-    'jit/shared/BaselineCompiler-shared.cpp',
     'jit/shared/CodeGenerator-shared.cpp',
     'jit/shared/Disassembler-shared.cpp',
     'jit/shared/Lowering-shared.cpp',
     'jit/Sink.cpp',
     'jit/Snapshots.cpp',
     'jit/StupidAllocator.cpp',
     'jit/TypedObjectPrediction.cpp',
     'jit/TypePolicy.cpp',
@@ -508,55 +507,51 @@ if not CONFIG['ENABLE_ION']:
         'jit/none/Trampoline-none.cpp'
     ]
 elif CONFIG['JS_CODEGEN_X86'] or CONFIG['JS_CODEGEN_X64']:
     LOpcodesGenerated.inputs += ['jit/x86-shared/LIR-x86-shared.h']
     UNIFIED_SOURCES += [
         'jit/x86-shared/Architecture-x86-shared.cpp',
         'jit/x86-shared/Assembler-x86-shared.cpp',
         'jit/x86-shared/AssemblerBuffer-x86-shared.cpp',
-        'jit/x86-shared/BaselineCompiler-x86-shared.cpp',
         'jit/x86-shared/CodeGenerator-x86-shared.cpp',
         'jit/x86-shared/Lowering-x86-shared.cpp',
         'jit/x86-shared/MacroAssembler-x86-shared-SIMD.cpp',
         'jit/x86-shared/MacroAssembler-x86-shared.cpp',
         'jit/x86-shared/MoveEmitter-x86-shared.cpp',
     ]
     SOURCES += [
         'jit/x86-shared/Disassembler-x86-shared.cpp',  # using namespace js::jit::X86Encoding;
     ]
     if CONFIG['JS_CODEGEN_X64']:
         LOpcodesGenerated.inputs += ['jit/x64/LIR-x64.h']
         UNIFIED_SOURCES += [
             'jit/x64/Assembler-x64.cpp',
             'jit/x64/Bailouts-x64.cpp',
-            'jit/x64/BaselineCompiler-x64.cpp',
             'jit/x64/CodeGenerator-x64.cpp',
             'jit/x64/Lowering-x64.cpp',
             'jit/x64/MacroAssembler-x64.cpp',
             'jit/x64/Trampoline-x64.cpp',
         ]
     else:
         LOpcodesGenerated.inputs += ['jit/x86/LIR-x86.h']
         UNIFIED_SOURCES += [
             'jit/x86/Assembler-x86.cpp',
             'jit/x86/Bailouts-x86.cpp',
-            'jit/x86/BaselineCompiler-x86.cpp',
             'jit/x86/CodeGenerator-x86.cpp',
             'jit/x86/Lowering-x86.cpp',
             'jit/x86/MacroAssembler-x86.cpp',
             'jit/x86/Trampoline-x86.cpp',
         ]
 elif CONFIG['JS_CODEGEN_ARM']:
     LOpcodesGenerated.inputs += ['jit/arm/LIR-arm.h']
     UNIFIED_SOURCES += [
         'jit/arm/Architecture-arm.cpp',
         'jit/arm/Assembler-arm.cpp',
         'jit/arm/Bailouts-arm.cpp',
-        'jit/arm/BaselineCompiler-arm.cpp',
         'jit/arm/CodeGenerator-arm.cpp',
         'jit/arm/disasm/Constants-arm.cpp',
         'jit/arm/disasm/Disasm-arm.cpp',
         'jit/arm/Lowering-arm.cpp',
         'jit/arm/MacroAssembler-arm.cpp',
         'jit/arm/MoveEmitter-arm.cpp',
         'jit/arm/Trampoline-arm.cpp',
     ]
@@ -600,46 +595,43 @@ elif CONFIG['JS_CODEGEN_ARM64']:
             'jit/arm64/vixl/Simulator-vixl.cpp'
         ]
 elif CONFIG['JS_CODEGEN_MIPS32'] or CONFIG['JS_CODEGEN_MIPS64']:
     LOpcodesGenerated.inputs += ['jit/mips-shared/LIR-mips-shared.h']
     UNIFIED_SOURCES += [
         'jit/mips-shared/Architecture-mips-shared.cpp',
         'jit/mips-shared/Assembler-mips-shared.cpp',
         'jit/mips-shared/Bailouts-mips-shared.cpp',
-        'jit/mips-shared/BaselineCompiler-mips-shared.cpp',
         'jit/mips-shared/CodeGenerator-mips-shared.cpp',
         'jit/mips-shared/Lowering-mips-shared.cpp',
         'jit/mips-shared/MacroAssembler-mips-shared.cpp',
         'jit/mips-shared/MoveEmitter-mips-shared.cpp',
     ]
     if CONFIG['JS_CODEGEN_MIPS32']:
         LOpcodesGenerated.inputs += ['jit/mips32/LIR-mips32.h']
         UNIFIED_SOURCES += [
             'jit/mips32/Architecture-mips32.cpp',
             'jit/mips32/Assembler-mips32.cpp',
             'jit/mips32/Bailouts-mips32.cpp',
-            'jit/mips32/BaselineCompiler-mips32.cpp',
             'jit/mips32/CodeGenerator-mips32.cpp',
             'jit/mips32/Lowering-mips32.cpp',
             'jit/mips32/MacroAssembler-mips32.cpp',
             'jit/mips32/MoveEmitter-mips32.cpp',
             'jit/mips32/Trampoline-mips32.cpp',
         ]
         if CONFIG['JS_SIMULATOR_MIPS32']:
             UNIFIED_SOURCES += [
                 'jit/mips32/Simulator-mips32.cpp'
             ]
     elif CONFIG['JS_CODEGEN_MIPS64']:
         LOpcodesGenerated.inputs += ['jit/mips64/LIR-mips64.h']
         UNIFIED_SOURCES += [
             'jit/mips64/Architecture-mips64.cpp',
             'jit/mips64/Assembler-mips64.cpp',
             'jit/mips64/Bailouts-mips64.cpp',
-            'jit/mips64/BaselineCompiler-mips64.cpp',
             'jit/mips64/CodeGenerator-mips64.cpp',
             'jit/mips64/Lowering-mips64.cpp',
             'jit/mips64/MacroAssembler-mips64.cpp',
             'jit/mips64/MoveEmitter-mips64.cpp',
             'jit/mips64/Trampoline-mips64.cpp',
         ]
         if CONFIG['JS_SIMULATOR_MIPS64']:
             UNIFIED_SOURCES += [
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1024,19 +1024,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
                 OnPopHandler* handler = frameobj->onPopHandler();
 
                 Maybe<AutoRealm> ar;
                 ar.emplace(cx, dbg->object);
 
                 RootedValue wrappedValue(cx, value);
                 RootedValue completion(cx);
                 if (!dbg->wrapDebuggeeValue(cx, &wrappedValue)) {
-                {
                     resumeMode = dbg->reportUncaughtException(ar);
-                }
                     break;
                 }
 
                 // Call the onPop handler.
                 ResumeMode nextResumeMode = resumeMode;
                 RootedValue nextValue(cx, wrappedValue);
                 bool success;
                 {
--- a/python/mozbuild/mozbuild/backend/cargo_build_defs.py
+++ b/python/mozbuild/mozbuild/backend/cargo_build_defs.py
@@ -90,16 +90,33 @@ cargo_extra_outputs = {
         'shorthands/table.rs',
         'shorthands/text.rs',
         'shorthands/ui.rs',
         'shorthands/xul.rs',
     ],
     'webrender': [
         'shaders.rs',
     ],
+    'geckodriver': [
+        'build-info.rs',
+    ],
+    'crc': [
+        'crc64_constants.rs',
+        'crc32_constants.rs',
+    ],
+    'bzip2-sys': [
+        'bzip2-1.0.6/blocksort.o',
+        'bzip2-1.0.6/bzlib.o',
+        'bzip2-1.0.6/compress.o',
+        'bzip2-1.0.6/crctable.o',
+        'bzip2-1.0.6/decompress.o',
+        'bzip2-1.0.6/huffman.o',
+        'bzip2-1.0.6/randtable.o',
+        'libbz2.a',
+    ],
     'cranelift-codegen': [
         'binemit-arm32.rs',
         'binemit-arm64.rs',
         'binemit-riscv.rs',
         'binemit-x86.rs',
         'encoding-arm32.rs',
         'encoding-arm64.rs',
         'encoding-riscv.rs',
--- a/python/mozbuild/mozbuild/backend/tup.py
+++ b/python/mozbuild/mozbuild/backend/tup.py
@@ -46,16 +46,17 @@ from ..frontend.data import (
     PerSourceFlag,
     Program,
     SimpleProgram,
     HostLibrary,
     HostRustLibrary,
     HostProgram,
     HostSimpleProgram,
     RustLibrary,
+    RustProgram,
     SharedLibrary,
     Sources,
     StaticLibrary,
     VariablePassthru,
 )
 from ..util import (
     FileAvoidWrite,
     expand_variables,
@@ -652,17 +653,17 @@ class TupBackend(CommonBackend):
         elif isinstance(obj, ComputedFlags):
             self._process_computed_flags(obj, backend_file)
         elif isinstance(obj, (Sources, GeneratedSources)):
             backend_file.sources[obj.canonical_suffix].extend(obj.files)
         elif isinstance(obj, HostSources):
             backend_file.host_sources[obj.canonical_suffix].extend(obj.files)
         elif isinstance(obj, VariablePassthru):
             backend_file.variables = obj.variables
-        elif isinstance(obj, RustLibrary):
+        elif isinstance(obj, (RustLibrary, RustProgram)):
             self._gen_rust_rules(obj, backend_file)
         elif isinstance(obj, StaticLibrary):
             backend_file.static_lib = obj
         elif isinstance(obj, SharedLibrary):
             backend_file.shared_lib = obj
         elif isinstance(obj, (HostProgram, HostSimpleProgram)):
             backend_file.host_programs.append(obj)
         elif isinstance(obj, HostLibrary):
@@ -747,35 +748,47 @@ class TupBackend(CommonBackend):
                     "ancestor directory of your objdir and srcdir, possibly "
                     "%s. To reduce file scanning overhead, this directory "
                     "should contain the fewest files possible that are not "
                     "necessary for this build." % tup_base_dir)
             tup = self.environment.substs.get('TUP', 'tup')
             self._cmd.run_process(cwd=tup_base_dir, log_name='tup', args=[tup, 'init', '--no-sync'])
 
     def _get_cargo_flags(self, obj):
+
+        def output_flags(obj):
+            if isinstance(obj, RustLibrary):
+                return ['--lib']
+            if isinstance(obj, RustProgram):
+                return ['--bin', obj.name]
+
+        def feature_flags(obj):
+            if isinstance(obj, RustLibrary) and obj.features:
+                return ['--features', ' '.join(obj.features)]
+            return []
+
         cargo_flags = ['--build-plan', '-Z', 'unstable-options']
         if not self.environment.substs.get('MOZ_DEBUG_RUST'):
             cargo_flags += ['--release']
         cargo_flags += [
             '--frozen',
             '--manifest-path', mozpath.join(obj.srcdir, 'Cargo.toml'),
-            '--lib',
+        ] + output_flags(obj) + [
             '--target=%s' % self.environment.substs['RUST_TARGET'],
-        ]
-        if obj.features:
-            cargo_flags += [
-                '--features', ' '.join(obj.features)
-            ]
+        ] + feature_flags(obj)
+
         return cargo_flags
 
     def _get_cargo_env(self, lib, backend_file):
+        cargo_target_dir = mozpath.normpath(lib.objdir)
+        if isinstance(lib, RustLibrary):
+            cargo_target_dir = mozpath.normpath(mozpath.join(cargo_target_dir,
+                                                             lib.target_dir))
         env = {
-            'CARGO_TARGET_DIR': mozpath.normpath(mozpath.join(lib.objdir,
-                                                              lib.target_dir)),
+            'CARGO_TARGET_DIR': cargo_target_dir,
             'RUSTC': self.environment.substs['RUSTC'],
             'MOZ_SRC': self.environment.topsrcdir,
             'MOZ_DIST': self.environment.substs['DIST'],
             'LIBCLANG_PATH': self.environment.substs['MOZ_LIBCLANG_PATH'],
             'CLANG_PATH': self.environment.substs['MOZ_CLANG_PATH'],
             'PKG_CONFIG_ALLOW_CROSS': '1',
             'RUST_BACKTRACE': 'full',
             'MOZ_TOPOBJDIR': self.environment.topobjdir,
@@ -805,17 +818,17 @@ class TupBackend(CommonBackend):
         if os.environ.get('MOZ_AUTOMATION'):
             # Build scripts generally read environment variables that are set
             # by cargo, however, some may rely on MOZ_AUTOMATION. We may need
             # to audit for others as well.
             env['MOZ_AUTOMATION'] = os.environ['MOZ_AUTOMATION']
 
         return env
 
-    def _gen_cargo_rules(self, backend_file, build_plan, cargo_env, output_group):
+    def _gen_cargo_rules(self, obj,  build_plan, cargo_env, output_group):
         invocations = build_plan['invocations']
         processed = set()
 
         rust_build_home = mozpath.join(self.environment.topobjdir,
                                        'toolkit/library/rust')
 
         def display_name(invocation):
             output_str = ''
@@ -960,16 +973,24 @@ class TupBackend(CommonBackend):
                     output_group=output_group,
                     extra_inputs=[self._installed_files],
                     display='%s %s' % (header, display_name(invocation)),
                     check_unchanged=check_unchanged,
                 )
 
                 for dst, link in invocation['links'].iteritems():
                     rust_backend_file.symlink_rule(link, dst, output_group)
+                    if invocation['target_kind'][0] == 'bin' and link in outputs:
+                        # Additionally link the program to its final target.
+                        rust_backend_file.symlink_rule(link,
+                                                       mozpath.join(self.environment.topobjdir,
+                                                                    obj.install_target,
+                                                                    obj.name),
+                                                       output_group)
+
 
         for val in enumerate(invocations):
             _process(*val)
 
 
     def _gen_rust_rules(self, obj, backend_file):
         cargo_flags = self._get_cargo_flags(obj)
         cargo_env = self._get_cargo_env(obj, backend_file)
@@ -984,19 +1005,20 @@ class TupBackend(CommonBackend):
             ensure_exit_code=False,
             explicit_env=cargo_env)
         if cargo_status:
             raise Exception("cargo --build-plan failed with output:\n%s" %
                             '\n'.join(output_lines))
 
         cargo_plan = json.loads(''.join(output_lines))
 
-        self._gen_cargo_rules(backend_file, cargo_plan, cargo_env,
-                              self._rust_output_group(obj.output_category) or
-                              self._rust_libs)
+        output_group = self._rust_libs
+        if isinstance(obj, RustLibrary) and obj.output_category:
+            output_group = self._rust_output_group(obj.output_category)
+        self._gen_cargo_rules(obj, cargo_plan, cargo_env, output_group)
         self.backend_input_files |= set(cargo_plan['inputs'])
 
 
     def _process_generated_file(self, backend_file, obj):
         if obj.script and obj.method:
             backend_file.export_shell()
             cmd = self._py_action('file_generate')
             if obj.localized:
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -10,16 +10,17 @@
 
 #include "NSSCertDBTrustDomain.h"
 #include "NSSErrorsService.h"
 #include "PSMRunnable.h"
 #include "SSLServerCertVerification.h"
 #include "ScopedNSSTypes.h"
 #include "SharedSSLState.h"
 #include "keyhi.h"
+#include "mozilla/Base64.h"
 #include "mozilla/Casting.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Logging.h"
 #include "mozilla/Move.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsArray.h"
 #include "nsArrayUtils.h"
@@ -1015,26 +1016,32 @@ nsNSSSocketInfo::GetEsniTxt(nsACString &
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::SetEsniTxt(const nsACString & aEsniTxt)
 {
   mEsniTxt = aEsniTxt;
 
   if (mEsniTxt.Length()) {
-    fprintf(stderr,"\n\nTODO - SSL_EnableSNI() [%s] (%d bytes)\n",
-            mEsniTxt.get(), mEsniTxt.Length());
-
-#if 0
+    nsAutoCString esniBin;
+    if (NS_OK != Base64Decode(mEsniTxt, esniBin)) {
+      MOZ_LOG(gPIPNSSLog, LogLevel::Error,
+              ("[%p] Invalid ESNIKeys record. Couldn't base64 decode\n",
+               (void*) mFd));
+      return NS_OK;
+    }
+
     if (SECSuccess != SSL_EnableESNI(mFd,
-                                     reinterpret_cast<const PRUint8*>(mEsniTxt.get()),
-                                     mEsniTxt.Length(), "dummy.invalid")) {
-      return NS_ERROR_FAILURE;
+                                     reinterpret_cast<const PRUint8*>(esniBin.get()),
+                                     esniBin.Length(), nullptr)) {
+      MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("[%p] Invalid ESNIKeys record %s\n",
+                                            (void*) mFd,
+                                            PR_ErrorToName(PR_GetError())));
+      return NS_OK;
     }
-#endif
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSSocketInfo::GetServerRootCertIsBuiltInRoot(bool *aIsBuiltInRoot)
 {
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -1396,17 +1396,16 @@ impl ToMarionette for GetParameters {
     }
 }
 
 impl ToMarionette for JavascriptCommandParameters {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         let mut data = serde_json::to_value(self)?.as_object().unwrap().clone();
         data.insert("newSandbox".to_string(), Value::Bool(false));
         data.insert("specialPowers".to_string(), Value::Bool(false));
-        data.insert("scriptTimeout".to_string(), Value::Null);
         Ok(data)
     }
 }
 
 impl ToMarionette for LocatorParameters {
     fn to_marionette(&self) -> WebDriverResult<Map<String, Value>> {
         Ok(try_opt!(
             serde_json::to_value(self)?.as_object(),
--- a/testing/webdriver/src/command.rs
+++ b/testing/webdriver/src/command.rs
@@ -541,23 +541,39 @@ where
 /// In some user agents the operating system’s window dimensions, including
 /// decorations, are provided by the proprietary `window.outerWidth` and
 /// `window.outerHeight` DOM properties.
 ///
 /// [`screenX`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screenx
 /// [`screenY`]: https://w3c.github.io/webdriver/webdriver-spec.html#dfn-screeny
 #[derive(Debug, PartialEq, Serialize, Deserialize)]
 pub struct WindowRectParameters {
-    #[serde(default, deserialize_with = "deserialize_to_i32")]
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        deserialize_with = "deserialize_to_i32"
+    )]
     pub x: Option<i32>,
-    #[serde(default, deserialize_with = "deserialize_to_i32")]
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        deserialize_with = "deserialize_to_i32"
+    )]
     pub y: Option<i32>,
-    #[serde(default, deserialize_with = "deserialize_to_positive_i32")]
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        deserialize_with = "deserialize_to_positive_i32"
+    )]
     pub width: Option<i32>,
-    #[serde(default, deserialize_with = "deserialize_to_positive_i32")]
+    #[serde(
+        default,
+        skip_serializing_if = "Option::is_none",
+        deserialize_with = "deserialize_to_positive_i32"
+    )]
     pub height: Option<i32>,
 }
 
 fn deserialize_to_i32<'de, D>(deserializer: D) -> Result<Option<i32>, D::Error>
 where
     D: Deserializer<'de>,
 {
     let opt = Option::deserialize(deserializer)?.map(|value: f64| value as i64);
--- a/toolkit/components/places/nsMaybeWeakPtr.h
+++ b/toolkit/components/places/nsMaybeWeakPtr.h
@@ -14,19 +14,29 @@
 
 // nsMaybeWeakPtr is a helper object to hold a strong-or-weak reference
 // to the template class.  It's pretty minimal, but sufficient.
 
 template<class T>
 class nsMaybeWeakPtr
 {
 public:
-  MOZ_IMPLICIT nsMaybeWeakPtr(nsISupports* aRef) : mPtr(aRef) {}
+  nsMaybeWeakPtr() = default;
+  MOZ_IMPLICIT nsMaybeWeakPtr(T* aRef) : mPtr(aRef) {}
   MOZ_IMPLICIT nsMaybeWeakPtr(const nsCOMPtr<nsIWeakReference>& aRef) : mPtr(aRef) {}
-  MOZ_IMPLICIT nsMaybeWeakPtr(const nsCOMPtr<T>& aRef) : mPtr(aRef) {}
+
+  nsMaybeWeakPtr<T>& operator=(T* aRef) {
+    mPtr = aRef;
+    return *this;
+  }
+
+  nsMaybeWeakPtr<T>& operator=(const nsCOMPtr<nsIWeakReference>& aRef) {
+    mPtr = aRef;
+    return *this;
+  }
 
   bool operator==(const nsMaybeWeakPtr<T> &other) const {
     return mPtr == other.mPtr;
   }
 
   nsISupports* GetRawValue() const { return mPtr.get(); }
 
   const nsCOMPtr<T> GetValue() const;
@@ -42,24 +52,25 @@ private:
 template<class T>
 class nsMaybeWeakPtrArray : public nsTArray<nsMaybeWeakPtr<T>>
 {
   typedef nsTArray<nsMaybeWeakPtr<T>> MaybeWeakArray;
 
 public:
   nsresult AppendWeakElement(T* aElement, bool aOwnsWeak)
   {
-    nsCOMPtr<nsISupports> ref;
+    nsMaybeWeakPtr<T> ref;
+
     if (aOwnsWeak) {
       ref = do_GetWeakReference(aElement);
     } else {
       ref = aElement;
     }
 
-    if (MaybeWeakArray::Contains(ref.get())) {
+    if (MaybeWeakArray::Contains(ref)) {
       return NS_ERROR_INVALID_ARG;
     }
     if (!MaybeWeakArray::AppendElement(ref)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
     return NS_OK;
   }
 
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -730,16 +730,19 @@
           this.input.controller.handleEnter(true, aEvent);
         ]]></body>
       </method>
 
       <property name="selectedIndex"
                 onget="return this.richlistbox.selectedIndex;">
         <setter>
           <![CDATA[
+          if (val != this.richlistbox.selectedIndex) {
+            this._previousSelectedIndex = this.richlistbox.selectedIndex;
+          }
           this.richlistbox.selectedIndex = val;
           // Since ensureElementIsVisible may cause an expensive Layout flush,
           // invoke it only if there may be a scrollbar, so if we could fetch
           // more results than we can show at once.
           // maxResults is the maximum number of fetched results, maxRows is the
           // maximum number of rows we show at once, without a scrollbar.
           if (this.mPopupOpen && this.maxResults > this.maxRows) {
             // when clearing the selection (val == -1, so selectedItem will be
@@ -747,16 +750,18 @@
             this.richlistbox.ensureElementIsVisible(
               this.richlistbox.selectedItem || this.richlistbox.firstElementChild);
           }
           return val;
         ]]>
         </setter>
       </property>
 
+      <field name="_previousSelectedIndex">-1</field>
+
       <method name="onSearchBegin">
         <body><![CDATA[
           this.richlistbox.mousedOverIndex = -1;
 
           if (typeof this._onSearchBegin == "function") {
             this._onSearchBegin();
           }
         ]]></body>
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -815,42 +815,51 @@ nsBaseWidget::PerformFullscreenTransitio
 // Put the window into full-screen mode
 //
 //-------------------------------------------------------------------------
 void
 nsBaseWidget::InfallibleMakeFullScreen(bool aFullScreen, nsIScreen* aScreen)
 {
   HideWindowChrome(aFullScreen);
 
+  nsCOMPtr<nsIScreen> screen = aScreen;
+  if (!screen) {
+    screen = GetWidgetScreen();
+    if (!screen) {
+      return;
+    }
+  }
+
   if (aFullScreen) {
     if (!mOriginalBounds) {
       mOriginalBounds = new LayoutDeviceIntRect();
     }
     *mOriginalBounds = GetScreenBounds();
 
     // Move to top-left corner of screen and size to the screen dimensions
-    nsCOMPtr<nsIScreen> screen = aScreen;
-    if (!screen) {
-      screen = GetWidgetScreen();
-    }
-    if (screen) {
-      int32_t left, top, width, height;
-      if (NS_SUCCEEDED(screen->GetRectDisplayPix(&left, &top, &width, &height))) {
-        Resize(left, top, width, height, true);
-      }
+    int32_t left, top, width, height;
+    if (NS_SUCCEEDED(screen->GetRectDisplayPix(&left, &top, &width, &height))) {
+      Resize(left, top, width, height, true);
     }
   } else if (mOriginalBounds) {
+    int32_t left, top, width, height;
+    if (NS_FAILED(screen->GetAvailRectDisplayPix(&left, &top,
+                                                 &width, &height))) {
+      return;
+    }
+    gfx::Rect screenRect(left, top, width, height);
+    gfx::Rect rect;
     if (BoundsUseDesktopPixels()) {
       DesktopRect deskRect = *mOriginalBounds / GetDesktopToDeviceScale();
-      Resize(deskRect.X(), deskRect.Y(),
-	     deskRect.Width(), deskRect.Height(), true);
+      rect = deskRect.ToUnknownRect();
     } else {
-      Resize(mOriginalBounds->X(), mOriginalBounds->Y(),
-	     mOriginalBounds->Width(), mOriginalBounds->Height(), true);
+      rect = gfx::Rect(mOriginalBounds->ToUnknownRect());
     }
+    rect = rect.MoveInsideAndClamp(screenRect);
+    Resize(rect.x, rect.y, rect.width, rect.height, true);
   }
 }
 
 nsresult
 nsBaseWidget::MakeFullScreen(bool aFullScreen, nsIScreen* aScreen)
 {
   InfallibleMakeFullScreen(aFullScreen, aScreen);
   return NS_OK;