Merge mozilla-inbound to mozilla-central. a=merge
authorCosmin Sabou <csabou@mozilla.com>
Tue, 11 Sep 2018 13:06:37 +0300
changeset 491386 423bdf7a802b
parent 491340 cb28a4b17303 (current diff)
parent 491385 43a95f0f4725 (diff)
child 491392 bae0f4bb40a0
child 491419 77ef88dd1272
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
423bdf7a802b / 64.0a1 / 20180911101647 / files
nightly linux64
423bdf7a802b / 64.0a1 / 20180911101647 / files
nightly mac
423bdf7a802b / 64.0a1 / 20180911101647 / files
nightly win32
423bdf7a802b / 64.0a1 / 20180911101647 / files
nightly win64
423bdf7a802b / 64.0a1 / 20180911101647 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound to mozilla-central. a=merge
browser/base/content/browser.js
devtools/client/inspector/grids/test/browser_grids_grid-list-toggle-single-grid.js
dom/interfaces/payments/nsIPaymentActionRequest.idl
dom/payments/PaymentActionRequest.cpp
dom/payments/PaymentActionRequest.h
layout/tools/reftest/bootstrap.js
layout/tools/reftest/install.rdf
testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini
toolkit/components/search/nsSearchService.js
--- a/browser/base/content/browser-captivePortal.js
+++ b/browser/base/content/browser-captivePortal.js
@@ -53,117 +53,103 @@ var CaptivePortalWatcher = {
     Services.obs.addObserver(this, "captive-portal-login-abort");
     Services.obs.addObserver(this, "captive-portal-login-success");
 
     if (cps.state == cps.LOCKED_PORTAL) {
       // A captive portal has already been detected.
       this._captivePortalDetected();
 
       // Automatically open a captive portal tab if there's no other browser window.
-      let windows = Services.wm.getEnumerator("navigator:browser");
-      if (windows.getNext() == window && !windows.hasMoreElements()) {
+      if (BrowserWindowTracker.windowCount == 1) {
         this.ensureCaptivePortalTab();
       }
     } else if (cps.state == cps.UNKNOWN) {
       // We trigger a portal check after delayed startup to avoid doing a network
       // request before first paint.
       this._delayedRecheckPending = true;
-      Services.obs.addObserver(this, "browser-delayed-startup-finished");
     }
 
     XPCOMUtils.defineLazyPreferenceGetter(this, "PORTAL_RECHECK_DELAY_MS",
                                           "captivedetect.portalRecheckDelayMS", 500);
   },
 
   uninit() {
     Services.obs.removeObserver(this, "captive-portal-login");
     Services.obs.removeObserver(this, "captive-portal-login-abort");
     Services.obs.removeObserver(this, "captive-portal-login-success");
 
+    this._cancelDelayedCaptivePortal();
+  },
+
+  delayedStartup() {
     if (this._delayedRecheckPending) {
-      Services.obs.removeObserver(this, "browser-delayed-startup-finished");
-    }
-
-    if (this._delayedCaptivePortalDetectedInProgress) {
-      Services.obs.removeObserver(this, "xul-window-visible");
+      delete this._delayedRecheckPending;
+      cps.recheckCaptivePortal();
     }
   },
 
   observe(aSubject, aTopic, aData) {
     switch (aTopic) {
-      case "browser-delayed-startup-finished":
-        Services.obs.removeObserver(this, "browser-delayed-startup-finished");
-        delete this._delayedRecheckPending;
-        cps.recheckCaptivePortal();
-        break;
       case "captive-portal-login":
         this._captivePortalDetected();
         break;
       case "captive-portal-login-abort":
       case "captive-portal-login-success":
         this._captivePortalGone();
         break;
-      case "xul-window-visible":
-        this._delayedCaptivePortalDetected();
+      case "delayed-captive-portal-handled":
+        this._cancelDelayedCaptivePortal();
         break;
     }
   },
 
   _captivePortalDetected() {
     if (this._delayedCaptivePortalDetectedInProgress) {
       return;
     }
 
     let win = BrowserWindowTracker.getTopWindow();
+
     // Used by tests: ignore the main test window in order to enable testing of
     // the case where we have no open windows.
-    if (win && win.document.documentElement.getAttribute("ignorecaptiveportal")) {
+    if (win.document.documentElement.getAttribute("ignorecaptiveportal")) {
       win = null;
     }
 
     // If no browser window has focus, open and show the tab when we regain focus.
     // This is so that if a different application was focused, when the user
     // (re-)focuses a browser window, we open the tab immediately in that window
     // so they can log in before continuing to browse.
-    if (win != Services.ww.activeWindow) {
+    if (win != Services.focus.activeWindow) {
       this._delayedCaptivePortalDetectedInProgress = true;
-      Services.obs.addObserver(this, "xul-window-visible");
+      window.addEventListener("activate", this, { once: true });
+      Services.obs.addObserver(this, "delayed-captive-portal-handled");
     }
 
     this._showNotification();
   },
 
   /**
    * Called after we regain focus if we detect a portal while a browser window
    * doesn't have focus. Triggers a portal recheck to reaffirm state, and adds
    * the tab if needed after a short delay to allow the recheck to complete.
    */
   _delayedCaptivePortalDetected() {
     if (!this._delayedCaptivePortalDetectedInProgress) {
       return;
     }
 
-    let win = BrowserWindowTracker.getTopWindow();
     // Used by tests: ignore the main test window in order to enable testing of
     // the case where we have no open windows.
-    if (win && win.document.documentElement.getAttribute("ignorecaptiveportal")) {
-      win = null;
+    if (window.document.documentElement.getAttribute("ignorecaptiveportal")) {
+      return;
     }
 
-    if (win != Services.ww.activeWindow) {
-      // The window that got focused was not a browser window.
-      return;
-    }
-    Services.obs.removeObserver(this, "xul-window-visible");
-    this._delayedCaptivePortalDetectedInProgress = false;
+    Services.obs.notifyObservers(null, "delayed-captive-portal-handled");
 
-    if (win != window) {
-      // Some other browser window got focus, we don't have to do anything.
-      return;
-    }
     // Trigger a portal recheck. The user may have logged into the portal via
     // another client, or changed networks.
     cps.recheckCaptivePortal();
     this._waitingForRecheck = true;
     let requestTime = Date.now();
 
     let self = this;
     Services.obs.addObserver(function observer() {
@@ -180,41 +166,52 @@ var CaptivePortalWatcher = {
         // the browser window was focused) was small enough that we can add and
         // focus a tab with the login page with no noticeable delay.
         self.ensureCaptivePortalTab();
       }
     }, "captive-portal-check-complete");
   },
 
   _captivePortalGone() {
+    this._cancelDelayedCaptivePortal();
+    this._removeNotification();
+  },
+
+  _cancelDelayedCaptivePortal() {
     if (this._delayedCaptivePortalDetectedInProgress) {
-      Services.obs.removeObserver(this, "xul-window-visible");
       this._delayedCaptivePortalDetectedInProgress = false;
+      Services.obs.removeObserver(this, "delayed-captive-portal-handled");
+      window.removeEventListener("activate", this);
     }
-
-    this._removeNotification();
   },
 
   handleEvent(aEvent) {
-    if (aEvent.type != "TabSelect" || !this._captivePortalTab || !this._captivePortalNotification) {
-      return;
-    }
+    switch (aEvent.type) {
+      case "activate":
+        this._delayedCaptivePortalDetected();
+        break;
+      case "TabSelect":
+        if (!this._captivePortalTab || !this._captivePortalNotification) {
+          break;
+        }
 
-    let tab = this._captivePortalTab.get();
-    let n = this._captivePortalNotification;
-    if (!tab || !n) {
-      return;
-    }
+        let tab = this._captivePortalTab.get();
+        let n = this._captivePortalNotification;
+        if (!tab || !n) {
+          break;
+        }
 
-    let doc = tab.ownerDocument;
-    let button = n.querySelector("button.notification-button");
-    if (doc.defaultView.gBrowser.selectedTab == tab) {
-      button.style.visibility = "hidden";
-    } else {
-      button.style.visibility = "visible";
+        let doc = tab.ownerDocument;
+        let button = n.querySelector("button.notification-button");
+        if (doc.defaultView.gBrowser.selectedTab == tab) {
+          button.style.visibility = "hidden";
+        } else {
+          button.style.visibility = "visible";
+        }
+        break;
     }
   },
 
   _showNotification() {
     let buttons = [
       {
         label: this._browserBundle.GetStringFromName("captivePortal.showLoginPage2"),
         callback: () => {
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1554,16 +1554,18 @@ var gBrowserInit = {
     gBrowser.tabContainer.addEventListener("TabSelect", function() {
       for (let panel of document.querySelectorAll("panel[tabspecific='true']")) {
         if (panel.state == "open") {
           panel.hidePopup();
         }
       }
     });
 
+    CaptivePortalWatcher.delayedStartup();
+
     this.delayedStartupFinished = true;
 
     _resolveDelayedStartup();
 
     SessionStore.promiseAllWindowsRestored.then(() => {
       this._schedulePerWindowIdleTasks();
       document.documentElement.setAttribute("sessionrestored", "true");
     });
--- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js
+++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher.js
@@ -19,17 +19,17 @@ let testCasesForBothSuccessAndAbort = [
    * opened, and closed automatically when the success event is fired.
    */
   async function test_detectedWithNoBrowserWindow_Open(aSuccess) {
     await portalDetected();
     let win = await focusWindowAndWaitForPortalUI();
     await freePortal(aSuccess);
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    await closeWindowAndWaitForXulWindowVisible(win);
+    await closeWindowAndWaitForWindowActivate(win);
   },
 
   /**
    * A portal is detected when multiple browser windows are open but none
    * have focus. A browser window is focused, then the portal is freed.
    * The portal tab should be added and focused when the window is
    * focused, and closed automatically when the success event is fired.
    * The captive portal notification should be shown in all windows upon
@@ -52,17 +52,17 @@ let testCasesForBothSuccessAndAbort = [
     await focusWindowAndWaitForPortalUI(false, win2);
 
     await freePortal(aSuccess);
 
     ensureNoPortalNotification(win1);
     ensureNoPortalTab(win2);
     ensureNoPortalNotification(win2);
 
-    await closeWindowAndWaitForXulWindowVisible(win2);
+    await closeWindowAndWaitForWindowActivate(win2);
     // No need to wait for xul-window-visible: after win2 is closed, focus
     // is restored to the default window and win1 remains in the background.
     await BrowserTestUtils.closeWindow(win1);
   },
 
   /**
    * A portal is detected when there's no browser window, then a browser
    * window is opened, then the portal is freed.
@@ -72,17 +72,17 @@ let testCasesForBothSuccessAndAbort = [
    * opened, and closed automatically when the success event is fired.
    */
   async function test_detectedWithNoBrowserWindow_LongRecheck(aSuccess) {
     await portalDetected();
     let win = await focusWindowAndWaitForPortalUI(true);
     await freePortal(aSuccess);
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    await closeWindowAndWaitForXulWindowVisible(win);
+    await closeWindowAndWaitForWindowActivate(win);
   },
 
   /**
    * A portal is detected when there's no browser window, and the
    * portal is freed before a browser window is opened. No portal
    * UI should be shown when a browser window is opened.
    */
   async function test_detectedWithNoBrowserWindow_GoneBeforeOpen(aSuccess) {
@@ -90,17 +90,17 @@ let testCasesForBothSuccessAndAbort = [
     await freePortal(aSuccess);
     let win = await openWindowAndWaitForFocus();
     // Wait for a while to make sure no UI is shown.
     await new Promise(resolve => {
       setTimeout(resolve, 1000);
     });
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    await closeWindowAndWaitForXulWindowVisible(win);
+    await closeWindowAndWaitForWindowActivate(win);
   },
 
   /**
    * A portal is detected when a browser window has focus. No portal tab should
    * be opened. A notification bar should be displayed in all browser windows.
    */
   async function test_detectedWithFocus(aSuccess) {
     let win1 = await openWindowAndWaitForFocus();
@@ -108,17 +108,17 @@ let testCasesForBothSuccessAndAbort = [
     await portalDetected();
     ensureNoPortalTab(win1);
     ensureNoPortalTab(win2);
     ensurePortalNotification(win1);
     ensurePortalNotification(win2);
     await freePortal(aSuccess);
     ensureNoPortalNotification(win1);
     ensureNoPortalNotification(win2);
-    await closeWindowAndWaitForXulWindowVisible(win2);
-    await closeWindowAndWaitForXulWindowVisible(win1);
+    await closeWindowAndWaitForWindowActivate(win2);
+    await closeWindowAndWaitForWindowActivate(win1);
   },
 ];
 
 for (let testcase of testCasesForBothSuccessAndAbort) {
   add_task(testcase.bind(null, true));
   add_task(testcase.bind(null, false));
 }
--- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
+++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
@@ -16,17 +16,17 @@ let testcases = [
     let browser = win.gBrowser.selectedTab.linkedBrowser;
     let loadPromise =
       BrowserTestUtils.browserLoaded(browser, false, CANONICAL_URL_REDIRECTED);
     BrowserTestUtils.loadURI(browser, CANONICAL_URL_REDIRECTED);
     await loadPromise;
     await freePortal(true);
     ensurePortalTab(win);
     ensureNoPortalNotification(win);
-    await closeWindowAndWaitForXulWindowVisible(win);
+    await closeWindowAndWaitForWindowActivate(win);
   },
 
   /**
    * Test the various expected behaviors of the "Show Login Page" button
    * in the captive portal notification. The button should be visible for
    * all tabs except the captive portal tab, and when clicked, should
    * ensure a captive portal tab is open and select it.
    */
@@ -77,15 +77,15 @@ let testcases = [
     win.gBrowser.selectedTab = anotherTab;
     testShowLoginPageButtonVisibility(notification, "visible");
     tab = await clickButtonAndExpectNewPortalTab();
 
     BrowserTestUtils.removeTab(anotherTab);
     await freePortal(true);
     ensureNoPortalTab(win);
     ensureNoPortalNotification(win);
-    await closeWindowAndWaitForXulWindowVisible(win);
+    await closeWindowAndWaitForWindowActivate(win);
   },
 ];
 
 for (let testcase of testcases) {
   add_task(testcase);
 }
--- a/browser/base/content/test/captivePortal/head.js
+++ b/browser/base/content/test/captivePortal/head.js
@@ -55,17 +55,19 @@ async function focusWindowAndWaitForPort
   // so use a delay threshold of -1 to simulate a long recheck (so that any
   // amount of time is considered excessive), and a very large threshold to
   // simulate a short recheck.
   Services.prefs.setIntPref("captivedetect.portalRecheckDelayMS", aLongRecheck ? -1 : 1000000);
 
   if (!win) {
     win = await BrowserTestUtils.openNewBrowserWindow();
   }
-  await SimpleTest.promiseFocus(win);
+  let windowActivePromise = waitForBrowserWindowActive(win);
+  win.focus();
+  await windowActivePromise;
 
   // After a new window is opened, CaptivePortalWatcher asks for a recheck, and
   // waits for it to complete. We need to manually tell it a recheck completed.
   await BrowserTestUtils.waitForCondition(() => {
     return win.CaptivePortalWatcher._waitingForRecheck;
   }, "Waiting for CaptivePortalWatcher to trigger a recheck.");
   Services.obs.notifyObservers(null, "captive-portal-check-complete");
 
@@ -123,40 +125,49 @@ function ensureNoPortalNotification(win)
   let notificationBox =
     win.document.getElementById("high-priority-global-notificationbox");
   is(notificationBox.getNotificationWithValue(PORTAL_NOTIFICATION_VALUE), null,
     "There should be no captive portal notification in the window.");
 }
 
 /**
  * Some tests open a new window and close it later. When the window is closed,
- * the original window opened by mochitest gains focus, generating a
- * xul-window-visible notification. If the next test also opens a new window
- * before this notification has a chance to fire, CaptivePortalWatcher picks
+ * the original window opened by mochitest gains focus, generating an
+ * activate event. If the next test also opens a new window
+ * before this event has a chance to fire, CaptivePortalWatcher picks
  * up the first one instead of the one from the new window. To avoid this
- * unfortunate intermittent timing issue, we wait for the notification from
+ * unfortunate intermittent timing issue, we wait for the event from
  * the original window every time we close a window that we opened.
  */
-function waitForXulWindowVisible() {
+function waitForBrowserWindowActive(win) {
   return new Promise(resolve => {
-    Services.obs.addObserver(function observe() {
-      Services.obs.removeObserver(observe, "xul-window-visible");
+    if (Services.focus.activeWindow == win) {
       resolve();
-    }, "xul-window-visible");
+    } else {
+      win.addEventListener("activate", () => {
+        resolve();
+      }, { once: true });
+    }
   });
 }
 
-async function closeWindowAndWaitForXulWindowVisible(win) {
-  let p = waitForXulWindowVisible();
+async function closeWindowAndWaitForWindowActivate(win) {
+  let activationPromises = [];
+  for (let w of BrowserWindowTracker.orderedWindows) {
+    if (w != win &&
+        !win.document.documentElement.getAttribute("ignorecaptiveportal")) {
+      activationPromises.push(waitForBrowserWindowActive(win));
+    }
+  }
   await BrowserTestUtils.closeWindow(win);
-  await p;
+  await Promise.race(activationPromises);
 }
 
 /**
  * BrowserTestUtils.openNewBrowserWindow() does not guarantee the newly
  * opened window has received focus when the promise resolves, so we
  * have to manually wait every time.
  */
 async function openWindowAndWaitForFocus() {
   let win = await BrowserTestUtils.openNewBrowserWindow();
-  await SimpleTest.promiseFocus(win);
+  await waitForBrowserWindowActive(win);
   return win;
 }
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -38,16 +38,17 @@ support-files =
 [browser_change_app_handler.js]
 skip-if = os != "win" # Windows-specific handler application selection dialog
 [browser_checkspelling.js]
 [browser_connection.js]
 [browser_connection_bug388287.js]
 [browser_connection_bug1445991.js]
 skip-if = (verify && debug && (os == 'linux' || os == 'mac'))
 [browser_connection_dnsoverhttps.js]
+skip-if = ccov # Skipping test on all ccov platforms
 [browser_contentblocking.js]
 [browser_cookies_exceptions.js]
 [browser_defaultbrowser_alwayscheck.js]
 [browser_healthreport.js]
 skip-if = true || !healthreport # Bug 1185403 for the "true"
 [browser_homepages_filter_aboutpreferences.js]
 [browser_homepages_use_bookmark.js]
 [browser_extension_controlled.js]
--- a/browser/components/sessionstore/test/browser_restore_reversed_z_order.js
+++ b/browser/components/sessionstore/test/browser_restore_reversed_z_order.js
@@ -61,17 +61,17 @@ add_task(async function test_z_indices_a
   Assert.equal(state.windows[2].zIndex, 1, "Window #3 should be the topmost window");
   Assert.equal(state.windows[3].zIndex, 4, "Minimized window should be the last window to restore");
 });
 
 add_task(async function test_windows_are_restored_in_reversed_z_order() {
   await promiseBrowserState(gBrowserState);
 
   let indexedTabLabels = [...gTestURLsMap.values()];
-  let tabsRestoredLabels = [...BrowserWindowTracker.orderedWindows].map(window => window.gBrowser.selectedTab.label);
+  let tabsRestoredLabels = BrowserWindowTracker.orderedWindows.map(window => window.gBrowser.selectedTab.label);
 
   Assert.equal(tabsRestoredLabels[0], indexedTabLabels[2], "First restored tab should be last used tab");
   Assert.equal(tabsRestoredLabels[1], indexedTabLabels[1], "Second restored tab is correct");
   Assert.equal(tabsRestoredLabels[2], indexedTabLabels[0], "Third restored tab is correct");
   Assert.equal(tabsRestoredLabels[3], indexedTabLabels[3], "Last restored tab should be a minimized window");
 
   await promiseAllButPrimaryWindowClosed();
 });
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,5 +1,5 @@
 This is the PDF.js project output, https://github.com/mozilla/pdf.js
 
-Current extension version is: 2.0.815
+Current extension version is: 2.0.843
 
-Taken from upstream commit: d6927376
+Taken from upstream commit: bf368f3a
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.815';
-var pdfjsBuild = 'd6927376';
+var pdfjsVersion = '2.0.843';
+var pdfjsBuild = 'bf368f3a';
 var pdfjsSharedUtil = __w_pdfjs_require__(1);
 var pdfjsDisplayAPI = __w_pdfjs_require__(7);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(19);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(20);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(8);
 var pdfjsDisplaySVG = __w_pdfjs_require__(21);
 let pdfjsDisplayWorkerOptions = __w_pdfjs_require__(13);
 let pdfjsDisplayAPICompatibility = __w_pdfjs_require__(10);
@@ -4221,17 +4221,17 @@ function _fetchDocument(worker, source, 
     return Promise.reject(new Error('Worker was destroyed'));
   }
   if (pdfDataRangeTransport) {
     source.length = pdfDataRangeTransport.length;
     source.initialData = pdfDataRangeTransport.initialData;
   }
   return worker.messageHandler.sendWithPromise('GetDocRequest', {
     docId,
-    apiVersion: '2.0.815',
+    apiVersion: '2.0.843',
     source: {
       data: source.data,
       url: source.url,
       password: source.password,
       disableAutoFetch: source.disableAutoFetch,
       rangeChunkSize: source.rangeChunkSize,
       length: source.length
     },
@@ -5548,18 +5548,18 @@ var InternalRenderTask = function Intern
         }
       });
     }
   };
   return InternalRenderTask;
 }();
 var version, build;
 {
-  exports.version = version = '2.0.815';
-  exports.build = build = 'd6927376';
+  exports.version = version = '2.0.843';
+  exports.build = build = 'bf368f3a';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports.setPDFNetworkStreamFactory = setPDFNetworkStreamFactory;
@@ -6349,17 +6349,17 @@ var CanvasGraphics = function CanvasGrap
     this.groupLevel = 0;
     this.smaskStack = [];
     this.smaskCounter = 0;
     this.tempSMask = null;
     this.cachedCanvases = new CachedCanvases(this.canvasFactory);
     if (canvasCtx) {
       addContextCurrentTransform(canvasCtx);
     }
-    this.cachedGetSinglePixelWidth = null;
+    this._cachedGetSinglePixelWidth = null;
   }
   function putBinaryImageData(ctx, imgData) {
     if (typeof ImageData !== 'undefined' && imgData instanceof ImageData) {
       ctx.putImageData(imgData, 0, 0);
       return;
     }
     var height = imgData.height,
         width = imgData.width;
@@ -6824,22 +6824,22 @@ var CanvasGraphics = function CanvasGrap
       }
       if (this.current.activeSMask !== null && (this.stateStack.length === 0 || this.stateStack[this.stateStack.length - 1].activeSMask !== this.current.activeSMask)) {
         this.endSMaskGroup();
       }
       if (this.stateStack.length !== 0) {
         this.current = this.stateStack.pop();
         this.ctx.restore();
         this.pendingClip = null;
-        this.cachedGetSinglePixelWidth = null;
+        this._cachedGetSinglePixelWidth = null;
       }
     },
     transform: function CanvasGraphics_transform(a, b, c, d, e, f) {
       this.ctx.transform(a, b, c, d, e, f);
-      this.cachedGetSinglePixelWidth = null;
+      this._cachedGetSinglePixelWidth = null;
     },
     constructPath: function CanvasGraphics_constructPath(ops, args) {
       var ctx = this.ctx;
       var current = this.current;
       var x = current.x,
           y = current.y;
       for (var i = 0, j = 0, ii = ops.length; i < ii; i++) {
         switch (ops[i] | 0) {
@@ -7172,17 +7172,17 @@ var CanvasGraphics = function CanvasGrap
       } else {
         ctx.scale(textHScale, 1);
       }
       var lineWidth = current.lineWidth;
       var scale = current.textMatrixScale;
       if (scale === 0 || lineWidth === 0) {
         var fillStrokeMode = current.textRenderingMode & _util.TextRenderingMode.FILL_STROKE_MASK;
         if (fillStrokeMode === _util.TextRenderingMode.STROKE || fillStrokeMode === _util.TextRenderingMode.FILL_STROKE) {
-          this.cachedGetSinglePixelWidth = null;
+          this._cachedGetSinglePixelWidth = null;
           lineWidth = this.getSinglePixelWidth() * MIN_WIDTH_FACTOR;
         }
       } else {
         lineWidth /= scale;
       }
       if (fontSizeScale !== 1.0) {
         ctx.scale(fontSizeScale, fontSizeScale);
         lineWidth /= fontSizeScale;
@@ -7264,17 +7264,17 @@ var CanvasGraphics = function CanvasGrap
       var textHScale = current.textHScale * fontDirection;
       var fontMatrix = current.fontMatrix || _util.FONT_IDENTITY_MATRIX;
       var glyphsLength = glyphs.length;
       var isTextInvisible = current.textRenderingMode === _util.TextRenderingMode.INVISIBLE;
       var i, glyph, width, spacingLength;
       if (isTextInvisible || fontSize === 0) {
         return;
       }
-      this.cachedGetSinglePixelWidth = null;
+      this._cachedGetSinglePixelWidth = null;
       ctx.save();
       ctx.transform.apply(ctx, current.textMatrix);
       ctx.translate(current.x, current.y);
       ctx.scale(textHScale, fontDirection);
       for (i = 0; i < glyphsLength; ++i) {
         glyph = glyphs[i];
         if ((0, _util.isNum)(glyph)) {
           spacingLength = spacingDir * glyph * fontSize / 1000;
@@ -7732,24 +7732,22 @@ var CanvasGraphics = function CanvasGrap
           ctx.clip('evenodd');
         } else {
           ctx.clip();
         }
         this.pendingClip = null;
       }
       ctx.beginPath();
     },
-    getSinglePixelWidth: function CanvasGraphics_getSinglePixelWidth(scale) {
-      if (this.cachedGetSinglePixelWidth === null) {
-        this.ctx.save();
-        var inverse = this.ctx.mozCurrentTransformInverse;
-        this.ctx.restore();
-        this.cachedGetSinglePixelWidth = Math.sqrt(Math.max(inverse[0] * inverse[0] + inverse[1] * inverse[1], inverse[2] * inverse[2] + inverse[3] * inverse[3]));
-      }
-      return this.cachedGetSinglePixelWidth;
+    getSinglePixelWidth(scale) {
+      if (this._cachedGetSinglePixelWidth === null) {
+        const inverse = this.ctx.mozCurrentTransformInverse;
+        this._cachedGetSinglePixelWidth = Math.sqrt(Math.max(inverse[0] * inverse[0] + inverse[1] * inverse[1], inverse[2] * inverse[2] + inverse[3] * inverse[3]));
+      }
+      return this._cachedGetSinglePixelWidth;
     },
     getCanvasPosition: function CanvasGraphics_getCanvasPosition(x, y) {
       var transform = this.ctx.mozCurrentTransform;
       return [transform[0] * x + transform[2] * y + transform[4], transform[1] * x + transform[3] * y + transform[5]];
     }
   };
   for (var op in _util.OPS) {
     CanvasGraphics.prototype[_util.OPS[op]] = CanvasGraphics.prototype[op];
@@ -10010,18 +10008,18 @@ var renderTextLayer = function renderTex
       let textDivProperties = this._textDivProperties.get(textDiv);
       if (textDivProperties.isWhitespace) {
         return;
       }
       let fontSize = textDiv.style.fontSize;
       let fontFamily = textDiv.style.fontFamily;
       if (fontSize !== this._layoutTextLastFontSize || fontFamily !== this._layoutTextLastFontFamily) {
         this._layoutTextCtx.font = fontSize + ' ' + fontFamily;
-        this._lastFontSize = fontSize;
-        this._lastFontFamily = fontFamily;
+        this._layoutTextLastFontSize = fontSize;
+        this._layoutTextLastFontFamily = fontFamily;
       }
       let width = this._layoutTextCtx.measureText(textDiv.textContent).width;
       let transform = '';
       if (textDivProperties.canvasWidth !== 0 && width > 0) {
         textDivProperties.scale = textDivProperties.canvasWidth / width;
         transform = 'scaleX(' + textDivProperties.scale + ')';
       }
       if (textDivProperties.angle !== 0) {
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -118,18 +118,18 @@ return /******/ (function(modules) { // 
 /************************************************************************/
 /******/ ([
 /* 0 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '2.0.815';
-var pdfjsBuild = 'd6927376';
+var pdfjsVersion = '2.0.843';
+var pdfjsBuild = 'bf368f3a';
 var pdfjsCoreWorker = __w_pdfjs_require__(1);
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 1 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
@@ -322,17 +322,17 @@ var WorkerMessageHandler = {
     });
   },
   createDocumentHandler(docParams, port) {
     var pdfManager;
     var terminated = false;
     var cancelXHRs = null;
     var WorkerTasks = [];
     let apiVersion = docParams.apiVersion;
-    let workerVersion = '2.0.815';
+    let workerVersion = '2.0.843';
     if (apiVersion !== workerVersion) {
       throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`);
     }
     var docId = docParams.docId;
     var docBaseUrl = docParams.docBaseUrl;
     var workerHandlerName = docParams.docId + '_worker';
     var handler = new _message_handler.MessageHandler(workerHandlerName, docId, port);
     handler.postMessageTransfers = docParams.postMessageTransfers;
@@ -22280,17 +22280,17 @@ exports.CMapFactory = CMapFactory;
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.getFontType = exports.ProblematicCharRanges = exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.PRIVATE_USE_OFFSET_END = exports.PRIVATE_USE_OFFSET_START = exports.SEAC_ANALYSIS_ENABLED = undefined;
+exports.getFontType = exports.IdentityToUnicodeMap = exports.ToUnicodeMap = exports.FontFlags = exports.Font = exports.ErrorFont = exports.SEAC_ANALYSIS_ENABLED = undefined;
 
 var _util = __w_pdfjs_require__(2);
 
 var _cff_parser = __w_pdfjs_require__(31);
 
 var _glyphlist = __w_pdfjs_require__(34);
 
 var _encodings = __w_pdfjs_require__(33);
@@ -22302,21 +22302,19 @@ var _unicode = __w_pdfjs_require__(36);
 var _font_renderer = __w_pdfjs_require__(37);
 
 var _cmap = __w_pdfjs_require__(29);
 
 var _stream = __w_pdfjs_require__(14);
 
 var _type1_parser = __w_pdfjs_require__(38);
 
-var PRIVATE_USE_OFFSET_START = 0xE000;
-var PRIVATE_USE_OFFSET_END = 0xF8FF;
-var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
+const PRIVATE_USE_AREAS = [[0xE000, 0xF8FF], [0x100000, 0x10FFFD]];
 var PDF_GLYPH_SPACE_UNITS = 1000;
-var SEAC_ANALYSIS_ENABLED = false;
+var SEAC_ANALYSIS_ENABLED = true;
 var FontFlags = {
   FixedPitch: 1,
   Serif: 2,
   Symbolic: 4,
   Script: 8,
   Nonsymbolic: 32,
   Italic: 64,
   AllCap: 65536,
@@ -22585,17 +22583,16 @@ var OpenTypeFileBuilder = function OpenT
       if (tag in this.tables) {
         throw new Error('Table ' + tag + ' already exists');
       }
       this.tables[tag] = data;
     }
   };
   return OpenTypeFileBuilder;
 }();
-var ProblematicCharRanges = new Int32Array([0x0000, 0x0020, 0x007F, 0x00A1, 0x00AD, 0x00AE, 0x0600, 0x0780, 0x08A0, 0x10A0, 0x1780, 0x1800, 0x1C00, 0x1C50, 0x2000, 0x2010, 0x2011, 0x2012, 0x2028, 0x2030, 0x205F, 0x2070, 0x25CC, 0x25CD, 0x3000, 0x3001, 0x3164, 0x3165, 0xAA60, 0xAA80, 0xD800, 0xE000, 0xFFF0, 0x10000]);
 var Font = function FontClosure() {
   function Font(name, file, properties) {
     var charCode;
     this.name = name;
     this.loadedName = properties.loadedName;
     this.isType3Font = properties.isType3Font;
     this.sizes = [];
     this.missingFile = false;
@@ -22789,68 +22786,43 @@ var Font = function FontClosure() {
     for (var charCode in differences) {
       unicode = (0, _unicode.getUnicodeForGlyph)(differences[charCode], glyphsUnicodeMap);
       if (unicode !== -1) {
         toFontChar[+charCode] = unicode;
       }
     }
     return toFontChar;
   }
-  function isProblematicUnicodeLocation(code) {
-    var i = 0,
-        j = ProblematicCharRanges.length - 1;
-    while (i < j) {
-      var c = i + j + 1 >> 1;
-      if (code < ProblematicCharRanges[c]) {
-        j = c - 1;
-      } else {
-        i = c;
-      }
-    }
-    return !(i & 1);
-  }
-  function adjustMapping(charCodeToGlyphId, properties, missingGlyphs) {
-    var toUnicode = properties.toUnicode;
-    var isSymbolic = !!(properties.flags & FontFlags.Symbolic);
-    var isIdentityUnicode = properties.toUnicode instanceof IdentityToUnicodeMap;
+  function adjustMapping(charCodeToGlyphId, hasGlyph, newGlyphZeroId) {
     var newMap = Object.create(null);
     var toFontChar = [];
-    var usedFontCharCodes = [];
-    var nextAvailableFontCharCode = PRIVATE_USE_OFFSET_START;
+    var privateUseAreaIndex = 0;
+    var nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
+    var privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
     for (var originalCharCode in charCodeToGlyphId) {
       originalCharCode |= 0;
       var glyphId = charCodeToGlyphId[originalCharCode];
-      if (missingGlyphs[glyphId]) {
+      if (!hasGlyph(glyphId)) {
         continue;
       }
-      var fontCharCode = originalCharCode;
-      var hasUnicodeValue = false;
-      if (!isIdentityUnicode && toUnicode.has(originalCharCode)) {
-        hasUnicodeValue = true;
-        var unicode = toUnicode.get(fontCharCode);
-        if (unicode.length === 1) {
-          fontCharCode = unicode.charCodeAt(0);
-        }
-      }
-      if (usedFontCharCodes[fontCharCode] !== undefined || isProblematicUnicodeLocation(fontCharCode) || isSymbolic && !hasUnicodeValue) {
-        do {
-          if (nextAvailableFontCharCode > PRIVATE_USE_OFFSET_END) {
-            (0, _util.warn)('Ran out of space in font private use area.');
-            break;
-          }
-          fontCharCode = nextAvailableFontCharCode++;
-          if (SKIP_PRIVATE_USE_RANGE_F000_TO_F01F && fontCharCode === 0xF000) {
-            fontCharCode = 0xF020;
-            nextAvailableFontCharCode = fontCharCode + 1;
-          }
-        } while (usedFontCharCodes[fontCharCode] !== undefined);
+      if (nextAvailableFontCharCode > privateUseOffetEnd) {
+        privateUseAreaIndex++;
+        if (privateUseAreaIndex >= PRIVATE_USE_AREAS.length) {
+          (0, _util.warn)('Ran out of space in font private use area.');
+          break;
+        }
+        nextAvailableFontCharCode = PRIVATE_USE_AREAS[privateUseAreaIndex][0];
+        privateUseOffetEnd = PRIVATE_USE_AREAS[privateUseAreaIndex][1];
+      }
+      var fontCharCode = nextAvailableFontCharCode++;
+      if (glyphId === 0) {
+        glyphId = newGlyphZeroId;
       }
       newMap[fontCharCode] = glyphId;
       toFontChar[originalCharCode] = fontCharCode;
-      usedFontCharCodes[fontCharCode] = true;
     }
     return {
       toFontChar,
       charCodeToGlyphId: newMap,
       nextAvailableFontCharCode
     };
   }
   function getRanges(glyphs, numGlyphs) {
@@ -23027,16 +22999,19 @@ var Font = function FontClosure() {
         } else if (position < 96) {
           ulUnicodeRange3 |= 1 << position - 64;
         } else if (position < 123) {
           ulUnicodeRange4 |= 1 << position - 96;
         } else {
           throw new _util.FormatError('Unicode ranges Bits > 123 are reserved for internal usage');
         }
       }
+      if (lastCharIndex > 0xFFFF) {
+        lastCharIndex = 0xFFFF;
+      }
     } else {
       firstCharIndex = 0;
       lastCharIndex = 255;
     }
     var bbox = properties.bbox || [0, 0, 0, 0];
     var unitsPerEm = override.unitsPerEm || 1 / (properties.fontMatrix || _util.FONT_IDENTITY_MATRIX)[0];
     var scale = properties.ascentScaled ? 1.0 : unitsPerEm / PDF_GLYPH_SPACE_UNITS;
     var typoAscent = override.ascent || Math.round(scale * (properties.ascent || bbox[3]));
@@ -23580,33 +23555,31 @@ var Font = function FontClosure() {
           itemDecode = function fontItemDecode(data, offset) {
             return data[offset] << 9 | data[offset + 1] << 1;
           };
           itemEncode = function fontItemEncode(data, offset, value) {
             data[offset] = value >> 9 & 0xFF;
             data[offset + 1] = value >> 1 & 0xFF;
           };
         }
+        var numGlyphsOut = dupFirstEntry ? numGlyphs + 1 : numGlyphs;
         var locaData = loca.data;
-        var locaDataSize = itemSize * (1 + numGlyphs);
-        if (locaData.length !== locaDataSize) {
-          locaData = new Uint8Array(locaDataSize);
-          locaData.set(loca.data.subarray(0, locaDataSize));
-          loca.data = locaData;
-        }
+        var locaDataSize = itemSize * (1 + numGlyphsOut);
+        locaData = new Uint8Array(locaDataSize);
+        locaData.set(loca.data.subarray(0, locaDataSize));
+        loca.data = locaData;
         var oldGlyfData = glyf.data;
         var oldGlyfDataLength = oldGlyfData.length;
         var newGlyfData = new Uint8Array(oldGlyfDataLength);
         var startOffset = itemDecode(locaData, 0);
         var writeOffset = 0;
         var missingGlyphs = Object.create(null);
         itemEncode(locaData, 0, writeOffset);
         var i, j;
-        var locaCount = dupFirstEntry ? numGlyphs - 1 : numGlyphs;
-        for (i = 0, j = itemSize; i < locaCount; i++, j += itemSize) {
+        for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
           var endOffset = itemDecode(locaData, j);
           if (endOffset === 0) {
             endOffset = startOffset;
           }
           if (endOffset > oldGlyfDataLength && (oldGlyfDataLength + 3 & ~3) === endOffset) {
             endOffset = oldGlyfDataLength;
           }
           if (endOffset > oldGlyfDataLength) {
@@ -23621,17 +23594,17 @@ var Font = function FontClosure() {
             maxSizeOfInstructions = glyphProfile.sizeOfInstructions;
           }
           writeOffset += newLength;
           itemEncode(locaData, j, writeOffset);
           startOffset = endOffset;
         }
         if (writeOffset === 0) {
           var simpleGlyph = new Uint8Array([0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]);
-          for (i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
+          for (i = 0, j = itemSize; i < numGlyphsOut; i++, j += itemSize) {
             itemEncode(locaData, j, simpleGlyph.length);
           }
           glyf.data = simpleGlyph;
         } else if (dupFirstEntry) {
           var firstEntryLength = itemDecode(locaData, itemSize);
           if (newGlyfData.length > firstEntryLength + writeOffset) {
             glyf.data = newGlyfData.subarray(0, firstEntryLength + writeOffset);
           } else {
@@ -24040,45 +24013,47 @@ var Font = function FontClosure() {
         }
         this.isOpenType = false;
       }
       if (!tables['maxp']) {
         throw new _util.FormatError('Required "maxp" table is not found');
       }
       font.pos = (font.start || 0) + tables['maxp'].offset;
       var version = font.getInt32();
-      var numGlyphs = font.getUint16();
+      const numGlyphs = font.getUint16();
+      let numGlyphsOut = numGlyphs + 1;
+      let dupFirstEntry = true;
+      if (numGlyphsOut > 0xFFFF) {
+        dupFirstEntry = false;
+        numGlyphsOut = numGlyphs;
+        (0, _util.warn)('Not enough space in glyfs to duplicate first glyph.');
+      }
       var maxFunctionDefs = 0;
       var maxSizeOfInstructions = 0;
       if (version >= 0x00010000 && tables['maxp'].length >= 22) {
         font.pos += 8;
         var maxZones = font.getUint16();
         if (maxZones > 2) {
           tables['maxp'].data[14] = 0;
           tables['maxp'].data[15] = 2;
         }
         font.pos += 4;
         maxFunctionDefs = font.getUint16();
         font.pos += 4;
         maxSizeOfInstructions = font.getUint16();
       }
-      var dupFirstEntry = false;
-      if (properties.type === 'CIDFontType2' && properties.toUnicode && properties.toUnicode.get(0) > '\u0000') {
-        dupFirstEntry = true;
-        numGlyphs++;
-        tables['maxp'].data[4] = numGlyphs >> 8;
-        tables['maxp'].data[5] = numGlyphs & 255;
-      }
+      tables['maxp'].data[4] = numGlyphsOut >> 8;
+      tables['maxp'].data[5] = numGlyphsOut & 255;
       var hintsValid = sanitizeTTPrograms(tables['fpgm'], tables['prep'], tables['cvt '], maxFunctionDefs);
       if (!hintsValid) {
         delete tables['fpgm'];
         delete tables['prep'];
         delete tables['cvt '];
       }
-      sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphs);
+      sanitizeMetrics(font, tables['hhea'], tables['hmtx'], numGlyphsOut);
       if (!tables['head']) {
         throw new _util.FormatError('Required "head" table is not found');
       }
       sanitizeHead(tables['head'], numGlyphs, isTrueType ? tables['loca'].length : 0);
       var missingGlyphs = Object.create(null);
       if (isTrueType) {
         var isGlyphLocationsLong = int16(tables['head'].data[50], tables['head'].data[51]);
         var glyphsInfo = sanitizeGlyphLocations(tables['loca'], tables['glyf'], numGlyphs, isGlyphLocationsLong, hintsValid, dupFirstEntry, maxSizeOfInstructions);
@@ -24100,21 +24075,22 @@ var Font = function FontClosure() {
         yMax: int16(tables['head'].data[42], tables['head'].data[43]),
         yMin: signedInt16(tables['head'].data[38], tables['head'].data[39]),
         ascent: int16(tables['hhea'].data[4], tables['hhea'].data[5]),
         descent: signedInt16(tables['hhea'].data[6], tables['hhea'].data[7])
       };
       this.ascent = metricsOverride.ascent / metricsOverride.unitsPerEm;
       this.descent = metricsOverride.descent / metricsOverride.unitsPerEm;
       if (tables['post']) {
-        var valid = readPostScriptTable(tables['post'], properties, numGlyphs);
-        if (!valid) {
-          tables['post'] = null;
-        }
-      }
+        readPostScriptTable(tables['post'], properties, numGlyphs);
+      }
+      tables['post'] = {
+        tag: 'post',
+        data: createPostTable(properties)
+      };
       var charCodeToGlyphId = [],
           charCode;
       function hasGlyph(glyphId) {
         return !missingGlyphs[glyphId];
       }
       if (properties.composite) {
         var cidToGidMap = properties.cidToGidMap || [];
         var isCidToGidMapEmpty = cidToGidMap.length === 0;
@@ -24127,19 +24103,16 @@ var Font = function FontClosure() {
             glyphId = cid;
           } else if (cidToGidMap[cid] !== undefined) {
             glyphId = cidToGidMap[cid];
           }
           if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
             charCodeToGlyphId[charCode] = glyphId;
           }
         });
-        if (dupFirstEntry && (isCidToGidMapEmpty || !charCodeToGlyphId[0])) {
-          charCodeToGlyphId[0] = numGlyphs - 1;
-        }
       } else {
         var cmapTable = readCmapTable(tables['cmap'], font, this.isSymbolicFont, properties.hasEncoding);
         var cmapPlatformId = cmapTable.platformId;
         var cmapEncodingId = cmapTable.encodingId;
         var cmapMappings = cmapTable.mappings;
         var cmapMappingsLength = cmapMappings.length;
         if (properties.hasEncoding && (cmapPlatformId === 3 && cmapEncodingId === 1 || cmapPlatformId === 1 && cmapEncodingId === 0) || cmapPlatformId === -1 && cmapEncodingId === -1 && !!(0, _encodings.getEncoding)(properties.baseEncodingName)) {
           var baseEncoding = [];
@@ -24197,39 +24170,38 @@ var Font = function FontClosure() {
             }
             charCodeToGlyphId[charCode] = cmapMappings[i].glyphId;
           }
         }
       }
       if (charCodeToGlyphId.length === 0) {
         charCodeToGlyphId[0] = 0;
       }
-      var newMapping = adjustMapping(charCodeToGlyphId, properties, missingGlyphs);
+      let glyphZeroId = numGlyphsOut - 1;
+      if (!dupFirstEntry) {
+        glyphZeroId = 0;
+      }
+      var newMapping = adjustMapping(charCodeToGlyphId, hasGlyph, glyphZeroId);
       this.toFontChar = newMapping.toFontChar;
       tables['cmap'] = {
         tag: 'cmap',
-        data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphs)
+        data: createCmapTable(newMapping.charCodeToGlyphId, numGlyphsOut)
       };
       if (!tables['OS/2'] || !validateOS2Table(tables['OS/2'])) {
         tables['OS/2'] = {
           tag: 'OS/2',
           data: createOS2Table(properties, newMapping.charCodeToGlyphId, metricsOverride)
         };
       }
-      if (!tables['post']) {
-        tables['post'] = {
-          tag: 'post',
-          data: createPostTable(properties)
-        };
-      }
       if (!isTrueType) {
         try {
           cffFile = new _stream.Stream(tables['CFF '].data);
           var parser = new _cff_parser.CFFParser(cffFile, properties, SEAC_ANALYSIS_ENABLED);
           cff = parser.parse();
+          cff.duplicateFirstGlyph();
           var compiler = new _cff_parser.CFFCompiler(cff);
           tables['CFF '].data = compiler.compile();
         } catch (e) {
           (0, _util.warn)('Failed to compile font ' + properties.loadedName);
         }
       }
       if (!tables['name']) {
         tables['name'] = {
@@ -24246,18 +24218,22 @@ var Font = function FontClosure() {
       }
       return builder.toArray();
     },
     convert: function Font_convert(fontName, font, properties) {
       properties.fixedPitch = false;
       if (properties.builtInEncoding) {
         adjustToUnicode(properties, properties.builtInEncoding);
       }
+      let glyphZeroId = 1;
+      if (font instanceof CFFFont) {
+        glyphZeroId = font.numGlyphs - 1;
+      }
       var mapping = font.getGlyphMapping(properties);
-      var newMapping = adjustMapping(mapping, properties, Object.create(null));
+      var newMapping = adjustMapping(mapping, font.hasGlyphId.bind(font), glyphZeroId);
       this.toFontChar = newMapping.toFontChar;
       var numGlyphs = font.numGlyphs;
       function getCharCodes(charCodeToGlyphId, glyphId) {
         var charCodes = null;
         for (var charCode in charCodeToGlyphId) {
           if (glyphId === charCodeToGlyphId[charCode]) {
             if (!charCodes) {
               charCodes = [];
@@ -24398,21 +24374,21 @@ var Font = function FontClosure() {
         operatorListId = fontCharCode;
       }
       var accent = null;
       if (this.seacMap && this.seacMap[charcode]) {
         isInFont = true;
         var seac = this.seacMap[charcode];
         fontCharCode = seac.baseFontCharCode;
         accent = {
-          fontChar: String.fromCharCode(seac.accentFontCharCode),
+          fontChar: String.fromCodePoint(seac.accentFontCharCode),
           offset: seac.accentOffset
         };
       }
-      var fontChar = String.fromCharCode(fontCharCode);
+      var fontChar = typeof fontCharCode === 'number' ? String.fromCodePoint(fontCharCode) : '';
       var glyph = this.glyphCache[charcode];
       if (!glyph || !glyph.matchesForCache(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont)) {
         glyph = new Glyph(fontChar, unicode, accent, width, vmetric, operatorListId, isSpace, isInFont);
         this.glyphCache[charcode] = glyph;
       }
       return glyph;
     },
     charsToGlyphs: function Font_charsToGlyphs(chars) {
@@ -24664,16 +24640,26 @@ var Type1Font = function Type1FontClosur
           glyphId = glyphNames.indexOf(encoding[charCode]);
           if (glyphId >= 0) {
             builtInEncoding[charCode] = glyphId;
           }
         }
       }
       return type1FontGlyphMapping(properties, builtInEncoding, glyphNames);
     },
+    hasGlyphId: function Type1Font_hasGlyphID(id) {
+      if (id < 0 || id >= this.numGlyphs) {
+        return false;
+      }
+      if (id === 0) {
+        return true;
+      }
+      var glyph = this.charstrings[id - 1];
+      return glyph.charstring.length > 0;
+    },
     getSeacs: function Type1Font_getSeacs(charstrings) {
       var i, ii;
       var seacMap = [];
       for (i = 0, ii = charstrings.length; i < ii; i++) {
         var charstring = charstrings[i];
         if (charstring.seac) {
           seacMap[i + 1] = charstring.seac;
         }
@@ -24741,22 +24727,17 @@ var Type1Font = function Type1FontClosur
           index = 0;
         }
         charsetArray.push(index >> 8 & 0xff, index & 0xff);
       }
       cff.charset = new _cff_parser.CFFCharset(false, 0, [], charsetArray);
       var charStringsIndex = new _cff_parser.CFFIndex();
       charStringsIndex.add([0x8B, 0x0E]);
       for (i = 0; i < count; i++) {
-        var glyph = glyphs[i];
-        if (glyph.length === 0) {
-          charStringsIndex.add([0x8B, 0x0E]);
-          continue;
-        }
-        charStringsIndex.add(glyph);
+        charStringsIndex.add(glyphs[i]);
       }
       cff.charStrings = charStringsIndex;
       var privateDict = new _cff_parser.CFFPrivateDict();
       privateDict.setByName('Subrs', null);
       var fields = ['BlueValues', 'OtherBlues', 'FamilyBlues', 'FamilyOtherBlues', 'StemSnapH', 'StemSnapV', 'BlueShift', 'BlueFuzz', 'BlueScale', 'LanguageGroup', 'ExpansionFactor', 'ForceBold', 'StdHW', 'StdVW'];
       for (i = 0, ii = fields.length; i < ii; i++) {
         var field = fields[i];
         if (!(field in properties.privateData)) {
@@ -24782,16 +24763,17 @@ var Type1Font = function Type1FontClosur
   };
   return Type1Font;
 }();
 var CFFFont = function CFFFontClosure() {
   function CFFFont(file, properties) {
     this.properties = properties;
     var parser = new _cff_parser.CFFParser(file, properties, SEAC_ANALYSIS_ENABLED);
     this.cff = parser.parse();
+    this.cff.duplicateFirstGlyph();
     var compiler = new _cff_parser.CFFCompiler(this.cff);
     this.seacs = this.cff.seacs;
     try {
       this.data = compiler.compile();
     } catch (e) {
       (0, _util.warn)('Failed to compile font ' + properties.loadedName);
       this.data = file;
     }
@@ -24822,52 +24804,42 @@ var CFFFont = function CFFFontClosure() 
             charCodeToGlyphId[glyphId] = glyphId;
           }
         }
         return charCodeToGlyphId;
       }
       var encoding = cff.encoding ? cff.encoding.encoding : null;
       charCodeToGlyphId = type1FontGlyphMapping(properties, encoding, charsets);
       return charCodeToGlyphId;
+    },
+    hasGlyphId: function CFFFont_hasGlyphID(id) {
+      return this.cff.hasGlyphId(id);
     }
   };
   return CFFFont;
 }();
-(function checkSeacSupport() {
-  if (typeof navigator !== 'undefined' && /Windows/.test(navigator.userAgent)) {
-    exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED = true;
-  }
-})();
-(function checkChromeWindows() {
-  if (typeof navigator !== 'undefined' && /Windows.*Chrome/.test(navigator.userAgent)) {
-    SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = true;
-  }
-})();
 exports.SEAC_ANALYSIS_ENABLED = SEAC_ANALYSIS_ENABLED;
-exports.PRIVATE_USE_OFFSET_START = PRIVATE_USE_OFFSET_START;
-exports.PRIVATE_USE_OFFSET_END = PRIVATE_USE_OFFSET_END;
 exports.ErrorFont = ErrorFont;
 exports.Font = Font;
 exports.FontFlags = FontFlags;
 exports.ToUnicodeMap = ToUnicodeMap;
 exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
-exports.ProblematicCharRanges = ProblematicCharRanges;
 exports.getFontType = getFontType;
 
 /***/ }),
 /* 31 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
-exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined;
+exports.CFFFDSelect = exports.CFFCompiler = exports.CFFPrivateDict = exports.CFFTopDict = exports.CFFCharset = exports.CFFIndex = exports.CFFStrings = exports.CFFHeader = exports.CFF = exports.CFFParser = exports.CFFStandardStrings = undefined;
 
 var _util = __w_pdfjs_require__(2);
 
 var _charsets = __w_pdfjs_require__(32);
 
 var _encodings = __w_pdfjs_require__(33);
 
 var MAX_SUBR_NESTING = 10;
@@ -25621,59 +25593,50 @@ var CFFParser = function CFFParserClosur
           readSupplement();
         }
         raw = bytes.subarray(dataStart, dataEnd);
       }
       format = format & 0x7f;
       return new CFFEncoding(predefined, format, encoding, raw);
     },
     parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
-      var start = pos;
       var bytes = this.bytes;
       var format = bytes[pos++];
-      var fdSelect = [],
-          rawBytes;
-      var i,
-          invalidFirstGID = false;
+      var fdSelect = [];
+      var i;
       switch (format) {
         case 0:
           for (i = 0; i < length; ++i) {
             var id = bytes[pos++];
             fdSelect.push(id);
           }
-          rawBytes = bytes.subarray(start, pos);
           break;
         case 3:
           var rangesCount = bytes[pos++] << 8 | bytes[pos++];
           for (i = 0; i < rangesCount; ++i) {
             var first = bytes[pos++] << 8 | bytes[pos++];
             if (i === 0 && first !== 0) {
               (0, _util.warn)('parseFDSelect: The first range must have a first GID of 0' + ' -- trying to recover.');
-              invalidFirstGID = true;
               first = 0;
             }
             var fdIndex = bytes[pos++];
             var next = bytes[pos] << 8 | bytes[pos + 1];
             for (var j = first; j < next; ++j) {
               fdSelect.push(fdIndex);
             }
           }
           pos += 2;
-          rawBytes = bytes.subarray(start, pos);
-          if (invalidFirstGID) {
-            rawBytes[3] = rawBytes[4] = 0;
-          }
           break;
         default:
           throw new _util.FormatError(`parseFDSelect: Unknown format "${format}".`);
       }
       if (fdSelect.length !== length) {
         throw new _util.FormatError('parseFDSelect: Invalid font data.');
       }
-      return new CFFFDSelect(fdSelect, rawBytes);
+      return new CFFFDSelect(format, fdSelect);
     }
   };
   return CFFParser;
 }();
 var CFF = function CFFClosure() {
   function CFF() {
     this.header = null;
     this.names = [];
@@ -25682,16 +25645,36 @@ var CFF = function CFFClosure() {
     this.globalSubrIndex = null;
     this.encoding = null;
     this.charset = null;
     this.charStrings = null;
     this.fdArray = [];
     this.fdSelect = null;
     this.isCIDFont = false;
   }
+  CFF.prototype = {
+    duplicateFirstGlyph: function CFF_duplicateFirstGlyph() {
+      if (this.charStrings.count >= 65535) {
+        (0, _util.warn)('Not enough space in charstrings to duplicate first glyph.');
+        return;
+      }
+      var glyphZero = this.charStrings.get(0);
+      this.charStrings.add(glyphZero);
+      if (this.isCIDFont) {
+        this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]);
+      }
+    },
+    hasGlyphId: function CFF_hasGlyphID(id) {
+      if (id < 0 || id >= this.charStrings.count) {
+        return false;
+      }
+      var glyph = this.charStrings.get(id);
+      return glyph.length > 0;
+    }
+  };
   return CFF;
 }();
 var CFFHeader = function CFFHeaderClosure() {
   function CFFHeader(major, minor, hdrSize, offSize) {
     this.major = major;
     this.minor = minor;
     this.hdrSize = hdrSize;
     this.offSize = offSize;
@@ -25868,19 +25851,19 @@ var CFFEncoding = function CFFEncodingCl
     this.predefined = predefined;
     this.format = format;
     this.encoding = encoding;
     this.raw = raw;
   }
   return CFFEncoding;
 }();
 var CFFFDSelect = function CFFFDSelectClosure() {
-  function CFFFDSelect(fdSelect, raw) {
+  function CFFFDSelect(format, fdSelect) {
+    this.format = format;
     this.fdSelect = fdSelect;
-    this.raw = raw;
   }
   CFFFDSelect.prototype = {
     getFDIndex: function CFFFDSelect_get(glyphIndex) {
       if (glyphIndex < 0 || glyphIndex >= this.fdSelect.length) {
         return -1;
       }
       return this.fdSelect[glyphIndex];
     }
@@ -25961,47 +25944,42 @@ var CFFCompiler = function CFFCompilerCl
             var matrix = base.slice(0);
             if (subDict.hasName('FontMatrix')) {
               matrix = _util.Util.transform(matrix, subDict.getByName('FontMatrix'));
             }
             subDict.setByName('FontMatrix', matrix);
           }
         }
       }
+      cff.topDict.setByName('charset', 0);
       var compiled = this.compileTopDicts([cff.topDict], output.length, cff.isCIDFont);
       output.add(compiled.output);
       var topDictTracker = compiled.trackers[0];
       var stringIndex = this.compileStringIndex(cff.strings.strings);
       output.add(stringIndex);
       var globalSubrIndex = this.compileIndex(cff.globalSubrIndex);
       output.add(globalSubrIndex);
       if (cff.encoding && cff.topDict.hasName('Encoding')) {
         if (cff.encoding.predefined) {
           topDictTracker.setEntryLocation('Encoding', [cff.encoding.format], output);
         } else {
           var encoding = this.compileEncoding(cff.encoding);
           topDictTracker.setEntryLocation('Encoding', [output.length], output);
           output.add(encoding);
         }
       }
-      if (cff.charset && cff.topDict.hasName('charset')) {
-        if (cff.charset.predefined) {
-          topDictTracker.setEntryLocation('charset', [cff.charset.format], output);
-        } else {
-          var charset = this.compileCharset(cff.charset);
-          topDictTracker.setEntryLocation('charset', [output.length], output);
-          output.add(charset);
-        }
-      }
+      var charset = this.compileCharset(cff.charset);
+      topDictTracker.setEntryLocation('charset', [output.length], output);
+      output.add(charset);
       var charStrings = this.compileCharStrings(cff.charStrings);
       topDictTracker.setEntryLocation('CharStrings', [output.length], output);
       output.add(charStrings);
       if (cff.isCIDFont) {
         topDictTracker.setEntryLocation('FDSelect', [output.length], output);
-        var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
+        var fdSelect = this.compileFDSelect(cff.fdSelect);
         output.add(fdSelect);
         compiled = this.compileTopDicts(cff.fdArray, output.length, true);
         topDictTracker.setEntryLocation('FDArray', [output.length], output);
         output.add(compiled.output);
         var fontDictTrackers = compiled.trackers;
         this.compilePrivateDicts(cff.fdArray, fontDictTrackers, output);
       }
       this.compilePrivateDicts([cff.topDict], [topDictTracker], output);
@@ -26186,26 +26164,65 @@ var CFFCompiler = function CFFCompilerCl
       }
       return this.compileIndex(stringIndex);
     },
     compileGlobalSubrIndex: function CFFCompiler_compileGlobalSubrIndex() {
       var globalSubrIndex = this.cff.globalSubrIndex;
       this.out.writeByteArray(this.compileIndex(globalSubrIndex));
     },
     compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
-      return this.compileIndex(charStrings);
+      var charStringsIndex = new CFFIndex();
+      for (var i = 0; i < charStrings.count; i++) {
+        var glyph = charStrings.get(i);
+        if (glyph.length === 0) {
+          charStringsIndex.add(new Uint8Array([0x8B, 0x0E]));
+          continue;
+        }
+        charStringsIndex.add(glyph);
+      }
+      return this.compileIndex(charStringsIndex);
     },
     compileCharset: function CFFCompiler_compileCharset(charset) {
-      return this.compileTypedArray(charset.raw);
+      let length = 1 + (this.cff.charStrings.count - 1) * 2;
+      let out = new Uint8Array(length);
+      return this.compileTypedArray(out);
     },
     compileEncoding: function CFFCompiler_compileEncoding(encoding) {
       return this.compileTypedArray(encoding.raw);
     },
     compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
-      return this.compileTypedArray(fdSelect);
+      let format = fdSelect.format;
+      let out, i;
+      switch (format) {
+        case 0:
+          out = new Uint8Array(1 + fdSelect.fdSelect.length);
+          out[0] = format;
+          for (i = 0; i < fdSelect.fdSelect.length; i++) {
+            out[i + 1] = fdSelect.fdSelect[i];
+          }
+          break;
+        case 3:
+          let start = 0;
+          let lastFD = fdSelect.fdSelect[0];
+          let ranges = [format, 0, 0, start >> 8 & 0xFF, start & 0xFF, lastFD];
+          for (i = 1; i < fdSelect.fdSelect.length; i++) {
+            let currentFD = fdSelect.fdSelect[i];
+            if (currentFD !== lastFD) {
+              ranges.push(i >> 8 & 0xFF, i & 0xFF, currentFD);
+              lastFD = currentFD;
+            }
+          }
+          let numRanges = (ranges.length - 3) / 3;
+          ranges[1] = numRanges >> 8 & 0xFF;
+          ranges[2] = numRanges & 0xFF;
+          ranges.push(i >> 8 & 0xFF, i & 0xFF);
+          out = new Uint8Array(ranges);
+          break;
+      }
+      return this.compileTypedArray(out);
     },
     compileTypedArray: function CFFCompiler_compileTypedArray(data) {
       var out = [];
       for (var i = 0, ii = data.length; i < ii; ++i) {
         out[i] = data[i];
       }
       return out;
     },
@@ -26266,16 +26283,17 @@ exports.CFFParser = CFFParser;
 exports.CFF = CFF;
 exports.CFFHeader = CFFHeader;
 exports.CFFStrings = CFFStrings;
 exports.CFFIndex = CFFIndex;
 exports.CFFCharset = CFFCharset;
 exports.CFFTopDict = CFFTopDict;
 exports.CFFPrivateDict = CFFPrivateDict;
 exports.CFFCompiler = CFFCompiler;
+exports.CFFFDSelect = CFFFDSelect;
 
 /***/ }),
 /* 32 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
@@ -33698,17 +33716,17 @@ var FontRendererFactory = function FontR
     for (var j = itemSize; j < loca.length; j += itemSize) {
       var endOffset = itemDecode(loca, j);
       glyphs.push(glyf.subarray(startOffset, endOffset));
       startOffset = endOffset;
     }
     return glyphs;
   }
   function lookupCmap(ranges, unicode) {
-    var code = unicode.charCodeAt(0),
+    var code = unicode.codePointAt(0),
         gid = 0;
     var l = 0,
         r = ranges.length - 1;
     while (l < r) {
       var c = l + r + 1 >> 1;
       if (code < ranges[c].start) {
         r = c - 1;
       } else {
--- a/browser/extensions/pdfjs/content/web/viewer.html
+++ b/browser/extensions/pdfjs/content/web/viewer.html
@@ -77,21 +77,25 @@ See https://github.com/adobe-type-tools/
               </button>
               <div class="splitToolbarButtonSeparator"></div>
               <button id="findNext" class="toolbarButton findNext" title="Find the next occurrence of the phrase" tabindex="93" data-l10n-id="find_next">
                 <span data-l10n-id="find_next_label">Next</span>
               </button>
             </div>
           </div>
 
-          <div id="findbarOptionsContainer">
+          <div id="findbarOptionsOneContainer">
             <input type="checkbox" id="findHighlightAll" class="toolbarField" tabindex="94">
             <label for="findHighlightAll" class="toolbarLabel" data-l10n-id="find_highlight">Highlight all</label>
             <input type="checkbox" id="findMatchCase" class="toolbarField" tabindex="95">
             <label for="findMatchCase" class="toolbarLabel" data-l10n-id="find_match_case_label">Match case</label>
+          </div>
+          <div id="findbarOptionsTwoContainer">
+            <input type="checkbox" id="findEntireWord" class="toolbarField" tabindex="96">
+            <label for="findEntireWord" class="toolbarLabel" data-l10n-id="find_entire_word_label">Whole words</label>
             <span id="findResultsCount" class="toolbarLabel hidden"></span>
           </div>
 
           <div id="findbarMessageContainer">
             <span id="findMsg" class="toolbarLabel"></span>
           </div>
         </div>  <!-- findbar -->
 
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -115,18 +115,18 @@
 
 ;
 let pdfjsWebApp, pdfjsWebAppOptions;
 {
   pdfjsWebApp = __webpack_require__(1);
   pdfjsWebAppOptions = __webpack_require__(8);
 }
 {
-  __webpack_require__(33);
-  __webpack_require__(36);
+  __webpack_require__(34);
+  __webpack_require__(37);
 }
 ;
 ;
 ;
 function getViewerConfiguration() {
   return {
     appContainer: document.body,
     mainContainer: document.getElementById('viewerContainer'),
@@ -195,19 +195,19 @@ function getViewerConfiguration() {
       resizer: document.getElementById('sidebarResizer')
     },
     findBar: {
       bar: document.getElementById('findbar'),
       toggleButton: document.getElementById('viewFind'),
       findField: document.getElementById('findInput'),
       highlightAllCheckbox: document.getElementById('findHighlightAll'),
       caseSensitiveCheckbox: document.getElementById('findMatchCase'),
+      entireWordCheckbox: document.getElementById('findEntireWord'),
       findMsg: document.getElementById('findMsg'),
       findResultsCount: document.getElementById('findResultsCount'),
-      findStatusIcon: document.getElementById('findStatusIcon'),
       findPreviousButton: document.getElementById('findPrevious'),
       findNextButton: document.getElementById('findNext')
     },
     passwordOverlay: {
       overlayName: 'passwordOverlay',
       container: document.getElementById('passwordOverlay'),
       label: document.getElementById('passwordText'),
       input: document.getElementById('password'),
@@ -293,41 +293,42 @@ var _password_prompt = __webpack_require
 var _pdf_attachment_viewer = __webpack_require__(13);
 
 var _pdf_document_properties = __webpack_require__(14);
 
 var _pdf_find_bar = __webpack_require__(15);
 
 var _pdf_find_controller = __webpack_require__(16);
 
-var _pdf_history = __webpack_require__(17);
-
-var _pdf_link_service = __webpack_require__(18);
-
-var _pdf_outline_viewer = __webpack_require__(19);
-
-var _pdf_presentation_mode = __webpack_require__(20);
-
-var _pdf_sidebar_resizer = __webpack_require__(21);
-
-var _pdf_thumbnail_viewer = __webpack_require__(22);
-
-var _pdf_viewer = __webpack_require__(24);
-
-var _secondary_toolbar = __webpack_require__(29);
-
-var _toolbar = __webpack_require__(31);
-
-var _view_history = __webpack_require__(32);
+var _pdf_history = __webpack_require__(18);
+
+var _pdf_link_service = __webpack_require__(19);
+
+var _pdf_outline_viewer = __webpack_require__(20);
+
+var _pdf_presentation_mode = __webpack_require__(21);
+
+var _pdf_sidebar_resizer = __webpack_require__(22);
+
+var _pdf_thumbnail_viewer = __webpack_require__(23);
+
+var _pdf_viewer = __webpack_require__(25);
+
+var _secondary_toolbar = __webpack_require__(30);
+
+var _toolbar = __webpack_require__(32);
+
+var _view_history = __webpack_require__(33);
 
 const DEFAULT_SCALE_DELTA = 1.1;
 const DISABLE_AUTO_FETCH_LOADING_BAR_TIMEOUT = 5000;
 const FORCE_PAGES_LOADED_TIMEOUT = 10000;
 const DefaultExternalServices = {
   updateFindControlState(data) {},
+  updateFindMatchesCount(data) {},
   initPassiveLoading(callbacks) {},
   fallback(data, callback) {},
   reportTelemetry(data) {},
   createDownloadManager(options) {
     throw new Error('Not implemented: createDownloadManager');
   },
   createPreferences() {
     throw new Error('Not implemented: createPreferences');
@@ -374,228 +375,224 @@ let PDFViewerApplication = {
   isInitialViewSet: false,
   downloadComplete: false,
   isViewerEmbedded: window.parent !== window,
   url: '',
   baseUrl: '',
   externalServices: DefaultExternalServices,
   _boundEvents: {},
   contentDispositionFilename: null,
-  initialize(appConfig) {
+  async initialize(appConfig) {
     this.preferences = this.externalServices.createPreferences();
     this.appConfig = appConfig;
-    return this._readPreferences().then(() => {
-      return this._parseHashParameters();
-    }).then(() => {
-      return this._initializeL10n();
-    }).then(() => {
-      if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) {
-        _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP);
-      }
-      return this._initializeViewerComponents();
-    }).then(() => {
-      this.bindEvents();
-      this.bindWindowEvents();
-      let appContainer = appConfig.appContainer || document.documentElement;
-      this.l10n.translate(appContainer).then(() => {
-        this.eventBus.dispatch('localized', { source: this });
-      });
-      this.initialized = true;
-    });
+    await this._readPreferences();
+    await this._parseHashParameters();
+    await this._initializeL10n();
+    if (this.isViewerEmbedded && _app_options.AppOptions.get('externalLinkTarget') === _pdfjsLib.LinkTarget.NONE) {
+      _app_options.AppOptions.set('externalLinkTarget', _pdfjsLib.LinkTarget.TOP);
+    }
+    await this._initializeViewerComponents();
+    this.bindEvents();
+    this.bindWindowEvents();
+    let appContainer = appConfig.appContainer || document.documentElement;
+    this.l10n.translate(appContainer).then(() => {
+      this.eventBus.dispatch('localized', { source: this });
+    });
+    this.initialized = true;
   },
-  _readPreferences() {
+  async _readPreferences() {
     const OVERRIDES = {
       disableFontFace: true,
       disableRange: true,
       disableStream: true,
       textLayerMode: _ui_utils.TextLayerMode.DISABLE
     };
-    return this.preferences.getAll().then(function (prefs) {
+    try {
+      const prefs = await this.preferences.getAll();
       for (let name in prefs) {
         if (name in OVERRIDES && _app_options.AppOptions.get(name) === OVERRIDES[name]) {
           continue;
         }
         _app_options.AppOptions.set(name, prefs[name]);
       }
-    }, function (reason) {});
+    } catch (reason) {}
   },
-  _parseHashParameters() {
+  async _parseHashParameters() {
+    if (!_app_options.AppOptions.get('pdfBugEnabled')) {
+      return;
+    }
     const waitOn = [];
-    if (_app_options.AppOptions.get('pdfBugEnabled')) {
-      let hash = document.location.hash.substring(1);
-      let hashParams = (0, _ui_utils.parseQueryString)(hash);
-      if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') {
-        waitOn.push(loadFakeWorker());
-      }
-      if ('disablerange' in hashParams) {
-        _app_options.AppOptions.set('disableRange', hashParams['disablerange'] === 'true');
-      }
-      if ('disablestream' in hashParams) {
-        _app_options.AppOptions.set('disableStream', hashParams['disablestream'] === 'true');
-      }
-      if ('disableautofetch' in hashParams) {
-        _app_options.AppOptions.set('disableAutoFetch', hashParams['disableautofetch'] === 'true');
-      }
-      if ('disablefontface' in hashParams) {
-        _app_options.AppOptions.set('disableFontFace', hashParams['disablefontface'] === 'true');
-      }
-      if ('disablehistory' in hashParams) {
-        _app_options.AppOptions.set('disableHistory', hashParams['disablehistory'] === 'true');
-      }
-      if ('webgl' in hashParams) {
-        _app_options.AppOptions.set('enableWebGL', hashParams['webgl'] === 'true');
-      }
-      if ('useonlycsszoom' in hashParams) {
-        _app_options.AppOptions.set('useOnlyCssZoom', hashParams['useonlycsszoom'] === 'true');
-      }
-      if ('verbosity' in hashParams) {
-        _app_options.AppOptions.set('verbosity', hashParams['verbosity'] | 0);
-      }
-      if ('textlayer' in hashParams) {
-        switch (hashParams['textlayer']) {
-          case 'off':
-            _app_options.AppOptions.set('textLayerMode', _ui_utils.TextLayerMode.DISABLE);
-            break;
-          case 'visible':
-          case 'shadow':
-          case 'hover':
-            let viewer = this.appConfig.viewerContainer;
-            viewer.classList.add('textLayer-' + hashParams['textlayer']);
-            break;
-        }
-      }
-      if ('pdfbug' in hashParams) {
-        _app_options.AppOptions.set('pdfBug', true);
-        let enabled = hashParams['pdfbug'].split(',');
-        waitOn.push(loadAndEnablePDFBug(enabled));
-      }
+    let hash = document.location.hash.substring(1);
+    let hashParams = (0, _ui_utils.parseQueryString)(hash);
+    if ('disableworker' in hashParams && hashParams['disableworker'] === 'true') {
+      waitOn.push(loadFakeWorker());
+    }
+    if ('disablerange' in hashParams) {
+      _app_options.AppOptions.set('disableRange', hashParams['disablerange'] === 'true');
+    }
+    if ('disablestream' in hashParams) {
+      _app_options.AppOptions.set('disableStream', hashParams['disablestream'] === 'true');
+    }
+    if ('disableautofetch' in hashParams) {
+      _app_options.AppOptions.set('disableAutoFetch', hashParams['disableautofetch'] === 'true');
+    }
+    if ('disablefontface' in hashParams) {
+      _app_options.AppOptions.set('disableFontFace', hashParams['disablefontface'] === 'true');
+    }
+    if ('disablehistory' in hashParams) {
+      _app_options.AppOptions.set('disableHistory', hashParams['disablehistory'] === 'true');
+    }
+    if ('webgl' in hashParams) {
+      _app_options.AppOptions.set('enableWebGL', hashParams['webgl'] === 'true');
+    }
+    if ('useonlycsszoom' in hashParams) {
+      _app_options.AppOptions.set('useOnlyCssZoom', hashParams['useonlycsszoom'] === 'true');
+    }
+    if ('verbosity' in hashParams) {
+      _app_options.AppOptions.set('verbosity', hashParams['verbosity'] | 0);
+    }
+    if ('textlayer' in hashParams) {
+      switch (hashParams['textlayer']) {
+        case 'off':
+          _app_options.AppOptions.set('textLayerMode', _ui_utils.TextLayerMode.DISABLE);
+          break;
+        case 'visible':
+        case 'shadow':
+        case 'hover':
+          let viewer = this.appConfig.viewerContainer;
+          viewer.classList.add('textLayer-' + hashParams['textlayer']);
+          break;
+      }
+    }
+    if ('pdfbug' in hashParams) {
+      _app_options.AppOptions.set('pdfBug', true);
+      let enabled = hashParams['pdfbug'].split(',');
+      waitOn.push(loadAndEnablePDFBug(enabled));
     }
     return Promise.all(waitOn);
   },
-  _initializeL10n() {
+  async _initializeL10n() {
     this.l10n = this.externalServices.createL10n({ locale: _app_options.AppOptions.get('locale') });
-    return this.l10n.getDirection().then(dir => {
-      document.getElementsByTagName('html')[0].dir = dir;
-    });
+    const dir = await this.l10n.getDirection();
+    document.getElementsByTagName('html')[0].dir = dir;
   },
-  _initializeViewerComponents() {
-    let { appConfig } = this;
-    return new Promise((resolve, reject) => {
-      this.overlayManager = new _overlay_manager.OverlayManager();
-      const dispatchToDOM = _app_options.AppOptions.get('eventBusDispatchToDOM');
-      let eventBus = appConfig.eventBus || (0, _dom_events.getGlobalEventBus)(dispatchToDOM);
-      this.eventBus = eventBus;
-      let pdfRenderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
-      pdfRenderingQueue.onIdle = this.cleanup.bind(this);
-      this.pdfRenderingQueue = pdfRenderingQueue;
-      let pdfLinkService = new _pdf_link_service.PDFLinkService({
-        eventBus,
-        externalLinkTarget: _app_options.AppOptions.get('externalLinkTarget'),
-        externalLinkRel: _app_options.AppOptions.get('externalLinkRel')
-      });
-      this.pdfLinkService = pdfLinkService;
-      let downloadManager = this.externalServices.createDownloadManager({ disableCreateObjectURL: _app_options.AppOptions.get('disableCreateObjectURL') });
-      this.downloadManager = downloadManager;
-      let container = appConfig.mainContainer;
-      let viewer = appConfig.viewerContainer;
-      this.pdfViewer = new _pdf_viewer.PDFViewer({
+  async _initializeViewerComponents() {
+    const appConfig = this.appConfig;
+    this.overlayManager = new _overlay_manager.OverlayManager();
+    const dispatchToDOM = _app_options.AppOptions.get('eventBusDispatchToDOM');
+    let eventBus = appConfig.eventBus || (0, _dom_events.getGlobalEventBus)(dispatchToDOM);
+    this.eventBus = eventBus;
+    let pdfRenderingQueue = new _pdf_rendering_queue.PDFRenderingQueue();
+    pdfRenderingQueue.onIdle = this.cleanup.bind(this);
+    this.pdfRenderingQueue = pdfRenderingQueue;
+    let pdfLinkService = new _pdf_link_service.PDFLinkService({
+      eventBus,
+      externalLinkTarget: _app_options.AppOptions.get('externalLinkTarget'),
+      externalLinkRel: _app_options.AppOptions.get('externalLinkRel')
+    });
+    this.pdfLinkService = pdfLinkService;
+    let downloadManager = this.externalServices.createDownloadManager({ disableCreateObjectURL: _app_options.AppOptions.get('disableCreateObjectURL') });
+    this.downloadManager = downloadManager;
+    let container = appConfig.mainContainer;
+    let viewer = appConfig.viewerContainer;
+    this.pdfViewer = new _pdf_viewer.PDFViewer({
+      container,
+      viewer,
+      eventBus,
+      renderingQueue: pdfRenderingQueue,
+      linkService: pdfLinkService,
+      downloadManager,
+      renderer: _app_options.AppOptions.get('renderer'),
+      enableWebGL: _app_options.AppOptions.get('enableWebGL'),
+      l10n: this.l10n,
+      textLayerMode: _app_options.AppOptions.get('textLayerMode'),
+      imageResourcesPath: _app_options.AppOptions.get('imageResourcesPath'),
+      renderInteractiveForms: _app_options.AppOptions.get('renderInteractiveForms'),
+      enablePrintAutoRotate: _app_options.AppOptions.get('enablePrintAutoRotate'),
+      useOnlyCssZoom: _app_options.AppOptions.get('useOnlyCssZoom'),
+      maxCanvasPixels: _app_options.AppOptions.get('maxCanvasPixels')
+    });
+    pdfRenderingQueue.setViewer(this.pdfViewer);
+    pdfLinkService.setViewer(this.pdfViewer);
+    let thumbnailContainer = appConfig.sidebar.thumbnailView;
+    this.pdfThumbnailViewer = new _pdf_thumbnail_viewer.PDFThumbnailViewer({
+      container: thumbnailContainer,
+      renderingQueue: pdfRenderingQueue,
+      linkService: pdfLinkService,
+      l10n: this.l10n
+    });
+    pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
+    this.pdfHistory = new _pdf_history.PDFHistory({
+      linkService: pdfLinkService,
+      eventBus
+    });
+    pdfLinkService.setHistory(this.pdfHistory);
+    this.findController = new _pdf_find_controller.PDFFindController({
+      pdfViewer: this.pdfViewer,
+      eventBus
+    });
+    this.findController.onUpdateResultsCount = matchesCount => {
+      if (this.supportsIntegratedFind) {
+        this.externalServices.updateFindMatchesCount(matchesCount);
+      } else {
+        this.findBar.updateResultsCount(matchesCount);
+      }
+    };
+    this.findController.onUpdateState = (state, previous, matchesCount) => {
+      if (this.supportsIntegratedFind) {
+        this.externalServices.updateFindControlState({
+          result: state,
+          findPrevious: previous,
+          matchesCount
+        });
+      } else {
+        this.findBar.updateUIState(state, previous, matchesCount);
+      }
+    };
+    this.pdfViewer.setFindController(this.findController);
+    let findBarConfig = Object.create(appConfig.findBar);
+    findBarConfig.findController = this.findController;
+    findBarConfig.eventBus = eventBus;
+    this.findBar = new _pdf_find_bar.PDFFindBar(findBarConfig, this.l10n);
+    this.pdfDocumentProperties = new _pdf_document_properties.PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, this.l10n);
+    this.pdfCursorTools = new _pdf_cursor_tools.PDFCursorTools({
+      container,
+      eventBus,
+      cursorToolOnLoad: _app_options.AppOptions.get('cursorToolOnLoad')
+    });
+    this.toolbar = new _toolbar.Toolbar(appConfig.toolbar, container, eventBus, this.l10n);
+    this.secondaryToolbar = new _secondary_toolbar.SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus);
+    if (this.supportsFullscreen) {
+      this.pdfPresentationMode = new _pdf_presentation_mode.PDFPresentationMode({
         container,
         viewer,
+        pdfViewer: this.pdfViewer,
         eventBus,
-        renderingQueue: pdfRenderingQueue,
-        linkService: pdfLinkService,
-        downloadManager,
-        renderer: _app_options.AppOptions.get('renderer'),
-        enableWebGL: _app_options.AppOptions.get('enableWebGL'),
-        l10n: this.l10n,
-        textLayerMode: _app_options.AppOptions.get('textLayerMode'),
-        imageResourcesPath: _app_options.AppOptions.get('imageResourcesPath'),
-        renderInteractiveForms: _app_options.AppOptions.get('renderInteractiveForms'),
-        enablePrintAutoRotate: _app_options.AppOptions.get('enablePrintAutoRotate'),
-        useOnlyCssZoom: _app_options.AppOptions.get('useOnlyCssZoom'),
-        maxCanvasPixels: _app_options.AppOptions.get('maxCanvasPixels')
-      });
-      pdfRenderingQueue.setViewer(this.pdfViewer);
-      pdfLinkService.setViewer(this.pdfViewer);
-      let thumbnailContainer = appConfig.sidebar.thumbnailView;
-      this.pdfThumbnailViewer = new _pdf_thumbnail_viewer.PDFThumbnailViewer({
-        container: thumbnailContainer,
-        renderingQueue: pdfRenderingQueue,
-        linkService: pdfLinkService,
-        l10n: this.l10n
-      });
-      pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
-      this.pdfHistory = new _pdf_history.PDFHistory({
-        linkService: pdfLinkService,
-        eventBus
-      });
-      pdfLinkService.setHistory(this.pdfHistory);
-      this.findController = new _pdf_find_controller.PDFFindController({
-        pdfViewer: this.pdfViewer,
-        eventBus
+        contextMenuItems: appConfig.fullscreen
       });
-      this.findController.onUpdateResultsCount = matchCount => {
-        if (this.supportsIntegratedFind) {
-          return;
-        }
-        this.findBar.updateResultsCount(matchCount);
-      };
-      this.findController.onUpdateState = (state, previous, matchCount) => {
-        if (this.supportsIntegratedFind) {
-          this.externalServices.updateFindControlState({
-            result: state,
-            findPrevious: previous
-          });
-        } else {
-          this.findBar.updateUIState(state, previous, matchCount);
-        }
-      };
-      this.pdfViewer.setFindController(this.findController);
-      let findBarConfig = Object.create(appConfig.findBar);
-      findBarConfig.findController = this.findController;
-      findBarConfig.eventBus = eventBus;
-      this.findBar = new _pdf_find_bar.PDFFindBar(findBarConfig, this.l10n);
-      this.pdfDocumentProperties = new _pdf_document_properties.PDFDocumentProperties(appConfig.documentProperties, this.overlayManager, eventBus, this.l10n);
-      this.pdfCursorTools = new _pdf_cursor_tools.PDFCursorTools({
-        container,
-        eventBus,
-        cursorToolOnLoad: _app_options.AppOptions.get('cursorToolOnLoad')
-      });
-      this.toolbar = new _toolbar.Toolbar(appConfig.toolbar, container, eventBus, this.l10n);
-      this.secondaryToolbar = new _secondary_toolbar.SecondaryToolbar(appConfig.secondaryToolbar, container, eventBus);
-      if (this.supportsFullscreen) {
-        this.pdfPresentationMode = new _pdf_presentation_mode.PDFPresentationMode({
-          container,
-          viewer,
-          pdfViewer: this.pdfViewer,
-          eventBus,
-          contextMenuItems: appConfig.fullscreen
-        });
-      }
-      this.passwordPrompt = new _password_prompt.PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.l10n);
-      this.pdfOutlineViewer = new _pdf_outline_viewer.PDFOutlineViewer({
-        container: appConfig.sidebar.outlineView,
-        eventBus,
-        linkService: pdfLinkService
-      });
-      this.pdfAttachmentViewer = new _pdf_attachment_viewer.PDFAttachmentViewer({
-        container: appConfig.sidebar.attachmentsView,
-        eventBus,
-        downloadManager
-      });
-      let sidebarConfig = Object.create(appConfig.sidebar);
-      sidebarConfig.pdfViewer = this.pdfViewer;
-      sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer;
-      sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer;
-      sidebarConfig.eventBus = eventBus;
-      this.pdfSidebar = new _pdf_sidebar.PDFSidebar(sidebarConfig, this.l10n);
-      this.pdfSidebar.onToggled = this.forceRendering.bind(this);
-      this.pdfSidebarResizer = new _pdf_sidebar_resizer.PDFSidebarResizer(appConfig.sidebarResizer, eventBus, this.l10n);
-      resolve(undefined);
-    });
+    }
+    this.passwordPrompt = new _password_prompt.PasswordPrompt(appConfig.passwordOverlay, this.overlayManager, this.l10n);
+    this.pdfOutlineViewer = new _pdf_outline_viewer.PDFOutlineViewer({
+      container: appConfig.sidebar.outlineView,
+      eventBus,
+      linkService: pdfLinkService
+    });
+    this.pdfAttachmentViewer = new _pdf_attachment_viewer.PDFAttachmentViewer({
+      container: appConfig.sidebar.attachmentsView,
+      eventBus,
+      downloadManager
+    });
+    let sidebarConfig = Object.create(appConfig.sidebar);
+    sidebarConfig.pdfViewer = this.pdfViewer;
+    sidebarConfig.pdfThumbnailViewer = this.pdfThumbnailViewer;
+    sidebarConfig.pdfOutlineViewer = this.pdfOutlineViewer;
+    sidebarConfig.eventBus = eventBus;
+    this.pdfSidebar = new _pdf_sidebar.PDFSidebar(sidebarConfig, this.l10n);
+    this.pdfSidebar.onToggled = this.forceRendering.bind(this);
+    this.pdfSidebarResizer = new _pdf_sidebar_resizer.PDFSidebarResizer(appConfig.sidebarResizer, eventBus, this.l10n);
   },
   run(config) {
     this.initialize(config).then(webViewerInitialized);
   },
   zoomIn(ticks) {
     let newScale = this.pdfViewer.currentScale;
     do {
       newScale = (newScale * DEFAULT_SCALE_DELTA).toFixed(2);
@@ -698,21 +695,21 @@ let PDFViewerApplication = {
     this.setTitle(title);
   },
   setTitle(title) {
     if (this.isViewerEmbedded) {
       return;
     }
     document.title = title;
   },
-  close() {
+  async close() {
     let errorWrapper = this.appConfig.errorWrapper.container;
     errorWrapper.setAttribute('hidden', 'true');
     if (!this.pdfLoadingTask) {
-      return Promise.resolve();
+      return;
     }
     let promise = this.pdfLoadingTask.destroy();
     this.pdfLoadingTask = null;
     if (this.pdfDocument) {
       this.pdfDocument = null;
       this.pdfThumbnailViewer.setDocument(null);
       this.pdfViewer.setDocument(null);
       this.pdfLinkService.setDocument(null, null);
@@ -731,21 +728,19 @@ let PDFViewerApplication = {
     this.findBar.reset();
     this.toolbar.reset();
     this.secondaryToolbar.reset();
     if (typeof PDFBug !== 'undefined') {
       PDFBug.cleanup();
     }
     return promise;
   },
-  open(file, args) {
+  async open(file, args) {
     if (this.pdfLoadingTask) {
-      return this.close().then(() => {
-        return this.open(file, args);
-      });
+      await this.close();
     }
     const workerParameters = _app_options.AppOptions.getAll('worker');
     for (let key in workerParameters) {
       _pdfjsLib.GlobalWorkerOptions[key] = workerParameters[key];
     }
     let parameters = Object.create(null);
     if (typeof file === 'string') {
       this.setTitleUsingUrl(file);
@@ -910,80 +905,66 @@ let PDFViewerApplication = {
       if (!_app_options.AppOptions.get('disableHistory') && !this.isViewerEmbedded) {
         let resetHistory = !_app_options.AppOptions.get('showPreviousViewOnLoad');
         this.pdfHistory.initialize(pdfDocument.fingerprint, resetHistory);
         if (this.pdfHistory.initialBookmark) {
           this.initialBookmark = this.pdfHistory.initialBookmark;
           this.initialRotation = this.pdfHistory.initialRotation;
         }
       }
-      let initialParams = {
-        bookmark: null,
-        hash: null
-      };
       let storePromise = store.getMultiple({
         page: null,
         zoom: _ui_utils.DEFAULT_SCALE_VALUE,
         scrollLeft: '0',
         scrollTop: '0',
         rotation: null,
         sidebarView: _pdf_sidebar.SidebarView.NONE,
         scrollMode: null,
         spreadMode: null
       }).catch(() => {});
-      Promise.all([storePromise, pageModePromise]).then(([values = {}, pageMode]) => {
+      Promise.all([storePromise, pageModePromise]).then(async ([values = {}, pageMode]) => {
+        const initialBookmark = this.initialBookmark;
         const zoom = _app_options.AppOptions.get('defaultZoomValue');
         let hash = zoom ? `zoom=${zoom}` : null;
         let rotation = null;
         let sidebarView = _app_options.AppOptions.get('sidebarViewOnLoad');
         let scrollMode = _app_options.AppOptions.get('scrollModeOnLoad');
         let spreadMode = _app_options.AppOptions.get('spreadModeOnLoad');
         if (values.page && _app_options.AppOptions.get('showPreviousViewOnLoad')) {
           hash = 'page=' + values.page + '&zoom=' + (zoom || values.zoom) + ',' + values.scrollLeft + ',' + values.scrollTop;
           rotation = parseInt(values.rotation, 10);
           sidebarView = sidebarView || values.sidebarView | 0;
           scrollMode = scrollMode || values.scrollMode | 0;
           spreadMode = spreadMode || values.spreadMode | 0;
         }
         if (pageMode && !_app_options.AppOptions.get('disablePageMode')) {
           sidebarView = sidebarView || apiPageModeToSidebarView(pageMode);
         }
-        return {
-          hash,
-          rotation,
-          sidebarView,
-          scrollMode,
-          spreadMode
-        };
-      }).then(({ hash, rotation, sidebarView, scrollMode, spreadMode }) => {
-        initialParams.bookmark = this.initialBookmark;
-        initialParams.hash = hash;
         this.setInitialView(hash, {
           rotation,
           sidebarView,
           scrollMode,
           spreadMode
         });
         this.eventBus.dispatch('documentinit', { source: this });
         if (!this.isViewerEmbedded) {
           pdfViewer.focus();
         }
-        return Promise.race([pagesPromise, new Promise(resolve => {
+        await Promise.race([pagesPromise, new Promise(resolve => {
           setTimeout(resolve, FORCE_PAGES_LOADED_TIMEOUT);
         })]);
-      }).then(() => {
-        if (!initialParams.bookmark && !initialParams.hash) {
+        if (!initialBookmark && !hash) {
           return;
         }
         if (pdfViewer.hasEqualPageSizes) {
           return;
         }
-        this.initialBookmark = initialParams.bookmark;
+        this.initialBookmark = initialBookmark;
         pdfViewer.currentScaleValue = pdfViewer.currentScaleValue;
-        this.setInitialView(initialParams.hash);
+        this.setInitialView(hash);
       }).then(function () {
         pdfViewer.update();
       });
     });
     pdfDocument.getPageLabels().then(labels => {
       if (!labels || _app_options.AppOptions.get('disablePageLabels')) {
         return;
       }
@@ -1340,23 +1321,23 @@ function webViewerInitialized() {
   appConfig.mainContainer.addEventListener('transitionend', function (evt) {
     if (evt.target === this) {
       PDFViewerApplication.eventBus.dispatch('resize', { source: this });
     }
   }, true);
   appConfig.sidebar.toggleButton.addEventListener('click', function () {
     PDFViewerApplication.pdfSidebar.toggle();
   });
-  Promise.resolve().then(function () {
+  try {
     webViewerOpenFileViaURL(file);
-  }).catch(function (reason) {
+  } catch (reason) {
     PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(msg => {
       PDFViewerApplication.error(msg, reason);
     });
-  });
+  }
 }
 let webViewerOpenFileViaURL;
 {
   webViewerOpenFileViaURL = function webViewerOpenFileViaURL(file) {
     PDFViewerApplication.setTitleUsingUrl(file);
     PDFViewerApplication.initPassiveLoading();
   };
 }
@@ -1557,25 +1538,27 @@ function webViewerSwitchSpreadMode(evt) 
 function webViewerDocumentProperties() {
   PDFViewerApplication.pdfDocumentProperties.open();
 }
 function webViewerFind(evt) {
   PDFViewerApplication.findController.executeCommand('find' + evt.type, {
     query: evt.query,
     phraseSearch: evt.phraseSearch,
     caseSensitive: evt.caseSensitive,
+    entireWord: evt.entireWord,
     highlightAll: evt.highlightAll,
     findPrevious: evt.findPrevious
   });
 }
 function webViewerFindFromUrlHash(evt) {
   PDFViewerApplication.findController.executeCommand('find', {
     query: evt.query,
     phraseSearch: evt.phraseSearch,
     caseSensitive: false,
+    entireWord: false,
     highlightAll: true,
     findPrevious: false
   });
 }
 function webViewerScaleChanging(evt) {
   PDFViewerApplication.toolbar.setPageScale(evt.presetValue, evt.scale);
   PDFViewerApplication.pdfViewer.update();
 }
@@ -1669,16 +1652,17 @@ function webViewerKeyDown(evt) {
       case 71:
         if (!PDFViewerApplication.supportsIntegratedFind) {
           let findState = PDFViewerApplication.findController.state;
           if (findState) {
             PDFViewerApplication.findController.executeCommand('findagain', {
               query: findState.query,
               phraseSearch: findState.phraseSearch,
               caseSensitive: findState.caseSensitive,
+              entireWord: findState.entireWord,
               highlightAll: findState.highlightAll,
               findPrevious: cmd === 5 || cmd === 12
             });
           }
           handled = true;
         }
         break;
       case 61:
@@ -1936,28 +1920,26 @@ function formatL10nValue(text, args) {
   if (!args) {
     return text;
   }
   return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => {
     return name in args ? args[name] : '{{' + name + '}}';
   });
 }
 let NullL10n = {
-  getLanguage() {
-    return Promise.resolve('en-us');
-  },
-  getDirection() {
-    return Promise.resolve('ltr');
+  async getLanguage() {
+    return 'en-us';
   },
-  get(property, args, fallback) {
-    return Promise.resolve(formatL10nValue(fallback, args));
+  async getDirection() {
+    return 'ltr';
   },
-  translate(element) {
-    return Promise.resolve();
-  }
+  async get(property, args, fallback) {
+    return formatL10nValue(fallback, args);
+  },
+  async translate(element) {}
 };
 function getOutputScale(ctx) {
   let devicePixelRatio = window.devicePixelRatio || 1;
   let backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.msBackingStorePixelRatio || ctx.oBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
   let pixelRatio = devicePixelRatio / backingStoreRatio;
   return {
     sx: pixelRatio,
     sy: pixelRatio,
@@ -3475,79 +3457,67 @@ class OverlayManager {
   constructor() {
     this._overlays = {};
     this._active = null;
     this._keyDownBound = this._keyDown.bind(this);
   }
   get active() {
     return this._active;
   }
-  register(name, element, callerCloseMethod = null, canForceClose = false) {
-    return new Promise(resolve => {
-      let container;
-      if (!name || !element || !(container = element.parentNode)) {
-        throw new Error('Not enough parameters.');
-      } else if (this._overlays[name]) {
-        throw new Error('The overlay is already registered.');
-      }
-      this._overlays[name] = {
-        element,
-        container,
-        callerCloseMethod,
-        canForceClose
-      };
-      resolve();
-    });
-  }
-  unregister(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
+  async register(name, element, callerCloseMethod = null, canForceClose = false) {
+    let container;
+    if (!name || !element || !(container = element.parentNode)) {
+      throw new Error('Not enough parameters.');
+    } else if (this._overlays[name]) {
+      throw new Error('The overlay is already registered.');
+    }
+    this._overlays[name] = {
+      element,
+      container,
+      callerCloseMethod,
+      canForceClose
+    };
+  }
+  async unregister(name) {
+    if (!this._overlays[name]) {
+      throw new Error('The overlay does not exist.');
+    } else if (this._active === name) {
+      throw new Error('The overlay cannot be removed while it is active.');
+    }
+    delete this._overlays[name];
+  }
+  async open(name) {
+    if (!this._overlays[name]) {
+      throw new Error('The overlay does not exist.');
+    } else if (this._active) {
+      if (this._overlays[name].canForceClose) {
+        this._closeThroughCaller();
       } else if (this._active === name) {
-        throw new Error('The overlay cannot be removed while it is active.');
-      }
-      delete this._overlays[name];
-      resolve();
-    });
-  }
-  open(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
-      } else if (this._active) {
-        if (this._overlays[name].canForceClose) {
-          this._closeThroughCaller();
-        } else if (this._active === name) {
-          throw new Error('The overlay is already active.');
-        } else {
-          throw new Error('Another overlay is currently active.');
-        }
-      }
-      this._active = name;
-      this._overlays[this._active].element.classList.remove('hidden');
-      this._overlays[this._active].container.classList.remove('hidden');
-      window.addEventListener('keydown', this._keyDownBound);
-      resolve();
-    });
-  }
-  close(name) {
-    return new Promise(resolve => {
-      if (!this._overlays[name]) {
-        throw new Error('The overlay does not exist.');
-      } else if (!this._active) {
-        throw new Error('The overlay is currently not active.');
-      } else if (this._active !== name) {
+        throw new Error('The overlay is already active.');
+      } else {
         throw new Error('Another overlay is currently active.');
       }
-      this._overlays[this._active].container.classList.add('hidden');
-      this._overlays[this._active].element.classList.add('hidden');
-      this._active = null;
-      window.removeEventListener('keydown', this._keyDownBound);
-      resolve();
-    });
+    }
+    this._active = name;
+    this._overlays[this._active].element.classList.remove('hidden');
+    this._overlays[this._active].container.classList.remove('hidden');
+    window.addEventListener('keydown', this._keyDownBound);
+  }
+  async close(name) {
+    if (!this._overlays[name]) {
+      throw new Error('The overlay does not exist.');
+    } else if (!this._active) {
+      throw new Error('The overlay is currently not active.');
+    } else if (this._active !== name) {
+      throw new Error('Another overlay is currently active.');
+    }
+    this._overlays[this._active].container.classList.add('hidden');
+    this._overlays[this._active].element.classList.add('hidden');
+    this._active = null;
+    window.removeEventListener('keydown', this._keyDownBound);
   }
   _keyDown(evt) {
     if (this._active && evt.keyCode === 27) {
       this._closeThroughCaller();
       evt.preventDefault();
     }
   }
   _closeThroughCaller() {
@@ -4017,27 +3987,28 @@ Object.defineProperty(exports, "__esModu
   value: true
 });
 exports.PDFFindBar = undefined;
 
 var _pdf_find_controller = __webpack_require__(16);
 
 var _ui_utils = __webpack_require__(2);
 
+const MATCHES_COUNT_LIMIT = 1000;
 class PDFFindBar {
   constructor(options, l10n = _ui_utils.NullL10n) {
     this.opened = false;
     this.bar = options.bar || null;
     this.toggleButton = options.toggleButton || null;
     this.findField = options.findField || null;
     this.highlightAll = options.highlightAllCheckbox || null;
     this.caseSensitive = options.caseSensitiveCheckbox || null;
+    this.entireWord = options.entireWordCheckbox || null;
     this.findMsg = options.findMsg || null;
     this.findResultsCount = options.findResultsCount || null;
-    this.findStatusIcon = options.findStatusIcon || null;
     this.findPreviousButton = options.findPreviousButton || null;
     this.findNextButton = options.findNextButton || null;
     this.findController = options.findController || null;
     this.eventBus = options.eventBus;
     this.l10n = l10n;
     if (this.findController === null) {
       throw new Error('PDFFindBar cannot be used without a ' + 'PDFFindController instance.');
     }
@@ -4066,33 +4037,37 @@ class PDFFindBar {
       this.dispatchEvent('again', false);
     });
     this.highlightAll.addEventListener('click', () => {
       this.dispatchEvent('highlightallchange');
     });
     this.caseSensitive.addEventListener('click', () => {
       this.dispatchEvent('casesensitivitychange');
     });
+    this.entireWord.addEventListener('click', () => {
+      this.dispatchEvent('entirewordchange');
+    });
     this.eventBus.on('resize', this._adjustWidth.bind(this));
   }
   reset() {
     this.updateUIState();
   }
   dispatchEvent(type, findPrev) {
     this.eventBus.dispatch('find', {
       source: this,
       type,
       query: this.findField.value,
+      phraseSearch: true,
       caseSensitive: this.caseSensitive.checked,
-      phraseSearch: true,
+      entireWord: this.entireWord.checked,
       highlightAll: this.highlightAll.checked,
       findPrevious: findPrev
     });
   }
-  updateUIState(state, previous, matchCount) {
+  updateUIState(state, previous, matchesCount) {
     let notFound = false;
     let findMsg = '';
     let status = '';
     switch (state) {
       case _pdf_find_controller.FindState.FOUND:
         break;
       case _pdf_find_controller.FindState.PENDING:
         status = 'pending';
@@ -4114,30 +4089,38 @@ class PDFFindBar {
     } else {
       this.findField.classList.remove('notFound');
     }
     this.findField.setAttribute('data-status', status);
     Promise.resolve(findMsg).then(msg => {
       this.findMsg.textContent = msg;
       this._adjustWidth();
     });
-    this.updateResultsCount(matchCount);
-  }
-  updateResultsCount(matchCount) {
+    this.updateResultsCount(matchesCount);
+  }
+  updateResultsCount({ current = 0, total = 0 } = {}) {
     if (!this.findResultsCount) {
       return;
     }
-    if (!matchCount) {
-      this.findResultsCount.classList.add('hidden');
-      this.findResultsCount.textContent = '';
-    } else {
-      this.findResultsCount.textContent = matchCount.toLocaleString();
-      this.findResultsCount.classList.remove('hidden');
-    }
-    this._adjustWidth();
+    let matchesCountMsg = '';
+    if (total) {
+      if (total > MATCHES_COUNT_LIMIT) {
+        matchesCountMsg = this.l10n.get('find_matches_count_limit', { limit: MATCHES_COUNT_LIMIT.toLocaleString() }, 'More than {{limit}} matches');
+      } else {
+        matchesCountMsg = this.l10n.get('find_matches_count', {
+          current: current.toLocaleString(),
+          total: total.toLocaleString()
+        }, '{{current}} of {{total}} matches');
+      }
+    }
+    Promise.resolve(matchesCountMsg).then(msg => {
+      this.findResultsCount.textContent = msg;
+      this.findResultsCount.classList[!total ? 'add' : 'remove']('hidden');
+      this._adjustWidth();
+    });
   }
   open() {
     if (!this.opened) {
       this.opened = true;
       this.toggleButton.classList.add('toggled');
       this.bar.classList.remove('hidden');
     }
     this.findField.select();
@@ -4183,16 +4166,18 @@ exports.PDFFindBar = PDFFindBar;
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFFindController = exports.FindState = undefined;
 
 var _pdfjsLib = __webpack_require__(3);
 
+var _pdf_find_utils = __webpack_require__(17);
+
 var _dom_events = __webpack_require__(10);
 
 var _ui_utils = __webpack_require__(2);
 
 const FindState = {
   FOUND: 0,
   NOT_FOUND: 1,
   WRAPPED: 2,
@@ -4227,17 +4212,17 @@ class PDFFindController {
   reset() {
     this.startedTextExtraction = false;
     this.extractTextPromises = [];
     this.pendingFindMatches = Object.create(null);
     this.active = false;
     this.pageContents = [];
     this.pageMatches = [];
     this.pageMatchesLength = null;
-    this.matchCount = 0;
+    this.matchesCountTotal = 0;
     this.selected = {
       pageIdx: -1,
       matchIdx: -1
     };
     this.offset = {
       pageIdx: null,
       matchIdx: null
     };
@@ -4313,41 +4298,65 @@ class PDFFindController {
     for (let i = 0, len = matchesWithLength.length; i < len; i++) {
       if (isSubTerm(matchesWithLength, i)) {
         continue;
       }
       matches.push(matchesWithLength[i].match);
       matchesLength.push(matchesWithLength[i].matchLength);
     }
   }
-  _calculatePhraseMatch(query, pageIndex, pageContent) {
+  _isEntireWord(content, startIdx, length) {
+    if (startIdx > 0) {
+      const first = content.charCodeAt(startIdx);
+      const limit = content.charCodeAt(startIdx - 1);
+      if ((0, _pdf_find_utils.getCharacterType)(first) === (0, _pdf_find_utils.getCharacterType)(limit)) {
+        return false;
+      }
+    }
+    const endIdx = startIdx + length - 1;
+    if (endIdx < content.length - 1) {
+      const last = content.charCodeAt(endIdx);
+      const limit = content.charCodeAt(endIdx + 1);
+      if ((0, _pdf_find_utils.getCharacterType)(last) === (0, _pdf_find_utils.getCharacterType)(limit)) {
+        return false;
+      }
+    }
+    return true;
+  }
+  _calculatePhraseMatch(query, pageIndex, pageContent, entireWord) {
     let matches = [];
     let queryLen = query.length;
     let matchIdx = -queryLen;
     while (true) {
       matchIdx = pageContent.indexOf(query, matchIdx + queryLen);
       if (matchIdx === -1) {
         break;
       }
+      if (entireWord && !this._isEntireWord(pageContent, matchIdx, queryLen)) {
+        continue;
+      }
       matches.push(matchIdx);
     }
     this.pageMatches[pageIndex] = matches;
   }
-  _calculateWordMatch(query, pageIndex, pageContent) {
+  _calculateWordMatch(query, pageIndex, pageContent, entireWord) {
     let matchesWithLength = [];
     let queryArray = query.match(/\S+/g);
     for (let i = 0, len = queryArray.length; i < len; i++) {
       let subquery = queryArray[i];
       let subqueryLen = subquery.length;
       let matchIdx = -subqueryLen;
       while (true) {
         matchIdx = pageContent.indexOf(subquery, matchIdx + subqueryLen);
         if (matchIdx === -1) {
           break;
         }
+        if (entireWord && !this._isEntireWord(pageContent, matchIdx, subqueryLen)) {
+          continue;
+        }
         matchesWithLength.push({
           match: matchIdx,
           matchLength: subqueryLen,
           skipped: false
         });
       }
     }
     if (!this.pageMatchesLength) {
@@ -4357,36 +4366,38 @@ class PDFFindController {
     this.pageMatches[pageIndex] = [];
     this._prepareMatches(matchesWithLength, this.pageMatches[pageIndex], this.pageMatchesLength[pageIndex]);
   }
   _calculateMatch(pageIndex) {
     let pageContent = this._normalize(this.pageContents[pageIndex]);
     let query = this._normalize(this.state.query);
     let caseSensitive = this.state.caseSensitive;
     let phraseSearch = this.state.phraseSearch;
+    const entireWord = this.state.entireWord;
     let queryLen = query.length;
     if (queryLen === 0) {
       return;
     }
     if (!caseSensitive) {
       pageContent = pageContent.toLowerCase();
       query = query.toLowerCase();
     }
     if (phraseSearch) {
-      this._calculatePhraseMatch(query, pageIndex, pageContent);
+      this._calculatePhraseMatch(query, pageIndex, pageContent, entireWord);
     } else {
-      this._calculateWordMatch(query, pageIndex, pageContent);
+      this._calculateWordMatch(query, pageIndex, pageContent, entireWord);
     }
     this._updatePage(pageIndex);
     if (this.resumePageIdx === pageIndex) {
       this.resumePageIdx = null;
       this._nextPageMatch();
     }
-    if (this.pageMatches[pageIndex].length > 0) {
-      this.matchCount += this.pageMatches[pageIndex].length;
+    const pageMatchesCount = this.pageMatches[pageIndex].length;
+    if (pageMatchesCount > 0) {
+      this.matchesCountTotal += pageMatchesCount;
       this._updateUIResultsCount();
     }
   }
   _extractText() {
     if (this.startedTextExtraction) {
       return;
     }
     this.startedTextExtraction = true;
@@ -4429,17 +4440,17 @@ class PDFFindController {
     if (this.dirtyMatch) {
       this.dirtyMatch = false;
       this.selected.pageIdx = this.selected.matchIdx = -1;
       this.offset.pageIdx = currentPageIndex;
       this.offset.matchIdx = null;
       this.hadMatch = false;
       this.resumePageIdx = null;
       this.pageMatches = [];
-      this.matchCount = 0;
+      this.matchesCountTotal = 0;
       this.pageMatchesLength = null;
       for (let i = 0; i < numPages; i++) {
         this._updatePage(i);
         if (!(i in this.pendingFindMatches)) {
           this.pendingFindMatches[i] = true;
           this.extractTextPromises[i].then(pageIdx => {
             delete this.pendingFindMatches[pageIdx];
             this._calculateMatch(pageIdx);
@@ -4526,40 +4537,138 @@ class PDFFindController {
         this._updatePage(previousPage);
       }
     }
     this._updateUIState(state, this.state.findPrevious);
     if (this.selected.pageIdx !== -1) {
       this._updatePage(this.selected.pageIdx);
     }
   }
+  _requestMatchesCount() {
+    const { pageIdx, matchIdx } = this.selected;
+    let current = 0;
+    if (matchIdx !== -1) {
+      for (let i = 0; i < pageIdx; i++) {
+        current += this.pageMatches[i] && this.pageMatches[i].length || 0;
+      }
+      current += matchIdx + 1;
+    }
+    return {
+      current,
+      total: this.matchesCountTotal
+    };
+  }
   _updateUIResultsCount() {
-    if (this.onUpdateResultsCount) {
-      this.onUpdateResultsCount(this.matchCount);
-    }
+    if (!this.onUpdateResultsCount) {
+      return;
+    }
+    const matchesCount = this._requestMatchesCount();
+    this.onUpdateResultsCount(matchesCount);
   }
   _updateUIState(state, previous) {
-    if (this.onUpdateState) {
-      this.onUpdateState(state, previous, this.matchCount);
-    }
+    if (!this.onUpdateState) {
+      return;
+    }
+    const matchesCount = this._requestMatchesCount();
+    this.onUpdateState(state, previous, matchesCount);
   }
 }
 exports.FindState = FindState;
 exports.PDFFindController = PDFFindController;
 
 /***/ }),
 /* 17 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
+const CharacterType = {
+  SPACE: 0,
+  ALPHA_LETTER: 1,
+  PUNCT: 2,
+  HAN_LETTER: 3,
+  KATAKANA_LETTER: 4,
+  HIRAGANA_LETTER: 5,
+  HALFWIDTH_KATAKANA_LETTER: 6,
+  THAI_LETTER: 7
+};
+function isAlphabeticalScript(charCode) {
+  return charCode < 0x2E80;
+}
+function isAscii(charCode) {
+  return (charCode & 0xFF80) === 0;
+}
+function isAsciiAlpha(charCode) {
+  return charCode >= 0x61 && charCode <= 0x7A || charCode >= 0x41 && charCode <= 0x5A;
+}
+function isAsciiDigit(charCode) {
+  return charCode >= 0x30 && charCode <= 0x39;
+}
+function isAsciiSpace(charCode) {
+  return charCode === 0x20 || charCode === 0x09 || charCode === 0x0D || charCode === 0x0A;
+}
+function isHan(charCode) {
+  return charCode >= 0x3400 && charCode <= 0x9FFF || charCode >= 0xF900 && charCode <= 0xFAFF;
+}
+function isKatakana(charCode) {
+  return charCode >= 0x30A0 && charCode <= 0x30FF;
+}
+function isHiragana(charCode) {
+  return charCode >= 0x3040 && charCode <= 0x309F;
+}
+function isHalfwidthKatakana(charCode) {
+  return charCode >= 0xFF60 && charCode <= 0xFF9F;
+}
+function isThai(charCode) {
+  return (charCode & 0xFF80) === 0x0E00;
+}
+function getCharacterType(charCode) {
+  if (isAlphabeticalScript(charCode)) {
+    if (isAscii(charCode)) {
+      if (isAsciiSpace(charCode)) {
+        return CharacterType.SPACE;
+      } else if (isAsciiAlpha(charCode) || isAsciiDigit(charCode) || charCode === 0x5F) {
+        return CharacterType.ALPHA_LETTER;
+      }
+      return CharacterType.PUNCT;
+    } else if (isThai(charCode)) {
+      return CharacterType.THAI_LETTER;
+    } else if (charCode === 0xA0) {
+      return CharacterType.SPACE;
+    }
+    return CharacterType.ALPHA_LETTER;
+  }
+  if (isHan(charCode)) {
+    return CharacterType.HAN_LETTER;
+  } else if (isKatakana(charCode)) {
+    return CharacterType.KATAKANA_LETTER;
+  } else if (isHiragana(charCode)) {
+    return CharacterType.HIRAGANA_LETTER;
+  } else if (isHalfwidthKatakana(charCode)) {
+    return CharacterType.HALFWIDTH_KATAKANA_LETTER;
+  }
+  return CharacterType.ALPHA_LETTER;
+}
+exports.CharacterType = CharacterType;
+exports.getCharacterType = getCharacterType;
+
+/***/ }),
+/* 18 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, "__esModule", {
+  value: true
+});
 exports.isDestArraysEqual = exports.isDestHashesEqual = exports.PDFHistory = undefined;
 
 var _ui_utils = __webpack_require__(2);
 
 var _dom_events = __webpack_require__(10);
 
 const HASH_CHANGE_TIMEOUT = 1000;
 const POSITION_UPDATED_THRESHOLD = 50;
@@ -4918,17 +5027,17 @@ function isDestArraysEqual(firstDest, se
   }
   return true;
 }
 exports.PDFHistory = PDFHistory;
 exports.isDestHashesEqual = isDestHashesEqual;
 exports.isDestArraysEqual = isDestArraysEqual;
 
 /***/ }),
-/* 18 */
+/* 19 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -5252,17 +5361,17 @@ class SimpleLinkService {
   executeNamedAction(action) {}
   onFileAttachmentAnnotation({ id, filename, content }) {}
   cachePageRef(pageNum, pageRef) {}
 }
 exports.PDFLinkService = PDFLinkService;
 exports.SimpleLinkService = SimpleLinkService;
 
 /***/ }),
-/* 19 */
+/* 20 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -5393,17 +5502,17 @@ class PDFOutlineViewer {
     }
     this.container.appendChild(fragment);
     this._dispatchEvent(outlineCount);
   }
 }
 exports.PDFOutlineViewer = PDFOutlineViewer;
 
 /***/ }),
-/* 20 */
+/* 21 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -5710,17 +5819,17 @@ class PDFPresentationMode {
     window.removeEventListener('fullscreenchange', this.fullscreenChangeBind);
     window.removeEventListener('mozfullscreenchange', this.fullscreenChangeBind);
     delete this.fullscreenChangeBind;
   }
 }
 exports.PDFPresentationMode = PDFPresentationMode;
 
 /***/ }),
-/* 21 */
+/* 22 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -5830,30 +5939,30 @@ class PDFSidebarResizer {
         }
       }
     });
   }
 }
 exports.PDFSidebarResizer = PDFSidebarResizer;
 
 /***/ }),
-/* 22 */
+/* 23 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFThumbnailViewer = undefined;
 
 var _ui_utils = __webpack_require__(2);
 
-var _pdf_thumbnail_view = __webpack_require__(23);
+var _pdf_thumbnail_view = __webpack_require__(24);
 
 const THUMBNAIL_SCROLL_MARGIN = -19;
 const THUMBNAIL_SELECTED_CLASS = 'selected';
 class PDFThumbnailViewer {
   constructor({ container, linkService, renderingQueue, l10n = _ui_utils.NullL10n }) {
     this.container = container;
     this.linkService = linkService;
     this.renderingQueue = renderingQueue;
@@ -6019,17 +6128,17 @@ class PDFThumbnailViewer {
       return true;
     }
     return false;
   }
 }
 exports.PDFThumbnailViewer = PDFThumbnailViewer;
 
 /***/ }),
-/* 23 */
+/* 24 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -6327,28 +6436,28 @@ class PDFThumbnailView {
   }
   static cleanup() {
     TempImageFactory.destroyCanvas();
   }
 }
 exports.PDFThumbnailView = PDFThumbnailView;
 
 /***/ }),
-/* 24 */
+/* 25 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFViewer = undefined;
 
-var _base_viewer = __webpack_require__(25);
+var _base_viewer = __webpack_require__(26);
 
 var _ui_utils = __webpack_require__(2);
 
 var _pdfjsLib = __webpack_require__(3);
 
 class PDFViewer extends _base_viewer.BaseViewer {
   get _setDocumentViewerElement() {
     return (0, _pdfjsLib.shadow)(this, '_setDocumentViewerElement', this.viewer);
@@ -6417,42 +6526,42 @@ class PDFViewer extends _base_viewer.Bas
   }
   get _isScrollModeHorizontal() {
     return this.isInPresentationMode ? false : this._scrollMode === _base_viewer.ScrollMode.HORIZONTAL;
   }
 }
 exports.PDFViewer = PDFViewer;
 
 /***/ }),
-/* 25 */
+/* 26 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.SpreadMode = exports.ScrollMode = exports.BaseViewer = undefined;
 
 var _ui_utils = __webpack_require__(2);
 
 var _pdf_rendering_queue = __webpack_require__(6);
 
-var _annotation_layer_builder = __webpack_require__(26);
+var _annotation_layer_builder = __webpack_require__(27);
 
 var _pdfjsLib = __webpack_require__(3);
 
 var _dom_events = __webpack_require__(10);
 
-var _pdf_page_view = __webpack_require__(27);
-
-var _pdf_link_service = __webpack_require__(18);
-
-var _text_layer_builder = __webpack_require__(28);
+var _pdf_page_view = __webpack_require__(28);
+
+var _pdf_link_service = __webpack_require__(19);
+
+var _text_layer_builder = __webpack_require__(29);
 
 const DEFAULT_CACHE_SIZE = 10;
 const ScrollMode = {
   VERTICAL: 0,
   HORIZONTAL: 1,
   WRAPPED: 2
 };
 const SpreadMode = {
@@ -7225,32 +7334,32 @@ class BaseViewer {
     this.update();
   }
 }
 exports.BaseViewer = BaseViewer;
 exports.ScrollMode = ScrollMode;
 exports.SpreadMode = SpreadMode;
 
 /***/ }),
-/* 26 */
+/* 27 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.DefaultAnnotationLayerFactory = exports.AnnotationLayerBuilder = undefined;
 
 var _pdfjsLib = __webpack_require__(3);
 
 var _ui_utils = __webpack_require__(2);
 
-var _pdf_link_service = __webpack_require__(18);
+var _pdf_link_service = __webpack_require__(19);
 
 class AnnotationLayerBuilder {
   constructor({ pageDiv, pdfPage, linkService, downloadManager, imageResourcesPath = '', renderInteractiveForms = false, l10n = _ui_utils.NullL10n }) {
     this.pageDiv = pageDiv;
     this.pdfPage = pdfPage;
     this.linkService = linkService;
     this.downloadManager = downloadManager;
     this.imageResourcesPath = imageResourcesPath;
@@ -7310,17 +7419,17 @@ class DefaultAnnotationLayerFactory {
       l10n
     });
   }
 }
 exports.AnnotationLayerBuilder = AnnotationLayerBuilder;
 exports.DefaultAnnotationLayerFactory = DefaultAnnotationLayerFactory;
 
 /***/ }),
-/* 27 */
+/* 28 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -7767,17 +7876,17 @@ class PDFPageView {
     } else {
       this.div.removeAttribute('data-page-label');
     }
   }
 }
 exports.PDFPageView = PDFPageView;
 
 /***/ }),
-/* 28 */
+/* 29 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -8036,32 +8145,32 @@ class DefaultTextLayerFactory {
       enhanceTextSelection
     });
   }
 }
 exports.TextLayerBuilder = TextLayerBuilder;
 exports.DefaultTextLayerFactory = DefaultTextLayerFactory;
 
 /***/ }),
-/* 29 */
+/* 30 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.SecondaryToolbar = undefined;
 
-var _base_viewer = __webpack_require__(25);
+var _base_viewer = __webpack_require__(26);
 
 var _pdf_cursor_tools = __webpack_require__(4);
 
-var _pdf_single_page_viewer = __webpack_require__(30);
+var _pdf_single_page_viewer = __webpack_require__(31);
 
 var _ui_utils = __webpack_require__(2);
 
 class SecondaryToolbar {
   constructor(options, mainContainer, eventBus) {
     this.toolbar = options.toolbar;
     this.toggleButton = options.toggleButton;
     this.toolbarButtonContainer = options.toolbarButtonContainer;
@@ -8314,28 +8423,28 @@ class SecondaryToolbar {
     }
     this.toolbarButtonContainer.setAttribute('style', 'max-height: ' + (this.containerHeight - _ui_utils.SCROLLBAR_PADDING) + 'px;');
     this.previousContainerHeight = this.containerHeight;
   }
 }
 exports.SecondaryToolbar = SecondaryToolbar;
 
 /***/ }),
-/* 30 */
+/* 31 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFSinglePageViewer = undefined;
 
-var _base_viewer = __webpack_require__(25);
+var _base_viewer = __webpack_require__(26);
 
 var _ui_utils = __webpack_require__(2);
 
 var _pdfjsLib = __webpack_require__(3);
 
 class PDFSinglePageViewer extends _base_viewer.BaseViewer {
   constructor(options) {
     super(options);
@@ -8439,17 +8548,17 @@ class PDFSinglePageViewer extends _base_
     return (0, _pdfjsLib.shadow)(this, '_isScrollModeHorizontal', false);
   }
   _updateScrollMode() {}
   _updateSpreadMode() {}
 }
 exports.PDFSinglePageViewer = PDFSinglePageViewer;
 
 /***/ }),
-/* 31 */
+/* 32 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -8624,17 +8733,17 @@ class Toolbar {
         container.setAttribute('style', 'min-width: ' + width + 'px; ' + 'max-width: ' + width + 'px;');
       }
     });
   }
 }
 exports.Toolbar = Toolbar;
 
 /***/ }),
-/* 32 */
+/* 33 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -8662,78 +8771,69 @@ class ViewHistory {
       }
       if (index === -1) {
         index = database.files.push({ fingerprint: this.fingerprint }) - 1;
       }
       this.file = database.files[index];
       this.database = database;
     });
   }
-  _writeToStorage() {
-    return new Promise(resolve => {
-      let databaseStr = JSON.stringify(this.database);
-      sessionStorage.setItem('pdfjs.history', databaseStr);
-      resolve();
-    });
-  }
-  _readFromStorage() {
-    return new Promise(function (resolve) {
-      resolve(sessionStorage.getItem('pdfjs.history'));
-    });
-  }
-  set(name, val) {
-    return this._initializedPromise.then(() => {
-      this.file[name] = val;
-      return this._writeToStorage();
-    });
-  }
-  setMultiple(properties) {
-    return this._initializedPromise.then(() => {
-      for (let name in properties) {
-        this.file[name] = properties[name];
-      }
-      return this._writeToStorage();
-    });
-  }
-  get(name, defaultValue) {
-    return this._initializedPromise.then(() => {
+  async _writeToStorage() {
+    let databaseStr = JSON.stringify(this.database);
+    sessionStorage.setItem('pdfjs.history', databaseStr);
+  }
+  async _readFromStorage() {
+    return sessionStorage.getItem('pdfjs.history');
+  }
+  async set(name, val) {
+    await this._initializedPromise;
+    this.file[name] = val;
+    return this._writeToStorage();
+  }
+  async setMultiple(properties) {
+    await this._initializedPromise;
+    for (let name in properties) {
+      this.file[name] = properties[name];
+    }
+    return this._writeToStorage();
+  }
+  async get(name, defaultValue) {
+    await this._initializedPromise;
+    let val = this.file[name];
+    return val !== undefined ? val : defaultValue;
+  }
+  async getMultiple(properties) {
+    await this._initializedPromise;
+    let values = Object.create(null);
+    for (let name in properties) {
       let val = this.file[name];
-      return val !== undefined ? val : defaultValue;
-    });
-  }
-  getMultiple(properties) {
-    return this._initializedPromise.then(() => {
-      let values = Object.create(null);
-      for (let name in properties) {
-        let val = this.file[name];
-        values[name] = val !== undefined ? val : properties[name];
-      }
-      return values;
-    });
+      values[name] = val !== undefined ? val : properties[name];
+    }
+    return values;
   }
 }
 exports.ViewHistory = ViewHistory;
 
 /***/ }),
-/* 33 */
+/* 34 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.FirefoxCom = exports.DownloadManager = undefined;
 
-__webpack_require__(34);
+__webpack_require__(35);
 
 var _pdfjsLib = __webpack_require__(3);
 
-var _preferences = __webpack_require__(35);
+var _preferences = __webpack_require__(36);
 
 var _app = __webpack_require__(1);
 
 ;
 let FirefoxCom = function FirefoxComClosure() {
   return {
     requestSync(action, data) {
       let request = document.createTextNode('');
@@ -8802,66 +8902,66 @@ class DownloadManager {
     FirefoxCom.request('download', {
       blobUrl,
       originalUrl: url,
       filename
     }, onResponse);
   }
 }
 class FirefoxPreferences extends _preferences.BasePreferences {
-  _writeToStorage(prefObj) {
+  async _writeToStorage(prefObj) {
     return new Promise(function (resolve) {
       FirefoxCom.request('setPreferences', prefObj, resolve);
     });
   }
-  _readFromStorage(prefObj) {
+  async _readFromStorage(prefObj) {
     return new Promise(function (resolve) {
       FirefoxCom.request('getPreferences', prefObj, function (prefStr) {
         let readPrefs = JSON.parse(prefStr);
         resolve(readPrefs);
       });
     });
   }
 }
 class MozL10n {
   constructor(mozL10n) {
     this.mozL10n = mozL10n;
   }
-  getLanguage() {
-    return Promise.resolve(this.mozL10n.getLanguage());
-  }
-  getDirection() {
-    return Promise.resolve(this.mozL10n.getDirection());
-  }
-  get(property, args, fallback) {
-    return Promise.resolve(this.mozL10n.get(property, args, fallback));
-  }
-  translate(element) {
+  async getLanguage() {
+    return this.mozL10n.getLanguage();
+  }
+  async getDirection() {
+    return this.mozL10n.getDirection();
+  }
+  async get(property, args, fallback) {
+    return this.mozL10n.get(property, args, fallback);
+  }
+  async translate(element) {
     this.mozL10n.translate(element);
-    return Promise.resolve();
   }
 }
 (function listenFindEvents() {
-  const events = ['find', 'findagain', 'findhighlightallchange', 'findcasesensitivitychange'];
+  const events = ['find', 'findagain', 'findhighlightallchange', 'findcasesensitivitychange', 'findentirewordchange'];
   let handleEvent = function (evt) {
     if (!_app.PDFViewerApplication.initialized) {
       return;
     }
     _app.PDFViewerApplication.eventBus.dispatch('find', {
       source: window,
       type: evt.type.substring('find'.length),
       query: evt.detail.query,
       phraseSearch: true,
       caseSensitive: !!evt.detail.caseSensitive,
+      entireWord: !!evt.detail.entireWord,
       highlightAll: !!evt.detail.highlightAll,
       findPrevious: !!evt.detail.findPrevious
     });
   };
-  for (let i = 0, len = events.length; i < len; i++) {
-    window.addEventListener(events[i], handleEvent);
+  for (let event of events) {
+    window.addEventListener(event, handleEvent);
   }
 })();
 function FirefoxComDataRangeTransport(length, initialData) {
   _pdfjsLib.PDFDataRangeTransport.call(this, length, initialData);
 }
 FirefoxComDataRangeTransport.prototype = Object.create(_pdfjsLib.PDFDataRangeTransport.prototype);
 FirefoxComDataRangeTransport.prototype.requestDataRange = function FirefoxComDataRangeTransport_requestDataRange(begin, end) {
   FirefoxCom.request('requestDataRange', {
@@ -8871,16 +8971,17 @@ FirefoxComDataRangeTransport.prototype.r
 };
 FirefoxComDataRangeTransport.prototype.abort = function FirefoxComDataRangeTransport_abort() {
   FirefoxCom.requestSync('abortLoading', null);
 };
 _app.PDFViewerApplication.externalServices = {
   updateFindControlState(data) {
     FirefoxCom.request('updateFindControlState', data);
   },
+  updateFindMatchesCount(data) {},
   initPassiveLoading(callbacks) {
     let pdfDataRangeTransport;
     window.addEventListener('message', function windowMessage(e) {
       if (e.source !== null) {
         console.warn('Rejected untrusted message from ' + e.origin);
         return;
       }
       let args = e.data;
@@ -8955,17 +9056,17 @@ document.mozL10n.setExternalLocalizerSer
   getStrings(key) {
     return FirefoxCom.requestSync('getStrings', key);
   }
 });
 exports.DownloadManager = DownloadManager;
 exports.FirefoxCom = FirefoxCom;
 
 /***/ }),
-/* 34 */
+/* 35 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 (function (window) {
   var gLanguage = "";
   var gExternalLocalizerServices = null;
@@ -9053,17 +9154,17 @@ exports.FirefoxCom = FirefoxCom;
       gLanguage = gExternalLocalizerServices.getLocale();
       gReadyState = "complete";
     },
     translate: translateFragment
   };
 })(undefined);
 
 /***/ }),
-/* 35 */
+/* 36 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
@@ -9120,76 +9221,73 @@ class BasePreferences {
               prefValue = prefs[name];
         if (defaultValue === undefined || typeof prefValue !== typeof defaultValue) {
           continue;
         }
         this.prefs[name] = prefValue;
       }
     });
   }
-  _writeToStorage(prefObj) {
-    return Promise.reject(new Error('Not implemented: _writeToStorage'));
-  }
-  _readFromStorage(prefObj) {
-    return Promise.reject(new Error('Not implemented: _readFromStorage'));
-  }
-  reset() {
-    return this._initializedPromise.then(() => {
-      this.prefs = Object.assign(Object.create(null), this.defaults);
-      return this._writeToStorage(this.defaults);
-    });
-  }
-  set(name, value) {
-    return this._initializedPromise.then(() => {
-      if (this.defaults[name] === undefined) {
-        throw new Error(`Set preference: "${name}" is undefined.`);
-      } else if (value === undefined) {
-        throw new Error('Set preference: no value is specified.');
-      }
-      let valueType = typeof value;
-      let defaultType = typeof this.defaults[name];
-      if (valueType !== defaultType) {
-        if (valueType === 'number' && defaultType === 'string') {
-          value = value.toString();
-        } else {
-          throw new Error(`Set preference: "${value}" is a ${valueType}, ` + `expected a ${defaultType}.`);
-        }
+  async _writeToStorage(prefObj) {
+    throw new Error('Not implemented: _writeToStorage');
+  }
+  async _readFromStorage(prefObj) {
+    throw new Error('Not implemented: _readFromStorage');
+  }
+  async reset() {
+    await this._initializedPromise;
+    this.prefs = Object.assign(Object.create(null), this.defaults);
+    return this._writeToStorage(this.defaults);
+  }
+  async set(name, value) {
+    await this._initializedPromise;
+    let defaultValue = this.defaults[name];
+    if (defaultValue === undefined) {
+      throw new Error(`Set preference: "${name}" is undefined.`);
+    } else if (value === undefined) {
+      throw new Error('Set preference: no value is specified.');
+    }
+    let valueType = typeof value;
+    let defaultType = typeof defaultValue;
+    if (valueType !== defaultType) {
+      if (valueType === 'number' && defaultType === 'string') {
+        value = value.toString();
       } else {
-        if (valueType === 'number' && !Number.isInteger(value)) {
-          throw new Error(`Set preference: "${value}" must be an integer.`);
-        }
-      }
-      this.prefs[name] = value;
-      return this._writeToStorage(this.prefs);
-    });
-  }
-  get(name) {
-    return this._initializedPromise.then(() => {
-      let defaultValue = this.defaults[name];
-      if (defaultValue === undefined) {
-        throw new Error(`Get preference: "${name}" is undefined.`);
-      } else {
-        let prefValue = this.prefs[name];
-        if (prefValue !== undefined) {
-          return prefValue;
-        }
-      }
-      return defaultValue;
-    });
-  }
-  getAll() {
-    return this._initializedPromise.then(() => {
-      return Object.assign(Object.create(null), this.defaults, this.prefs);
-    });
+        throw new Error(`Set preference: "${value}" is a ${valueType}, ` + `expected a ${defaultType}.`);
+      }
+    } else {
+      if (valueType === 'number' && !Number.isInteger(value)) {
+        throw new Error(`Set preference: "${value}" must be an integer.`);
+      }
+    }
+    this.prefs[name] = value;
+    return this._writeToStorage(this.prefs);
+  }
+  async get(name) {
+    await this._initializedPromise;
+    let defaultValue = this.defaults[name];
+    if (defaultValue === undefined) {
+      throw new Error(`Get preference: "${name}" is undefined.`);
+    } else {
+      let prefValue = this.prefs[name];
+      if (prefValue !== undefined) {
+        return prefValue;
+      }
+    }
+    return defaultValue;
+  }
+  async getAll() {
+    await this._initializedPromise;
+    return Object.assign(Object.create(null), this.defaults, this.prefs);
   }
 }
 exports.BasePreferences = BasePreferences;
 
 /***/ }),
-/* 36 */
+/* 37 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
--- a/browser/extensions/pdfjs/moz.yaml
+++ b/browser/extensions/pdfjs/moz.yaml
@@ -15,15 +15,15 @@ origin:
   description: Portable Document Format (PDF) viewer that is built with HTML5
 
   # Full URL for the package's homepage/etc
   # Usually different from repository url
   url: https://github.com/mozilla/pdf.js
 
   # Human-readable identifier for this version/release
   # Generally "version NNN", "tag SSS", "bookmark SSS"
-  release: version 2.0.815
+  release: version 2.0.843
 
   # The package's license, where possible using the mnemonic from
   # https://spdx.org/licenses/
   # Multiple licenses can be specified (as a YAML list)
   # A "LICENSE" file must exist containing the full license text
   license: Apache-2.0
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -160,18 +160,26 @@ thumb_page_canvas=Thumbnail of Page {{pa
 find_input.title=Find
 find_input.placeholder=Find in document…
 find_previous.title=Find the previous occurrence of the phrase
 find_previous_label=Previous
 find_next.title=Find the next occurrence of the phrase
 find_next_label=Next
 find_highlight=Highlight all
 find_match_case_label=Match case
+find_entire_word_label=Whole words
 find_reached_top=Reached top of document, continued from bottom
 find_reached_bottom=Reached end of document, continued from top
+# LOCALIZATION NOTE (find_matches_count): "{{current}}" and "{{total}}" will be
+# replaced by a number representing the index of the currently active find result,
+# respectively a number representing the total number of matches in the document.
+find_matches_count={{current}} of {{total}} matches
+# LOCALIZATION NOTE (find_matches_count_limit): "{{limit}}" will be replaced by
+# a numerical value.
+find_matches_count_limit=More than {{limit}} matches
 find_not_found=Phrase not found
 
 # Error panel labels
 error_more_info=More Information
 error_less_info=Less Information
 error_close=Close
 # LOCALIZATION NOTE (error_version_info): "{{version}}" and "{{build}}" will be
 # replaced by the PDF.JS version and build ID.
--- a/browser/modules/BrowserUsageTelemetry.jsm
+++ b/browser/modules/BrowserUsageTelemetry.jsm
@@ -8,23 +8,20 @@
 var EXPORTED_SYMBOLS = [
   "BrowserUsageTelemetry",
   "URLBAR_SELECTED_RESULT_TYPES",
   "URLBAR_SELECTED_RESULT_METHODS",
   "MINIMUM_TAB_COUNT_INTERVAL_MS",
  ];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
                                "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-XPCOMUtils.defineLazyGlobalGetters(this, ["URLSearchParams"]);
-
 // The upper bound for the count of the visited unique domain names.
 const MAX_UNIQUE_VISITED_DOMAINS = 100;
 
 // Observed topic names.
 const TAB_RESTORING_TOPIC = "SSTabRestoring";
 const TELEMETRY_SUBSESSIONSPLIT_TOPIC = "internal-telemetry-after-subsession-split";
 const DOMWINDOW_OPENED_TOPIC = "domwindowopened";
 const AUTOCOMPLETE_ENTER_TEXT_TOPIC = "autocomplete-did-enter-text";
@@ -197,39 +194,17 @@ let URICountListener = {
     if (shouldCountURI) {
       Services.telemetry.scalarAdd(UNFILTERED_URI_COUNT_SCALAR_NAME, 1);
     }
 
     if (!this.isHttpURI(uri)) {
       return;
     }
 
-    let parseURLResult = Services.search.parseSubmissionURL(uriSpec);
-    if (parseURLResult.engine) {
-      this._recordSearchTelemetry(uriSpec, parseURLResult);
-    } else if (this._urlsQueuedForParsing) {
-      if (Services.search.isInitialized) {
-        this._urlsQueuedForParsing = null;
-      } else {
-        this._urlsQueuedForParsing.push(uriSpec);
-        if (this._urlsQueuedForParsing.length == 1) {
-          Services.search.init(rv => {
-            if (Components.isSuccessCode(rv)) {
-              for (let url of this._urlsQueuedForParsing) {
-                let innerParseURLResult = Services.search.parseSubmissionURL(url);
-                if (innerParseURLResult.engine) {
-                  this._recordSearchTelemetry(url, innerParseURLResult);
-                }
-              }
-            }
-            this._urlsQueuedForParsing = null;
-          });
-        }
-      }
-    }
+    Services.search.recordSearchURLTelemetry(uriSpec);
 
     if (!shouldCountURI) {
       return;
     }
 
     // Update the URI counts.
     Services.telemetry.scalarAdd(TOTAL_URI_COUNT_SCALAR_NAME, 1);
 
@@ -257,41 +232,16 @@ let URICountListener = {
 
   /**
    * Reset the counts. This should be called when breaking a session in Telemetry.
    */
   reset() {
     this._domainSet.clear();
   },
 
-  _urlsQueuedForParsing: [],
-
-  _recordSearchTelemetry(url, parseURLResult) {
-    switch (parseURLResult.engine.identifier) {
-      case "google":
-      case "google-2018":
-        let type;
-        let queries = new URLSearchParams(url.split("#")[0].split("?")[1]);
-        let code = queries.get("client");
-        if (code) {
-          // Detecting follow-on searches for sap is a little tricky.
-          // There are a few parameters that only show up
-          // with follow-ons, so we look for those. (oq/ved/ei)
-          type = queries.has("oq") || queries.has("ved") || queries.has("ei") ? "sap-follow-on" : "sap";
-        } else {
-          type = "organic";
-        }
-        let payload = `google.in-content:${type}:${code || "none"}`;
-
-        let histogram = Services.telemetry.getKeyedHistogramById("SEARCH_COUNTS");
-        histogram.add(payload);
-        break;
-    }
-  },
-
   QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
                                           Ci.nsISupportsWeakReference]),
 };
 
 let urlbarListener = {
 
   // This is needed for recordUrlbarSelectedResultMethod().
   selectedIndex: -1,
--- a/browser/modules/BrowserWindowTracker.jsm
+++ b/browser/modules/BrowserWindowTracker.jsm
@@ -9,17 +9,16 @@
 
 var EXPORTED_SYMBOLS = ["BrowserWindowTracker"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // Lazy getters
 XPCOMUtils.defineLazyModuleGetters(this, {
-  AppConstants: "resource://gre/modules/AppConstants.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
 });
 
 // Constants
 const TAB_EVENTS = ["TabBrowserInserted", "TabSelect"];
 const WINDOW_EVENTS = ["activate", "sizemodechange", "unload"];
 const DEBUG = false;
 
@@ -140,81 +139,54 @@ var WindowHelper = {
 
   onSizemodeChange(window) {
     if (window.windowState == window.STATE_MINIMIZED) {
       // Make sure to have the minimized window at the end of the list.
       _untrackWindowOrder(window);
       _trackedWindows.push(window);
     }
   },
-
-  getTopWindow(options) {
-    let checkPrivacy = typeof options == "object" &&
-                       "private" in options;
-
-    let allowPopups = typeof options == "object" && !!options.allowPopups;
-
-    function isSuitableBrowserWindow(win) {
-      return (!win.closed &&
-              (allowPopups || win.toolbar.visible) &&
-              (!checkPrivacy ||
-               PrivateBrowsingUtils.permanentPrivateBrowsing ||
-               PrivateBrowsingUtils.isWindowPrivate(win) == options.private));
-    }
-
-    let broken_wm_z_order =
-      AppConstants.platform != "macosx" && AppConstants.platform != "win";
-
-    if (broken_wm_z_order) {
-      let win = Services.wm.getMostRecentWindow("navigator:browser");
-
-      // if we're lucky, this isn't a popup, and we can just return this
-      if (win && !isSuitableBrowserWindow(win)) {
-        win = null;
-        // this is oldest to newest, so this gets a bit ugly
-        for (let nextWin of Services.wm.getEnumerator("navigator:browser")) {
-          if (isSuitableBrowserWindow(nextWin))
-            win = nextWin;
-        }
-      }
-      return win;
-    }
-    for (let win of Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true)) {
-      if (isSuitableBrowserWindow(win))
-        return win;
-    }
-    return null;
-  },
 };
 
 this.BrowserWindowTracker = {
   /**
    * Get the most recent browser window.
    *
    * @param options an object accepting the arguments for the search.
    *        * private: true to restrict the search to private windows
    *            only, false to restrict the search to non-private only.
    *            Omit the property to search in both groups.
    *        * allowPopups: true if popup windows are permissable.
    */
-  getTopWindow(options) {
-    return WindowHelper.getTopWindow(options);
+  getTopWindow(options = {}) {
+    for (let win of _trackedWindows) {
+      if (!win.closed &&
+          (options.allowPopups || win.toolbar.visible) &&
+          (!("private" in options) ||
+           PrivateBrowsingUtils.permanentPrivateBrowsing ||
+           PrivateBrowsingUtils.isWindowPrivate(win) == options.private)) {
+        return win;
+      }
+    }
+    return null;
   },
 
   /**
-   * Iterator property that yields window objects by z-index, in reverse order.
-   * This means that the lastly focused window will the first item that is yielded.
-   * Note: we only know the order of windows we're actively tracking, which
-   * basically means _only_ browser windows.
+   * Number of currently open browser windows.
    */
-  orderedWindows: {
-    * [Symbol.iterator]() {
-      // Clone the windows array immediately as it may change during iteration,
-      // we'd rather have an outdated order than skip/revisit windows.
-      for (let window of [..._trackedWindows])
-        yield window;
-    },
+  get windowCount() {
+    return _trackedWindows.length;
+  },
+
+  /**
+   * Array of browser windows ordered by z-index, in reverse order.
+   * This means that the top-most browser window will be the first item.
+   */
+  get orderedWindows() {
+    // Clone the windows array immediately as it may change during iteration,
+    // we'd rather have an outdated order than skip/revisit windows.
+    return [..._trackedWindows];
   },
 
   track(window) {
     return WindowHelper.addWindow(window);
   },
 };
--- a/browser/modules/test/browser/browser_BrowserWindowTracker.js
+++ b/browser/modules/test/browser/browser_BrowserWindowTracker.js
@@ -74,40 +74,42 @@ add_task(async function test_getTopWindo
     Assert.equal(window, windows[expectedMostRecentIndex],
       "Window focused before the popup window should be the most recent one.");
     popupWindow.close();
   });
 });
 
 add_task(async function test_orderedWindows() {
   await withOpenWindows(10, async function(windows) {
-    let ordered = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW);
+    Assert.equal(BrowserWindowTracker.windowCount, 11,
+      "Number of tracked windows, including the test window");
+    let ordered = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW);
     Assert.deepEqual([9, 8, 7, 6, 5, 4, 3, 2, 1, 0], ordered.map(w => windows.indexOf(w)),
       "Order of opened windows should be as opened.");
 
     // Mess with the focused window things a bit.
     for (let idx of [4, 6, 1]) {
       let promise = BrowserTestUtils.waitForEvent(windows[idx], "activate");
       Services.focus.focusedWindow = windows[idx];
       await promise;
     }
 
-    let ordered2 = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW);
+    let ordered2 = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW);
     // After the shuffle, we expect window '1' to be the top-most window, because
     // it was the last one we called focus on. Then '6', the window we focused
     // before-last, followed by '4'. The order of the other windows remains
     // unchanged.
     let expected = [1, 6, 4, 9, 8, 7, 5, 3, 2, 0];
     Assert.deepEqual(expected, ordered2.map(w => windows.indexOf(w)),
       "After shuffle of focused windows, the order should've changed.");
 
     // Minimizing a window should put it at the end of the ordered list of windows.
     let promise = BrowserTestUtils.waitForEvent(windows[9], "sizemodechange");
     windows[9].minimize();
     await promise;
 
-    let ordered3 = [...BrowserWindowTracker.orderedWindows].filter(w => w != TEST_WINDOW);
+    let ordered3 = BrowserWindowTracker.orderedWindows.filter(w => w != TEST_WINDOW);
     // Test the end of the array of window indices, because Windows Debug builds
     // mysteriously swap the order of the first two windows.
     Assert.deepEqual([8, 7, 5, 3, 2, 0, 9], ordered3.map(w => windows.indexOf(w)).slice(3),
       "When a window is minimized, the order should've changed.");
   });
 });
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -478,8 +478,12 @@ def alter_path(sdk_bin_path):
     path = os.pathsep.join(sdk_bin_path)
     os.environ['PATH'] = path
     return path
 
 
 set_config('PATH', alter_path)
 
 check_prog('MAKECAB', ('makecab.exe',))
+
+# Make sure that the build system can handle non-ASCII characters in
+# environment variables to prevent silent breakage on non-English systems.
+set_config('NONASCII', b'\241\241')
--- a/dom/interfaces/payments/moz.build
+++ b/dom/interfaces/payments/moz.build
@@ -1,16 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 XPIDL_SOURCES += [
-    'nsIPaymentActionRequest.idl',
     'nsIPaymentActionResponse.idl',
     'nsIPaymentAddress.idl',
     'nsIPaymentRequest.idl',
     'nsIPaymentRequestService.idl',
     'nsIPaymentUIService.idl',
 ]
 
 XPIDL_MODULE = 'dom_payments'
deleted file mode 100644
--- a/dom/interfaces/payments/nsIPaymentActionRequest.idl
+++ /dev/null
@@ -1,161 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "nsISupports.idl"
-#include "nsIVariant.idl"
-#include "nsIPaymentRequest.idl"
-#include "nsIPaymentActionResponse.idl"
-#include "nsIPaymentAddress.idl"
-
-interface nsIArray;
-
-[builtinclass, uuid(3fef5459-b0ea-469b-be9f-b99e8ca75d3d)]
-interface nsIPaymentActionCallback : nsISupports
-{
-  void respondPayment(in nsIPaymentActionResponse aResponse);
-  void changeShippingAddress(in AString aRequestId, in nsIPaymentAddress aAddress);
-  void changeShippingOption(in AString aRequestId, in AString aOption);
-};
-
-[builtinclass, uuid(7ddbe8be-beac-4952-96f6-619981dff7a6)]
-interface nsIPaymentActionRequest : nsISupports
-{
-  const uint32_t UNKNOWN_ACTION = 0;
-  const uint32_t CREATE_ACTION = 1;
-  const uint32_t CANMAKE_ACTION = 2;
-  const uint32_t SHOW_ACTION = 3;
-  const uint32_t ABORT_ACTION = 4;
-  const uint32_t COMPLETE_ACTION = 5;
-  const uint32_t UPDATE_ACTION = 6;
-  const uint32_t CLOSE_ACTION = 7;
-
-  /*
-   *  The payment request identifier.
-   */
-  readonly attribute AString requestId;
-
-  /*
-   *  The type of the requested task.
-   */
-  readonly attribute uint32_t type;
-
-  /*
-   *  The callback for the response from UI module
-   */
-  readonly attribute nsIPaymentActionCallback callback;
-
-  /*
-   *  Initialize function for this request.
-   */
-  void init(in AString aRequestId,
-            in uint32_t aType,
-            in nsIPaymentActionCallback aCallback);
-};
-
-[builtinclass, uuid(1d38dce6-8bcd-441b-aa94-68e300b6e175)]
-interface nsIPaymentCreateActionRequest : nsIPaymentActionRequest
-{
-  /*
-   *  The tab identifier
-   */
-  readonly attribute uint64_t tabId;
-
-  /*
-   *  The top level document's principal
-   */
-  readonly attribute nsIPrincipal topLevelPrincipal;
-
-  /*
-   *  The methodData information of the payment request.
-   */
-  readonly attribute nsIArray methodData;
-
-  /*
-   *  The Details information of the payment request.
-   */
-  readonly attribute nsIPaymentDetails details;
-
-  /*
-   *  The Options information of the payment request.
-   */
-  readonly attribute nsIPaymentOptions options;
-
-  /*
-   *  The selected shipping option of the payment request;
-   */
-  readonly attribute AString shippingOption;
-
-  /*
-   *  Initialize function the this request.
-   */
-  void initRequest(in AString aRequestId,
-                   in nsIPaymentActionCallback aCallback,
-                   in uint64_t aTabId,
-                   in nsIPrincipal aPrincipal,
-                   in nsIArray aMethodData,
-                   in nsIPaymentDetails aDetails,
-                   in nsIPaymentOptions aOptions,
-                   in AString aShippingOption);
-};
-
-[builtinclass, uuid(4429697d-1135-47de-a46e-5196d399ec55)]
-interface nsIPaymentCompleteActionRequest : nsIPaymentActionRequest
-{
-  /*
-   *  The complete status from merchant side.
-   */
-  readonly attribute AString completeStatus;
-
-  /*
-   *  Initialize function for this request.
-   */
-  void initRequest(in AString aRequestId,
-                   in nsIPaymentActionCallback aCallback,
-                   in AString aCompleteStatus);
-};
-
-[builtinclass, uuid(21f631e8-c047-4fd8-b3c6-68e26c62639a)]
-interface nsIPaymentUpdateActionRequest : nsIPaymentActionRequest
-{
-  /*
-   *  The details information for updating the specified payment request.
-   */
-  readonly attribute nsIPaymentDetails details;
-
-  /*
-   *  The selected shipping option information
-   */
-  readonly attribute AString shippingOption;
-
-  /*
-   *  Initialize function for this request.
-   */
-  void initRequest(in AString aRequestId,
-                   in nsIPaymentActionCallback aCallback,
-                   in nsIPaymentDetails aDetails,
-                   in AString aShippingOption);
-};
-
-%{C++
-#define NS_PAYMENT_ACTION_REQUEST_CID \
-  { 0x7ddbe8be, 0xbeac, 0x4952, { 0x96, 0xf6, 0x61, 0x99, 0x81, 0xdf, 0xf7, 0xa6 } }
-#define NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID \
-  "@mozilla.org/dom/payments/payment-action-request;1"
-
-#define NS_PAYMENT_CREATE_ACTION_REQUEST_CID \
-  { 0x1d38dce6, 0x8bcd, 0x441b, { 0xaa, 0x94, 0x68, 0xe3, 0x00, 0xb6, 0xe1, 0x75 } }
-#define NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID \
-  "@mozilla.org/dom/payments/payment-create-action-request;1"
-
-#define NS_PAYMENT_COMPLETE_ACTION_REQUEST_CID \
-  { 0x4429697d, 0x1135, 0x47de, { 0xa4, 0x6e, 0x51, 0x96, 0xd3, 0x99, 0xec, 0x55 } }
-#define NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID \
-  "@mozilla.org/dom/payments/payment-complete-action-request;1"
-
-#define NS_PAYMENT_UPDATE_ACTION_REQUEST_CID \
-  { 0x21f631e8, 0xc047, 0x4fd8, { 0xb3, 0xc6, 0x68, 0xe2, 0x6c, 0x62, 0x63, 0x9a } }
-#define NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID \
-  "@mozilla.org/dom/payments/payment-update-action-request;1"
-%}
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -58,19 +58,20 @@ interface nsIPaymentDetails : nsISupport
   readonly attribute AString id;
   readonly attribute nsIPaymentItem totalItem;
   readonly attribute nsIArray displayItems;
   readonly attribute nsIArray shippingOptions;
   readonly attribute nsIArray modifiers;
   readonly attribute AString error;
   [implicit_jscontext]
   readonly attribute jsval shippingAddressErrors;
-
-  void update(in nsIPaymentDetails aDetails, in boolean aRequestShipping);
-  AString shippingAddressErrorsJSON();
+  [implicit_jscontext]
+  readonly attribute jsval payer;
+  [implicit_jscontext]
+  readonly attribute jsval paymentMethod;
 };
 
 [scriptable, builtinclass, uuid(d53f9f20-138e-47cc-9fd5-db16a3f6d301)]
 interface nsIPaymentOptions : nsISupports
 {
   readonly attribute boolean requestPayerName;
   readonly attribute boolean requestPayerEmail;
   readonly attribute boolean requestPayerPhone;
@@ -84,13 +85,9 @@ interface nsIPaymentRequest : nsISupport
   readonly attribute uint64_t tabId;
   readonly attribute nsIPrincipal topLevelPrincipal;
   readonly attribute AString requestId;
   readonly attribute AString completeStatus;
   readonly attribute nsIArray paymentMethods;
   readonly attribute nsIPaymentDetails paymentDetails;
   readonly attribute nsIPaymentOptions paymentOptions;
   readonly attribute AString shippingOption;
-
-  [noscript] void setCompleteStatus(in AString aCompleteStatus);
-  void updatePaymentDetails(in nsIPaymentDetails aDetails,
-                            in AString aShippingOption);
 };
--- a/dom/interfaces/payments/nsIPaymentRequestService.idl
+++ b/dom/interfaces/payments/nsIPaymentRequestService.idl
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVariant.idl"
 #include "nsIPaymentRequest.idl"
-#include "nsIPaymentActionRequest.idl"
 #include "nsIPaymentActionResponse.idl"
 #include "nsIPaymentAddress.idl"
 #include "nsISimpleEnumerator.idl"
 #include "nsIPaymentUIService.idl"
 
 /**
  *  nsPaymentRequestService is used to manage the created PaymentRequest in the
  *  chrome process. It is also the IPC agent for payment UI to communicate with
@@ -65,32 +64,16 @@ interface nsIPaymentRequestService : nsI
    */
   void cleanup();
 
   /**
    *  Setup the customized nsIPaymentUIService.
    *  This API is for testing only.
    */
   void setTestingUIService(in nsIPaymentUIService aUIService);
-
-  /**
-   *  Request a specified action on the specified PaymentRequest.
-   *  @param aRequest - the requested action.
-   */
-  void requestPayment(in nsIPaymentActionRequest aRequest);
-
-  /**
-   *  This is a cleanup function to break the association between
-   *  nsIPaymentRequestService and nsIPaymentActionCallback.
-   *  nsIPaymentActionCallback is an interface that registered to
-   *  nsIPaymentRequestService when the merchant asks to perform actions, and it
-   *  will be called when user's response send back to nsIPaymentRequestService.
-   *  @param aCallback - the specified nsIPaymentActionCallback.
-   */
-  void removeActionCallback(in nsIPaymentActionCallback aCallback);
 };
 
 %{C++
 #define NS_PAYMENT_REQUEST_SERVICE_CID \
   { 0xcccd665f, 0xedf3, 0x41fc, { 0xab, 0x9b, 0xfc, 0x55, 0xb3, 0x73, 0x40, 0xaa } }
 #define NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID \
   "@mozilla.org/dom/payments/payment-request-service;1"
 %}
--- a/dom/media/webspeech/recognition/SpeechRecognition.cpp
+++ b/dom/media/webspeech/recognition/SpeechRecognition.cpp
@@ -748,17 +748,17 @@ SpeechRecognition::Start(const Optional<
   }
 
   MediaStreamConstraints constraints;
   constraints.mAudio.SetAsBoolean() = true;
 
   if (aStream.WasPassed()) {
     StartRecording(&aStream.Value());
   } else {
-    AutoNoJSAPI();
+    AutoNoJSAPI nojsapi;
     MediaManager* manager = MediaManager::Get();
     MediaManager::GetUserMediaSuccessCallback onsuccess(
       new GetUserMediaSuccessCallback(this));
     MediaManager::GetUserMediaErrorCallback onerror(
       new GetUserMediaErrorCallback(this));
     manager->GetUserMedia(GetOwner(),
                           constraints,
                           std::move(onsuccess),
deleted file mode 100644
--- a/dom/payments/PaymentActionRequest.cpp
+++ /dev/null
@@ -1,230 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 "nsArrayUtils.h"
-#include "nsIMutableArray.h"
-#include "PaymentActionRequest.h"
-#include "PaymentRequestData.h"
-
-using namespace mozilla::dom::payments;
-
-namespace mozilla {
-namespace dom {
-
-/* PaymentActionRequest */
-
-NS_IMPL_ISUPPORTS(PaymentActionRequest,
-                  nsIPaymentActionRequest)
-
-PaymentActionRequest::PaymentActionRequest()
-  : mRequestId(EmptyString())
-  , mType(nsIPaymentActionRequest::UNKNOWN_ACTION)
-  , mCallback(nullptr)
-{
-}
-
-NS_IMETHODIMP
-PaymentActionRequest::Init(const nsAString& aRequestId,
-                           const uint32_t aType,
-                           nsIPaymentActionCallback* aCallback)
-{
-  mRequestId = aRequestId;
-  mType = aType;
-  mCallback = aCallback;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentActionRequest::GetRequestId(nsAString& aRequestId)
-{
-  aRequestId = mRequestId;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentActionRequest::GetType(uint32_t* aType)
-{
-  *aType = mType;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentActionRequest::GetCallback(nsIPaymentActionCallback** aCallback)
-{
-  NS_ENSURE_ARG_POINTER(aCallback);
-  nsCOMPtr<nsIPaymentActionCallback> callback = mCallback;
-  callback.forget(aCallback);
-  return NS_OK;
-}
-
-/* PaymentCreateActionRequest */
-
-NS_IMPL_ISUPPORTS_INHERITED(PaymentCreateActionRequest,
-                            PaymentActionRequest,
-                            nsIPaymentCreateActionRequest)
-
-PaymentCreateActionRequest::PaymentCreateActionRequest()
-  : mTabId(0)
-{
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::InitRequest(const nsAString& aRequestId,
-                                        nsIPaymentActionCallback* aCallback,
-                                        const uint64_t aTabId,
-                                        nsIPrincipal* aTopLevelPrincipal,
-                                        nsIArray* aMethodData,
-                                        nsIPaymentDetails* aDetails,
-                                        nsIPaymentOptions* aOptions,
-                                        const nsAString& aShippingOption)
-{
-  NS_ENSURE_ARG_POINTER(aCallback);
-  NS_ENSURE_ARG_POINTER(aTopLevelPrincipal);
-  NS_ENSURE_ARG_POINTER(aMethodData);
-  NS_ENSURE_ARG_POINTER(aDetails);
-  NS_ENSURE_ARG_POINTER(aOptions);
-  nsresult rv = Init(aRequestId, nsIPaymentActionRequest::CREATE_ACTION, aCallback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  mTabId = aTabId;
-  mTopLevelPrincipal = aTopLevelPrincipal;
-  mMethodData = aMethodData;
-  mDetails = aDetails;
-  mOptions = aOptions;
-  mShippingOption = aShippingOption;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetTabId(uint64_t* aTabId)
-{
-  NS_ENSURE_ARG_POINTER(aTabId);
-  *aTabId = mTabId;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetTopLevelPrincipal(nsIPrincipal** aTopLevelPrincipal)
-{
-  NS_ENSURE_ARG_POINTER(aTopLevelPrincipal);
-  MOZ_ASSERT(mTopLevelPrincipal);
-  nsCOMPtr<nsIPrincipal> principal = mTopLevelPrincipal;
-  principal.forget(aTopLevelPrincipal);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetMethodData(nsIArray** aMethodData)
-{
-  NS_ENSURE_ARG_POINTER(aMethodData);
-  MOZ_ASSERT(mMethodData);
-  nsCOMPtr<nsIArray> methodData = mMethodData;
-  methodData.forget(aMethodData);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetDetails(nsIPaymentDetails** aDetails)
-{
-  NS_ENSURE_ARG_POINTER(aDetails);
-  MOZ_ASSERT(mDetails);
-  nsCOMPtr<nsIPaymentDetails> details = mDetails;
-  details.forget(aDetails);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetOptions(nsIPaymentOptions** aOptions)
-{
-  NS_ENSURE_ARG_POINTER(aOptions);
-  MOZ_ASSERT(mOptions);
-  nsCOMPtr<nsIPaymentOptions> options = mOptions;
-  options.forget(aOptions);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCreateActionRequest::GetShippingOption(nsAString& aShippingOption)
-{
-  aShippingOption = mShippingOption;
-  return NS_OK;
-}
-
-/* PaymentCompleteActionRequest */
-
-NS_IMPL_ISUPPORTS_INHERITED(PaymentCompleteActionRequest,
-                            PaymentActionRequest,
-                            nsIPaymentCompleteActionRequest)
-
-PaymentCompleteActionRequest::PaymentCompleteActionRequest()
-  : mCompleteStatus(EmptyString())
-{
-}
-
-NS_IMETHODIMP
-PaymentCompleteActionRequest::GetCompleteStatus(nsAString& aCompleteStatus)
-{
-  aCompleteStatus = mCompleteStatus;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentCompleteActionRequest::InitRequest(const nsAString& aRequestId,
-                                          nsIPaymentActionCallback* aCallback,
-                                          const nsAString& aCompleteStatus)
-{
-  NS_ENSURE_ARG_POINTER(aCallback);
-  nsresult rv = Init(aRequestId, nsIPaymentActionRequest::COMPLETE_ACTION, aCallback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  mCompleteStatus = aCompleteStatus;
-  return NS_OK;
-}
-
-/* PaymentUpdateActionRequest */
-
-NS_IMPL_ISUPPORTS_INHERITED(PaymentUpdateActionRequest,
-                            PaymentActionRequest,
-                            nsIPaymentUpdateActionRequest)
-
-NS_IMETHODIMP
-PaymentUpdateActionRequest::GetDetails(nsIPaymentDetails** aDetails)
-{
-  NS_ENSURE_ARG_POINTER(aDetails);
-  MOZ_ASSERT(mDetails);
-  nsCOMPtr<nsIPaymentDetails> details = mDetails;
-  details.forget(aDetails);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentUpdateActionRequest::GetShippingOption(nsAString& aShippingOption)
-{
-  aShippingOption = mShippingOption;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PaymentUpdateActionRequest::InitRequest(const nsAString& aRequestId,
-                                        nsIPaymentActionCallback* aCallback,
-                                        nsIPaymentDetails* aDetails,
-                                        const nsAString& aShippingOption)
-{
-  NS_ENSURE_ARG_POINTER(aCallback);
-  NS_ENSURE_ARG_POINTER(aDetails);
-  nsresult rv = Init(aRequestId, nsIPaymentActionRequest::UPDATE_ACTION, aCallback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  mDetails = aDetails;
-  mShippingOption = aShippingOption;
-  return NS_OK;
-}
-
-} // end of namespace dom
-} // end of namespace mozilla
deleted file mode 100644
--- a/dom/payments/PaymentActionRequest.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* 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 mozilla_dom_PaymentActionRequest_h
-#define mozilla_dom_PaymentActionRequest_h
-
-#include "nsIPaymentActionRequest.h"
-#include "nsCOMPtr.h"
-#include "nsIArray.h"
-#include "nsString.h"
-
-namespace mozilla {
-namespace dom {
-
-class PaymentActionRequest : public nsIPaymentActionRequest
-{
-public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSIPAYMENTACTIONREQUEST
-
-  PaymentActionRequest();
-
-protected:
-  virtual ~PaymentActionRequest() = default;
-
-  nsString mRequestId;
-  uint32_t mType;
-  nsCOMPtr<nsIPaymentActionCallback> mCallback;
-};
-
-class PaymentCreateActionRequest final : public nsIPaymentCreateActionRequest
-                                       , public PaymentActionRequest
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_FORWARD_NSIPAYMENTACTIONREQUEST(PaymentActionRequest::)
-  NS_DECL_NSIPAYMENTCREATEACTIONREQUEST
-
-  PaymentCreateActionRequest();
-
-private:
-  ~PaymentCreateActionRequest() = default;
-
-  uint64_t mTabId;
-  nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
-  nsCOMPtr<nsIArray> mMethodData;
-  nsCOMPtr<nsIPaymentDetails> mDetails;
-  nsCOMPtr<nsIPaymentOptions> mOptions;
-  nsString mShippingOption;
-};
-
-class PaymentCompleteActionRequest final : public nsIPaymentCompleteActionRequest
-                                         , public PaymentActionRequest
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_FORWARD_NSIPAYMENTACTIONREQUEST(PaymentActionRequest::)
-  NS_DECL_NSIPAYMENTCOMPLETEACTIONREQUEST
-
-  PaymentCompleteActionRequest();
-
-private:
-  ~PaymentCompleteActionRequest() = default;
-
-  nsString mCompleteStatus;
-};
-
-class PaymentUpdateActionRequest final : public nsIPaymentUpdateActionRequest
-                                       , public PaymentActionRequest
-{
-public:
-  NS_DECL_ISUPPORTS_INHERITED
-  NS_FORWARD_NSIPAYMENTACTIONREQUEST(PaymentActionRequest::)
-  NS_DECL_NSIPAYMENTUPDATEACTIONREQUEST
-
-  PaymentUpdateActionRequest() = default;
-private:
-  ~PaymentUpdateActionRequest() = default;
-
-  nsCOMPtr<nsIPaymentDetails> mDetails;
-  nsString mShippingOption;
-};
-
-} // end of namespace dom
-} // end of namespace mozilla
-
-#endif
--- a/dom/payments/PaymentRequest.cpp
+++ b/dom/payments/PaymentRequest.cpp
@@ -740,50 +740,59 @@ PaymentRequest::Show(const Optional<Owni
   mAcceptPromise = promise;
   mState = eInteractive;
   return promise.forget();
 }
 
 void
 PaymentRequest::RejectShowPayment(nsresult aRejectReason)
 {
-  MOZ_ASSERT(mAcceptPromise);
+  MOZ_ASSERT(mAcceptPromise || mResponse);
   MOZ_ASSERT(mState == eInteractive);
 
-  mAcceptPromise->MaybeReject(aRejectReason);
+  if (mResponse) {
+    mResponse->RejectRetry(aRejectReason);
+  } else {
+    mAcceptPromise->MaybeReject(aRejectReason);
+  }
   mState = eClosed;
   mAcceptPromise = nullptr;
 }
 
 void
 PaymentRequest::RespondShowPayment(const nsAString& aMethodName,
                                    const nsAString& aDetails,
                                    const nsAString& aPayerName,
                                    const nsAString& aPayerEmail,
                                    const nsAString& aPayerPhone,
                                    nsresult aRv)
 {
-  MOZ_ASSERT(mAcceptPromise);
+  MOZ_ASSERT(mAcceptPromise || mResponse);
   MOZ_ASSERT(mState == eInteractive);
 
   if (NS_FAILED(aRv)) {
     RejectShowPayment(aRv);
     return;
   }
 
   // https://github.com/w3c/payment-request/issues/692
   mShippingAddress.swap(mFullShippingAddress);
   mFullShippingAddress = nullptr;
 
-  RefPtr<PaymentResponse> paymentResponse =
-    new PaymentResponse(GetOwner(), this, mId, aMethodName,
-                        mShippingOption, mShippingAddress, aDetails,
-                        aPayerName, aPayerEmail, aPayerPhone);
-  mResponse = paymentResponse;
-  mAcceptPromise->MaybeResolve(paymentResponse);
+  if (mResponse) {
+    mResponse->RespondRetry(aMethodName, mShippingOption, mShippingAddress,
+                            aDetails, aPayerName, aPayerEmail, aPayerPhone);
+  } else {
+    RefPtr<PaymentResponse> paymentResponse =
+      new PaymentResponse(GetOwner(), this, mId, aMethodName,
+                          mShippingOption, mShippingAddress, aDetails,
+                          aPayerName, aPayerEmail, aPayerPhone);
+    mResponse = paymentResponse;
+    mAcceptPromise->MaybeResolve(paymentResponse);
+  }
 
   mState = eClosed;
   mAcceptPromise = nullptr;
 }
 
 void
 PaymentRequest::RespondComplete()
 {
@@ -898,16 +907,32 @@ PaymentRequest::AbortUpdate(nsresult aRv
 
   // Remember update error |aRv| and do the following steps in RespondShowPayment.
   // 1. Set target.state to closed
   // 2. Reject the promise target.acceptPromise with exception "aRv"
   // 3. Abort the algorithm with update error
   mUpdateError = aRv;
 }
 
+nsresult
+PaymentRequest::RetryPayment(JSContext* aCx, const PaymentValidationErrors& aErrors)
+{
+  if (mState == eInteractive) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+  RefPtr<PaymentRequestManager> manager = PaymentRequestManager::GetSingleton();
+  MOZ_ASSERT(manager);
+  nsresult rv = manager->RetryPayment(aCx, this, aErrors);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  mState = eInteractive;
+  return NS_OK;
+}
+
 void
 PaymentRequest::GetId(nsAString& aRetVal) const
 {
   aRetVal = mId;
 }
 
 void
 PaymentRequest::GetInternalId(nsAString& aRetVal)
--- a/dom/payments/PaymentRequest.h
+++ b/dom/payments/PaymentRequest.h
@@ -104,16 +104,18 @@ public:
                           const nsAString& aPayerPhone,
                           nsresult aRv);
   void RejectShowPayment(nsresult aRejectReason);
   void RespondComplete();
 
   already_AddRefed<Promise> Abort(ErrorResult& aRv);
   void RespondAbortPayment(bool aResult);
 
+  nsresult RetryPayment(JSContext* aCx, const PaymentValidationErrors& aErrors);
+
   void GetId(nsAString& aRetVal) const;
   void GetInternalId(nsAString& aRetVal);
   void SetId(const nsAString& aId);
 
   bool Equals(const nsAString& aInternalId) const;
 
   bool ReadyForUpdate();
   bool IsUpdating() const { return mUpdating; }
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -336,24 +336,28 @@ NS_IMPL_ISUPPORTS(PaymentDetails,
                   nsIPaymentDetails)
 
 PaymentDetails::PaymentDetails(const nsAString& aId,
                                nsIPaymentItem* aTotalItem,
                                nsIArray* aDisplayItems,
                                nsIArray* aShippingOptions,
                                nsIArray* aModifiers,
                                const nsAString& aError,
-                               const nsAString& aShippingAddressErrors)
+                               const nsAString& aShippingAddressErrors,
+                               const nsAString& aPayerErrors,
+                               const nsAString& aPaymentMethodErrors)
   : mId(aId)
   , mTotalItem(aTotalItem)
   , mDisplayItems(aDisplayItems)
   , mShippingOptions(aShippingOptions)
   , mModifiers(aModifiers)
   , mError(aError)
   , mShippingAddressErrors(aShippingAddressErrors)
+  , mPayerErrors(aPayerErrors)
+  , mPaymentMethodErrors(aPaymentMethodErrors)
 {
 }
 
 nsresult
 PaymentDetails::Create(const IPCPaymentDetails& aIPCDetails,
                        nsIPaymentDetails** aDetails)
 {
   NS_ENSURE_ARG_POINTER(aDetails);
@@ -410,17 +414,19 @@ PaymentDetails::Create(const IPCPaymentD
       return rv;
     }
   }
   modifiers = detailsModifiers.forget();
 
   nsCOMPtr<nsIPaymentDetails> details =
     new PaymentDetails(aIPCDetails.id(), total, displayItems, shippingOptions,
                        modifiers, aIPCDetails.error(),
-                       aIPCDetails.shippingAddressErrors());
+                       aIPCDetails.shippingAddressErrors(),
+                       aIPCDetails.payerErrors(),
+                       aIPCDetails.paymentMethodErrors());
 
   details.forget(aDetails);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentDetails::GetId(nsAString& aId)
 {
@@ -479,16 +485,41 @@ PaymentDetails::GetShippingAddressErrors
   errors.Init(mShippingAddressErrors);
   if (!ToJSValue(aCx, errors, aErrors)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
+PaymentDetails::GetPayer(JSContext* aCx, JS::MutableHandleValue aErrors)
+{
+  PayerErrorFields errors;
+  errors.Init(mPayerErrors);
+  if (!ToJSValue(aCx, errors, aErrors)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentDetails::GetPaymentMethod(JSContext* aCx, JS::MutableHandleValue aErrors)
+{
+  if (mPaymentMethodErrors.IsEmpty()) {
+    aErrors.set(JS::NullValue());
+    return NS_OK;
+  }
+  nsresult rv = DeserializeToJSValue(mPaymentMethodErrors, aCx ,aErrors);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  return NS_OK;
+}
+
+nsresult
 PaymentDetails::Update(nsIPaymentDetails* aDetails, const bool aRequestShipping)
 {
   MOZ_ASSERT(aDetails);
   /*
    * According to the spec [1], update the attributes if they present in new
    * details (i.e., PaymentDetailsUpdate); otherwise, keep original value.
    * Note |id| comes only from initial details (i.e., PaymentDetailsInit) and
    * |error| only from new details.
@@ -528,24 +559,54 @@ PaymentDetails::Update(nsIPaymentDetails
     mModifiers = modifiers;
   }
 
   rv = aDetails->GetError(mError);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  aDetails->ShippingAddressErrorsJSON(mShippingAddressErrors);
+  PaymentDetails* rowDetails = static_cast<PaymentDetails*>(aDetails);
+  MOZ_ASSERT(rowDetails);
+  mShippingAddressErrors = rowDetails->GetShippingAddressErrors();
+  mPayerErrors = rowDetails->GetPayer();
+  mPaymentMethodErrors = rowDetails->GetPaymentMethod();
+
   return NS_OK;
 
 }
-NS_IMETHODIMP
-PaymentDetails::ShippingAddressErrorsJSON(nsAString& aErrors)
+
+const nsString&
+PaymentDetails::GetShippingAddressErrors() const
+{
+  return mShippingAddressErrors;
+}
+
+const nsString&
+PaymentDetails::GetPayer() const
 {
-  aErrors = mShippingAddressErrors;
+  return mPayerErrors;
+}
+
+const nsString&
+PaymentDetails::GetPaymentMethod() const
+{
+  return mPaymentMethodErrors;
+}
+
+nsresult
+PaymentDetails::UpdateErrors(const nsAString& aError,
+                             const nsAString& aPayerErrors,
+                             const nsAString& aPaymentMethodErrors,
+                             const nsAString& aShippingAddressErrors)
+{
+  mError = aError;
+  mPayerErrors = aPayerErrors;
+  mPaymentMethodErrors = aPaymentMethodErrors;
+  mShippingAddressErrors = aShippingAddressErrors;
   return NS_OK;
 }
 
 /* PaymentOptions */
 
 NS_IMPL_ISUPPORTS(PaymentOptions,
                   nsIPaymentOptions)
 
@@ -696,35 +757,51 @@ PaymentRequest::GetPaymentOptions(nsIPay
 
 NS_IMETHODIMP
 PaymentRequest::GetShippingOption(nsAString& aShippingOption)
 {
   aShippingOption = mShippingOption;
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 PaymentRequest::UpdatePaymentDetails(nsIPaymentDetails* aPaymentDetails,
                                      const nsAString& aShippingOption)
 {
   MOZ_ASSERT(aPaymentDetails);
   bool requestShipping;
   nsresult rv = mPaymentOptions->GetRequestShipping(&requestShipping);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   mShippingOption = aShippingOption;
-  return mPaymentDetails->Update(aPaymentDetails, requestShipping);
+
+  PaymentDetails* rowDetails = static_cast<PaymentDetails*>(mPaymentDetails.get());
+  MOZ_ASSERT(rowDetails);
+  return rowDetails->Update(aPaymentDetails, requestShipping);
 }
 
-NS_IMETHODIMP
+void
 PaymentRequest::SetCompleteStatus(const nsAString& aCompleteStatus)
 {
   mCompleteStatus = aCompleteStatus;
-  return NS_OK;
+}
+
+nsresult
+PaymentRequest::UpdateErrors(const nsAString& aError,
+                             const nsAString& aPayerErrors,
+                             const nsAString& aPaymentMethodErrors,
+                             const nsAString& aShippingAddressErrors)
+{
+  PaymentDetails* rowDetails = static_cast<PaymentDetails*>(mPaymentDetails.get());
+  MOZ_ASSERT(rowDetails);
+  return rowDetails->UpdateErrors(aError,
+                                  aPayerErrors,
+                                  aPaymentMethodErrors,
+                                  aShippingAddressErrors);
 }
 
 NS_IMETHODIMP
 PaymentRequest::GetCompleteStatus(nsAString& aCompleteStatus)
 {
   aCompleteStatus = mCompleteStatus;
   return NS_OK;
 }
--- a/dom/payments/PaymentRequestData.h
+++ b/dom/payments/PaymentRequestData.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PaymentRequestData_h
 #define mozilla_dom_PaymentRequestData_h
 
 #include "nsIPaymentAddress.h"
 #include "nsIPaymentRequest.h"
 #include "nsCOMPtr.h"
-#include "mozilla/dom/PPaymentRequest.h"
+#include "mozilla/dom/PaymentRequestParent.h"
 
 namespace mozilla {
 namespace dom {
 namespace payments {
 
 class PaymentMethodData final : public nsIPaymentMethodData
 {
 public:
@@ -126,34 +126,47 @@ class PaymentDetails final : public nsIP
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTDETAILS
 
 
   static nsresult Create(const IPCPaymentDetails& aIPCDetails,
                          nsIPaymentDetails** aDetails);
+  nsresult Update(nsIPaymentDetails* aDetails, const bool aRequestShipping);
+  const nsString& GetShippingAddressErrors() const;
+  const nsString& GetPayer() const;
+  const nsString& GetPaymentMethod() const;
+  nsresult UpdateErrors(const nsAString& aError,
+                        const nsAString& aPayerErrors,
+                        const nsAString& aPaymentMethodErrors,
+                        const nsAString& aShippingAddressErrors);
+
 private:
   PaymentDetails(const nsAString& aId,
                  nsIPaymentItem* aTotalItem,
                  nsIArray* aDisplayItems,
                  nsIArray* aShippingOptions,
                  nsIArray* aModifiers,
                  const nsAString& aError,
-                 const nsAString& aShippingAddressError);
+                 const nsAString& aShippingAddressError,
+                 const nsAString& aPayerError,
+                 const nsAString& aPaymentMethodError);
 
   ~PaymentDetails() = default;
 
   nsString mId;
   nsCOMPtr<nsIPaymentItem> mTotalItem;
   nsCOMPtr<nsIArray> mDisplayItems;
   nsCOMPtr<nsIArray> mShippingOptions;
   nsCOMPtr<nsIArray> mModifiers;
   nsString mError;
   nsString mShippingAddressErrors;
+  nsString mPayerErrors;
+  nsString mPaymentMethodErrors;
 };
 
 class PaymentOptions final : public nsIPaymentOptions
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTOPTIONS
 
@@ -184,27 +197,54 @@ public:
   PaymentRequest(const uint64_t aTabId,
                  const nsAString& aRequestId,
                  nsIPrincipal* aPrincipal,
                  nsIArray* aPaymentMethods,
                  nsIPaymentDetails* aPaymentDetails,
                  nsIPaymentOptions* aPaymentOptions,
                  const nsAString& aShippingOption);
 
+  void SetIPC(PaymentRequestParent* aIPC)
+  {
+    mIPC = aIPC;
+  }
+
+  PaymentRequestParent* GetIPC() const
+  {
+    return mIPC;
+  }
+
+  nsresult
+  UpdatePaymentDetails(nsIPaymentDetails* aPaymentDetails,
+                       const nsAString& aShippingOption);
+
+  void
+  SetCompleteStatus(const nsAString& aCompleteStatus);
+
+  nsresult
+  UpdateErrors(const nsAString& aError,
+               const nsAString& aPayerErrors,
+               const nsAString& aPaymentMethodErrors,
+               const nsAString& aShippingAddressErrors);
+
 private:
   ~PaymentRequest() = default;
 
   uint64_t mTabId;
   nsString mRequestId;
   nsString mCompleteStatus;
   nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
   nsCOMPtr<nsIArray> mPaymentMethods;
   nsCOMPtr<nsIPaymentDetails> mPaymentDetails;
   nsCOMPtr<nsIPaymentOptions> mPaymentOptions;
   nsString mShippingOption;
+
+  // IPC's life cycle should be controlled by IPC mechanism.
+  // PaymentRequest should not own the reference of it.
+  PaymentRequestParent* mIPC;
 };
 
 class PaymentAddress final : public nsIPaymentAddress
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTADDRESS
 
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -158,32 +158,34 @@ ConvertDetailsInit(JSContext* aCx,
   nsTArray<IPCPaymentDetailsModifier> modifiers;
   nsresult rv = ConvertDetailsBase(aCx, aDetails, displayItems, shippingOptions,
                                    modifiers, aRequestShipping);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Convert |id|
-  nsString id(EmptyString());
+  nsAutoString id;
   if (aDetails.mId.WasPassed()) {
     id = aDetails.mId.Value();
   }
 
   // Convert required |total|
   IPCPaymentItem total;
   ConvertItem(aDetails.mTotal, total);
 
   aIPCDetails = IPCPaymentDetails(id,
                                   total,
                                   displayItems,
                                   shippingOptions,
                                   modifiers,
                                   EmptyString(), // error message
-                                  EmptyString()); // shippingAddressErrors
+                                  EmptyString(), // shippingAddressErrors
+                                  EmptyString(), // payerErrors
+                                  EmptyString()); // paymentMethodErrors
   return NS_OK;
 }
 
 nsresult
 ConvertDetailsUpdate(JSContext* aCx,
                      const PaymentDetailsUpdate& aDetails,
                      IPCPaymentDetails& aIPCDetails,
                      bool aRequestShipping)
@@ -199,33 +201,49 @@ ConvertDetailsUpdate(JSContext* aCx,
     return rv;
   }
 
   // Convert required |total|
   IPCPaymentItem total;
   ConvertItem(aDetails.mTotal, total);
 
   // Convert |error|
-  nsString error(EmptyString());
+  nsAutoString error;
   if (aDetails.mError.WasPassed()) {
     error = aDetails.mError.Value();
   }
 
-  nsString shippingAddressErrors(EmptyString());
+  nsAutoString shippingAddressErrors;
   if (!aDetails.mShippingAddressErrors.ToJSON(shippingAddressErrors)) {
     return NS_ERROR_FAILURE;
   }
 
+  nsAutoString payerErrors;
+  if (!aDetails.mPayerErrors.ToJSON(payerErrors)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsAutoString paymentMethodErrors;
+  if (aDetails.mPaymentMethodErrors.WasPassed()) {
+    JS::RootedObject object(aCx, aDetails.mPaymentMethodErrors.Value());
+    nsresult rv = SerializeFromJSObject(aCx, object, paymentMethodErrors);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
   aIPCDetails = IPCPaymentDetails(EmptyString(), // id
                                   total,
                                   displayItems,
                                   shippingOptions,
                                   modifiers,
                                   error,
-                                  shippingAddressErrors);
+                                  shippingAddressErrors,
+                                  payerErrors,
+                                  paymentMethodErrors);
   return NS_OK;
 }
 
 void
 ConvertOptions(const PaymentOptions& aOptions,
                IPCPaymentOptions& aIPCOption)
 {
   uint8_t shippingTypeIndex = static_cast<uint8_t>(aOptions.mShippingType);
@@ -535,16 +553,54 @@ PaymentRequestManager::ClosePayment(Paym
   }
   nsAutoString requestId;
   aRequest->GetInternalId(requestId);
   IPCPaymentCloseActionRequest action(requestId);
   return SendRequestPayment(aRequest, action, false);
 }
 
 nsresult
+PaymentRequestManager::RetryPayment(JSContext* aCx,
+                                    PaymentRequest* aRequest,
+                                    const PaymentValidationErrors& aErrors)
+{
+  NS_ENSURE_ARG_POINTER(aCx);
+  NS_ENSURE_ARG_POINTER(aRequest);
+
+  nsAutoString requestId;
+  aRequest->GetInternalId(requestId);
+
+  nsAutoString error;
+  if (aErrors.mError.WasPassed()) {
+    error = aErrors.mError.Value();
+  }
+
+  nsAutoString shippingAddressErrors;
+  aErrors.mShippingAddress.ToJSON(shippingAddressErrors);
+
+  nsAutoString payerErrors;
+  aErrors.mPayer.ToJSON(payerErrors);
+
+  nsAutoString paymentMethodErrors;
+  if (aErrors.mPaymentMethod.WasPassed()) {
+    JS::RootedObject object(aCx, aErrors.mPaymentMethod.Value());
+    nsresult rv = SerializeFromJSObject(aCx, object, paymentMethodErrors);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+  IPCPaymentRetryActionRequest action(requestId,
+                                      error,
+                                      payerErrors,
+                                      paymentMethodErrors,
+                                      shippingAddressErrors);
+  return SendRequestPayment(aRequest, action);
+}
+
+nsresult
 PaymentRequestManager::RespondPayment(PaymentRequest* aRequest,
                                       const IPCPaymentActionResponse& aResponse)
 {
   switch (aResponse.type()) {
     case IPCPaymentActionResponse::TIPCPaymentCanMakeActionResponse: {
       const IPCPaymentCanMakeActionResponse& response = aResponse;
       aRequest->RespondCanMakePayment(response.result());
       NotifyRequestDone(aRequest);
--- a/dom/payments/PaymentRequestManager.h
+++ b/dom/payments/PaymentRequestManager.h
@@ -53,16 +53,19 @@ public:
                            const PaymentComplete& aComplete,
                            bool aTimedOut = false);
   nsresult UpdatePayment(JSContext* aCx,
                          PaymentRequest* aRequest,
                          const PaymentDetailsUpdate& aDetails,
                          bool aRequestShipping,
                          bool aDeferredShow);
   nsresult ClosePayment(PaymentRequest* aRequest);
+  nsresult RetryPayment(JSContext* aCx,
+                        PaymentRequest* aRequest,
+                        const PaymentValidationErrors& aErrors);
 
   nsresult RespondPayment(PaymentRequest* aRequest,
                           const IPCPaymentActionResponse& aResponse);
   nsresult ChangeShippingAddress(PaymentRequest* aRequest,
                                  const IPCPaymentAddress& aAddress);
   nsresult ChangeShippingOption(PaymentRequest* aRequest,
                                 const nsAString& aOption);
 
--- a/dom/payments/PaymentRequestModule.cpp
+++ b/dom/payments/PaymentRequestModule.cpp
@@ -1,99 +1,74 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/ModuleUtils.h"
-#include "PaymentActionRequest.h"
 #include "PaymentActionResponse.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
 
 using mozilla::dom::GeneralResponseData;
 using mozilla::dom::BasicCardResponseData;
-using mozilla::dom::PaymentActionRequest;
-using mozilla::dom::PaymentCreateActionRequest;
-using mozilla::dom::PaymentCompleteActionRequest;
-using mozilla::dom::PaymentUpdateActionRequest;
 using mozilla::dom::PaymentCanMakeActionResponse;
 using mozilla::dom::PaymentAbortActionResponse;
 using mozilla::dom::PaymentShowActionResponse;
 using mozilla::dom::PaymentCompleteActionResponse;
 using mozilla::dom::payments::PaymentAddress;
 using mozilla::dom::PaymentRequestService;
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(GeneralResponseData)
 NS_GENERIC_FACTORY_CONSTRUCTOR(BasicCardResponseData)
-NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentActionRequest)
-NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCreateActionRequest)
-NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionRequest)
-NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentUpdateActionRequest)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCanMakeActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAbortActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentShowActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentCompleteActionResponse)
 NS_GENERIC_FACTORY_CONSTRUCTOR(PaymentAddress)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(PaymentRequestService,
                                          PaymentRequestService::GetSingleton)
 
 NS_DEFINE_NAMED_CID(NS_GENERAL_RESPONSE_DATA_CID);
 NS_DEFINE_NAMED_CID(NS_BASICCARD_RESPONSE_DATA_CID);
-NS_DEFINE_NAMED_CID(NS_PAYMENT_ACTION_REQUEST_CID);
-NS_DEFINE_NAMED_CID(NS_PAYMENT_CREATE_ACTION_REQUEST_CID);
-NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_REQUEST_CID);
-NS_DEFINE_NAMED_CID(NS_PAYMENT_UPDATE_ACTION_REQUEST_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ABORT_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_SHOW_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_ADDRESS_CID);
 NS_DEFINE_NAMED_CID(NS_PAYMENT_REQUEST_SERVICE_CID);
 
 static const mozilla::Module::CIDEntry kPaymentRequestCIDs[] = {
   { &kNS_GENERAL_RESPONSE_DATA_CID, false, nullptr, GeneralResponseDataConstructor},
   { &kNS_BASICCARD_RESPONSE_DATA_CID, false, nullptr, BasicCardResponseDataConstructor},
-  { &kNS_PAYMENT_ACTION_REQUEST_CID, false, nullptr, PaymentActionRequestConstructor},
-  { &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID, false, nullptr, PaymentCreateActionRequestConstructor},
-  { &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID, false, nullptr, PaymentCompleteActionRequestConstructor},
-  { &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID, false, nullptr, PaymentUpdateActionRequestConstructor},
   { &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID, false, nullptr, PaymentCanMakeActionResponseConstructor},
   { &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID, false, nullptr, PaymentAbortActionResponseConstructor},
   { &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID, false, nullptr, PaymentShowActionResponseConstructor},
   { &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID, false, nullptr, PaymentCompleteActionResponseConstructor},
   { &kNS_PAYMENT_ADDRESS_CID, false, nullptr, PaymentAddressConstructor},
   { &kNS_PAYMENT_REQUEST_SERVICE_CID, true, nullptr, PaymentRequestServiceConstructor },
   { nullptr }
 };
 
 static const mozilla::Module::ContractIDEntry kPaymentRequestContracts[] = {
   { NS_GENERAL_RESPONSE_DATA_CONTRACT_ID, &kNS_GENERAL_RESPONSE_DATA_CID },
   { NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID, &kNS_BASICCARD_RESPONSE_DATA_CID },
-  { NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_ACTION_REQUEST_CID },
-  { NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_CREATE_ACTION_REQUEST_CID },
-  { NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_REQUEST_CID },
-  { NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID, &kNS_PAYMENT_UPDATE_ACTION_REQUEST_CID },
   { NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_CANMAKE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_ABORT_ACTION_RESPONSE_CID },
   { NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_SHOW_ACTION_RESPONSE_CID },
   { NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID, &kNS_PAYMENT_COMPLETE_ACTION_RESPONSE_CID },
   { NS_PAYMENT_ADDRESS_CONTRACT_ID, &kNS_PAYMENT_ADDRESS_CID },
   { NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID, &kNS_PAYMENT_REQUEST_SERVICE_CID },
   { nullptr }
 };
 
 static const mozilla::Module::CategoryEntry kPaymentRequestCategories[] = {
   { "payment-request", "GeneralResponseData", NS_GENERAL_RESPONSE_DATA_CONTRACT_ID },
   { "payment-request", "BasicCardResponseData", NS_BASICCARD_RESPONSE_DATA_CONTRACT_ID },
-  { "payment-request", "PaymentActionRequest", NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID },
-  { "payment-request", "PaymentCreateActionRequest", NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID },
-  { "payment-request", "PaymentCompleteActionRequest", NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID },
-  { "payment-request", "PaymentUpdateActionRequest", NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID },
   { "payment-request", "PaymentCanMakeActionResponse", NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentAbortActionResponse", NS_PAYMENT_ABORT_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentShowActionResponse", NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentCompleteActionResponse", NS_PAYMENT_COMPLETE_ACTION_RESPONSE_CONTRACT_ID },
   { "payment-request", "PaymentAddress", NS_PAYMENT_ADDRESS_CONTRACT_ID },
   { "payment-request", "PaymentRequestService", NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID },
   { nullptr }
 };
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/PaymentRequestParent.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestService.h"
 #include "BasicCardPayment.h"
 #include "nsSimpleEnumerator.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -156,257 +157,252 @@ PaymentRequestService::LaunchUIAction(co
     uiService = mTestingUIService;
   } else {
     uiService = do_GetService(NS_PAYMENT_UI_SERVICE_CONTRACT_ID, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
   switch (aActionType) {
-    case nsIPaymentActionRequest::SHOW_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentShowActionRequest:{
       rv = uiService->ShowPayment(aRequestId);
       break;
     }
-    case nsIPaymentActionRequest::ABORT_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: {
       rv = uiService->AbortPayment(aRequestId);
       break;
     }
-    case nsIPaymentActionRequest::COMPLETE_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: {
       rv = uiService->CompletePayment(aRequestId);
       break;
     }
-    case nsIPaymentActionRequest::UPDATE_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: {
       rv = uiService->UpdatePayment(aRequestId);
       break;
     }
-    case nsIPaymentActionRequest::CLOSE_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: {
       rv = uiService->ClosePayment(aRequestId);
       break;
     }
     default : {
       return NS_ERROR_FAILURE;
     }
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-PaymentRequestService::RemoveActionCallback(nsIPaymentActionCallback* aCallback)
+nsresult
+PaymentRequestService::RequestPayment(const nsAString& aRequestId,
+                                      const IPCPaymentActionRequest& aAction,
+                                      PaymentRequestParent* aIPC)
 {
-  NS_ENSURE_ARG_POINTER(aCallback);
-  for (auto iter = mCallbackHashtable.Iter(); !iter.Done(); iter.Next()) {
-    nsCOMPtr<nsIPaymentActionCallback> callback = iter.Data();
-    MOZ_ASSERT(callback);
-    if (callback == aCallback) {
-      iter.Remove();
-      return NS_OK;
-    }
-  }
-  return NS_OK;
-}
+  NS_ENSURE_ARG_POINTER(aIPC);
 
-NS_IMETHODIMP
-PaymentRequestService::RequestPayment(nsIPaymentActionRequest* aRequest)
-{
-  NS_ENSURE_ARG_POINTER(aRequest);
+  nsresult rv = NS_OK;
+  uint32_t type = aAction.type();
 
-  nsAutoString requestId;
-  nsresult rv = aRequest->GetRequestId(requestId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  rv = aRequest->GetCallback(getter_AddRefs(callback));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  rv = SetActionCallback(requestId, callback);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  uint32_t type;
-  rv = aRequest->GetType(&type);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  if (type != IPCPaymentActionRequest::TIPCPaymentCreateActionRequest) {
+    nsCOMPtr<nsIPaymentRequest> request;
+    rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    if (!request && type != IPCPaymentActionRequest::TIPCPaymentCloseActionRequest) {
+      return NS_ERROR_FAILURE;
+    }
+    if (request) {
+      payments::PaymentRequest* rowRequest =
+        static_cast<payments::PaymentRequest*>(request.get());
+      if (!rowRequest) {
+        return NS_ERROR_FAILURE;
+      }
+      rowRequest->SetIPC(aIPC);
+    }
   }
 
   switch (type) {
-    case nsIPaymentActionRequest::CREATE_ACTION: {
-      nsCOMPtr<nsIPaymentCreateActionRequest> request =
-        do_QueryInterface(aRequest);
-      MOZ_ASSERT(request);
-      uint64_t tabId;
-      rv = request->GetTabId(&tabId);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIPrincipal> topLevelPrincipal;
-      rv = request->GetTopLevelPrincipal(getter_AddRefs(topLevelPrincipal));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIArray> methodData;
-      rv = request->GetMethodData(getter_AddRefs(methodData));
+    case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest: {
+      const IPCPaymentCreateActionRequest& action = aAction;
+      uint64_t tabId = aIPC->GetTabId();
+      nsCOMPtr<nsIMutableArray> methodData = do_CreateInstance(NS_ARRAY_CONTRACTID);
+      MOZ_ASSERT(methodData);
+      for (IPCPaymentMethodData data : action.methodData()) {
+        nsCOMPtr<nsIPaymentMethodData> method;
+        rv = payments::PaymentMethodData::Create(data, getter_AddRefs(method));
+        NS_ENSURE_SUCCESS(rv, rv);
+        rv = methodData->AppendElement(method);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+      nsCOMPtr<nsIPaymentDetails> details;
+      rv = payments::PaymentDetails::Create(action.details(), getter_AddRefs(details));
       NS_ENSURE_SUCCESS(rv, rv);
-
-      nsCOMPtr<nsIPaymentDetails> details;
-      rv = request->GetDetails(getter_AddRefs(details));
-      NS_ENSURE_SUCCESS(rv, rv);
-
       nsCOMPtr<nsIPaymentOptions> options;
-      rv = request->GetOptions(getter_AddRefs(options));
+      rv = payments::PaymentOptions::Create(action.options(), getter_AddRefs(options));
       NS_ENSURE_SUCCESS(rv, rv);
-
-      nsAutoString shippingOption;
-      rv = request->GetShippingOption(shippingOption);
-      NS_ENSURE_SUCCESS(rv, rv);
-
       nsCOMPtr<nsIPaymentRequest> payment =
-        new payments::PaymentRequest(tabId, requestId, topLevelPrincipal,
-                                     methodData, details, options, shippingOption);
+        new payments::PaymentRequest(tabId,
+                                     aRequestId,
+                                     action.topLevelPrincipal(),
+                                     methodData,
+                                     details,
+                                     options,
+                                     action.shippingOption());
 
       if (!mRequestQueue.AppendElement(payment, mozilla::fallible)) {
         return NS_ERROR_OUT_OF_MEMORY;
       }
       break;
     }
-    case nsIPaymentActionRequest::CANMAKE_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest: {
       nsCOMPtr<nsIPaymentCanMakeActionResponse> canMakeResponse =
         do_CreateInstance(NS_PAYMENT_CANMAKE_ACTION_RESPONSE_CONTRACT_ID);
       MOZ_ASSERT(canMakeResponse);
-
-      rv = canMakeResponse->Init(requestId, CanMakePayment(requestId));
+      rv = canMakeResponse->Init(aRequestId,
+                                 CanMakePayment(aRequestId));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-
       rv = RespondPayment(canMakeResponse.get());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       break;
     }
-    case nsIPaymentActionRequest::SHOW_ACTION: {
-      if (mShowingRequest || !CanMakePayment(requestId)) {
+    case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: {
+      if (mShowingRequest || !CanMakePayment(aRequestId)) {
         uint32_t responseStatus;
         if (mShowingRequest) {
           responseStatus = nsIPaymentActionResponse::PAYMENT_REJECTED;
         } else {
           responseStatus = nsIPaymentActionResponse::PAYMENT_NOTSUPPORTED;
         }
         nsCOMPtr<nsIPaymentShowActionResponse> showResponse =
           do_CreateInstance(NS_PAYMENT_SHOW_ACTION_RESPONSE_CONTRACT_ID);
         MOZ_ASSERT(showResponse);
-        rv = showResponse->Init(requestId,
+        rv = showResponse->Init(aRequestId,
                                 responseStatus,
                                 EmptyString(),
                                 nullptr,
                                 EmptyString(),
                                 EmptyString(),
                                 EmptyString());
         rv = RespondPayment(showResponse.get());
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       } else {
-        rv = GetPaymentRequestById(requestId, getter_AddRefs(mShowingRequest));
+        rv = GetPaymentRequestById(aRequestId,
+                                   getter_AddRefs(mShowingRequest));
         if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NS_ERROR_FAILURE;
+          return rv;
         }
-        rv = LaunchUIAction(requestId, type);
+        rv = LaunchUIAction(aRequestId, type);
         if (NS_WARN_IF(NS_FAILED(rv))) {
-          return NS_ERROR_FAILURE;
+          return rv;
         }
       }
       break;
     }
-    case nsIPaymentActionRequest::ABORT_ACTION: {
-      rv = LaunchUIAction(requestId, type);
+    case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: {
+      rv = LaunchUIAction(aRequestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+        return rv;
       }
       break;
     }
-    case nsIPaymentActionRequest::COMPLETE_ACTION: {
-      nsCOMPtr<nsIPaymentCompleteActionRequest> request =
-        do_QueryInterface(aRequest);
-      MOZ_ASSERT(request);
-      nsAutoString completeStatus;
-      rv = request->GetCompleteStatus(completeStatus);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
-      }
+    case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: {
+      const IPCPaymentCompleteActionRequest& action = aAction;
       nsCOMPtr<nsIPaymentRequest> payment;
-      rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
-      }
-      rv = payment->SetCompleteStatus(completeStatus);
+      rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+        return rv;
       }
-      rv = LaunchUIAction(requestId, type);
+      MOZ_ASSERT(payment);
+      payments::PaymentRequest* rowPayment =
+        static_cast<payments::PaymentRequest*>(payment.get());
+      MOZ_ASSERT(rowPayment);
+      rowPayment->SetCompleteStatus(action.completeStatus());
+      rv = LaunchUIAction(aRequestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+        return rv;
       }
       break;
     }
-    case nsIPaymentActionRequest::UPDATE_ACTION: {
-      nsCOMPtr<nsIPaymentUpdateActionRequest> request = do_QueryInterface(aRequest);
-      MOZ_ASSERT(request);
-
+    case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: {
+      const IPCPaymentUpdateActionRequest& action = aAction;
       nsCOMPtr<nsIPaymentDetails> details;
-      rv = request->GetDetails(getter_AddRefs(details));
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      nsAutoString shippingOption;
-      rv = request->GetShippingOption(shippingOption);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      rv = request->GetRequestId(requestId);
-      NS_ENSURE_SUCCESS(rv, rv);
-      nsCOMPtr<nsIPaymentRequest> payment;
-      rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
+      rv = payments::PaymentDetails::Create(action.details(), getter_AddRefs(details));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-      rv = payment->UpdatePaymentDetails(details, shippingOption);
+      nsCOMPtr<nsIPaymentRequest> payment;
+      rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      MOZ_ASSERT(payment);
+      payments::PaymentRequest* rowPayment =
+        static_cast<payments::PaymentRequest*>(payment.get());
+      MOZ_ASSERT(rowPayment);
+      rv = rowPayment->UpdatePaymentDetails(details, action.shippingOption());
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (mShowingRequest) {
         MOZ_ASSERT(mShowingRequest == payment);
-        rv = LaunchUIAction(requestId, type);
+        rv = LaunchUIAction(aRequestId, type);
       } else {
         mShowingRequest = payment;
-        rv = LaunchUIAction(requestId, nsIPaymentActionRequest::SHOW_ACTION);
+        rv = LaunchUIAction(aRequestId,
+                            IPCPaymentActionRequest::TIPCPaymentShowActionRequest);
       }
       if (NS_WARN_IF(NS_FAILED(rv))) {
-        return NS_ERROR_FAILURE;
+        return rv;
       }
       break;
     }
-    case nsIPaymentActionRequest::CLOSE_ACTION: {
+    case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: {
       nsCOMPtr<nsIPaymentRequest> payment;
-      rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
+      rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
-      rv = LaunchUIAction(requestId, type);
+      rv = LaunchUIAction(aRequestId, type);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (mShowingRequest == payment) {
         mShowingRequest = nullptr;
       }
       mRequestQueue.RemoveElement(payment);
       break;
     }
+    case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: {
+      const IPCPaymentRetryActionRequest& action = aAction;
+      nsCOMPtr<nsIPaymentRequest> payment;
+      rv = GetPaymentRequestById(aRequestId, getter_AddRefs(payment));
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+      MOZ_ASSERT(payment);
+      payments::PaymentRequest* rowPayment =
+        static_cast<payments::PaymentRequest*>(payment.get());
+      MOZ_ASSERT(rowPayment);
+      rowPayment->UpdateErrors(action.error(),
+                               action.payerErrors(),
+                               action.paymentMethodErrors(),
+                               action.shippingAddressErrors());
+      MOZ_ASSERT(mShowingRequest == payment);
+      rv = LaunchUIAction(aRequestId,
+                          IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest);
+      break;
+    }
     default: {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -417,29 +413,28 @@ PaymentRequestService::RespondPayment(ns
   nsresult rv = aResponse->GetRequestId(requestId);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPaymentRequest> request;
   rv = GetPaymentRequestById(requestId, getter_AddRefs(request));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
-
   if (!request) {
     return NS_ERROR_FAILURE;
   }
-
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  if (!mCallbackHashtable.Get(requestId, getter_AddRefs(callback))) {
+  payments::PaymentRequest* rowRequest =
+    static_cast<payments::PaymentRequest*>(request.get());
+  if (!rowRequest) {
     return NS_ERROR_FAILURE;
   }
-  if (NS_WARN_IF(!callback)) {
+  if (!rowRequest->GetIPC()) {
     return NS_ERROR_FAILURE;
   }
-  rv = callback->RespondPayment(aResponse);
+  rv = rowRequest->GetIPC()->RespondPayment(aResponse);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // Remove nsIPaymentRequest from mRequestQueue while receive succeeded abort
   // response or complete response
   uint32_t type;
   rv = aResponse->GetType(&type);
@@ -482,75 +477,67 @@ PaymentRequestService::RespondPayment(ns
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentRequestService::ChangeShippingAddress(const nsAString& aRequestId,
                                              nsIPaymentAddress* aAddress)
 {
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
+  nsCOMPtr<nsIPaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!request) {
     return NS_ERROR_FAILURE;
   }
-  if (NS_WARN_IF(!callback)) {
+  payments::PaymentRequest* rowRequest =
+    static_cast<payments::PaymentRequest*>(request.get());
+  if (!rowRequest) {
     return NS_ERROR_FAILURE;
   }
-
-  nsresult rv = callback->ChangeShippingAddress(aRequestId, aAddress);
+  if (!rowRequest->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = rowRequest->GetIPC()->ChangeShippingAddress(aRequestId, aAddress);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentRequestService::ChangeShippingOption(const nsAString& aRequestId,
                                             const nsAString& aOption)
 {
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
+  nsCOMPtr<nsIPaymentRequest> request;
+  nsresult rv = GetPaymentRequestById(aRequestId, getter_AddRefs(request));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (!request) {
     return NS_ERROR_FAILURE;
   }
-  if (NS_WARN_IF(!callback)) {
+  payments::PaymentRequest* rowRequest =
+    static_cast<payments::PaymentRequest*>(request.get());
+  if (!rowRequest) {
     return NS_ERROR_FAILURE;
   }
-
-  nsresult rv = callback->ChangeShippingOption(aRequestId, aOption);
+  if (!rowRequest->GetIPC()) {
+    return NS_ERROR_FAILURE;
+  }
+  rv = rowRequest->GetIPC()->ChangeShippingOption(aRequestId, aOption);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
-nsresult
-PaymentRequestService::SetActionCallback(const nsAString& aRequestId,
-                                         nsIPaymentActionCallback* aCallback)
-{
-  NS_ENSURE_ARG_POINTER(aCallback);
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  if (mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
-    mCallbackHashtable.Remove(aRequestId);
-  }
-  mCallbackHashtable.Put(aRequestId, aCallback);
-  return NS_OK;
-}
-
-nsresult
-PaymentRequestService::RemoveActionCallback(const nsAString& aRequestId)
-{
-  nsCOMPtr<nsIPaymentActionCallback> callback;
-  if (!mCallbackHashtable.Get(aRequestId, getter_AddRefs(callback))) {
-    return NS_ERROR_FAILURE;
-  }
-  mCallbackHashtable.Remove(aRequestId);
-  return NS_OK;
-}
-
 bool
 PaymentRequestService::CanMakePayment(const nsAString& aRequestId)
 {
   /*
    *  TODO: Check third party payment app support by traversing all
    *        registered third party payment apps.
    */
   return IsBasicCardPayment(aRequestId);
--- a/dom/payments/PaymentRequestService.h
+++ b/dom/payments/PaymentRequestService.h
@@ -29,39 +29,33 @@ public:
 
   static already_AddRefed<PaymentRequestService> GetSingleton();
 
   already_AddRefed<nsIPaymentRequest>
   GetPaymentRequestByIndex(const uint32_t index);
 
   uint32_t NumPayments() const;
 
+  nsresult RequestPayment(const nsAString& aRequestId,
+                          const IPCPaymentActionRequest& aAction,
+                          PaymentRequestParent* aCallback);
 private:
   ~PaymentRequestService() = default;
 
   nsresult
-  SetActionCallback(const nsAString& aRequestId,
-                    nsIPaymentActionCallback* aCallback);
-  nsresult
-  RemoveActionCallback(const nsAString& aRequestId);
-
-  // this method is only used for testing
-  nsresult
   LaunchUIAction(const nsAString& aRequestId, uint32_t aActionType);
 
   bool
   CanMakePayment(const nsAString& aRequestId);
 
   bool
   IsBasicCardPayment(const nsAString& aRequestId);
 
   FallibleTArray<nsCOMPtr<nsIPaymentRequest>> mRequestQueue;
 
-  nsInterfaceHashtable<nsStringHashKey, nsIPaymentActionCallback> mCallbackHashtable;
-
   nsCOMPtr<nsIPaymentUIService> mTestingUIService;
 
   nsCOMPtr<nsIPaymentRequest> mShowingRequest;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
--- a/dom/payments/PaymentResponse.cpp
+++ b/dom/payments/PaymentResponse.cpp
@@ -26,17 +26,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
 NS_INTERFACE_MAP_END
 
 PaymentResponse::PaymentResponse(nsPIDOMWindowInner* aWindow,
                                  PaymentRequest* aRequest,
                                  const nsAString& aRequestId,
                                  const nsAString& aMethodName,
                                  const nsAString& aShippingOption,
-                                 RefPtr<PaymentAddress> aShippingAddress,
+                                 PaymentAddress* aShippingAddress,
                                  const nsAString& aDetails,
                                  const nsAString& aPayerName,
                                  const nsAString& aPayerEmail,
                                  const nsAString& aPayerPhone)
   : mOwner(aWindow)
   , mCompleteCalled(false)
   , mRequest(aRequest)
   , mRequestId(aRequestId)
@@ -178,16 +178,166 @@ PaymentResponse::RespondComplete()
 {
   // mPromise may be null when timing out
   if (mPromise) {
     mPromise->MaybeResolve(JS::UndefinedHandleValue);
     mPromise = nullptr;
   }
 }
 
+already_AddRefed<Promise>
+PaymentResponse::Retry(JSContext* aCx,
+                       const PaymentValidationErrors& aErrors,
+                       ErrorResult& aRv)
+{
+  nsIGlobalObject* global = mOwner->AsGlobal();
+  ErrorResult errResult;
+  RefPtr<Promise> promise = Promise::Create(global, errResult);
+  if (errResult.Failed()) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  if (mTimer) {
+    mTimer->Cancel();
+    mTimer = nullptr;
+  }
+
+  nsIDocument* doc = mOwner->GetExtantDoc();
+  if (!doc || !doc->IsCurrentActiveDocument()) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return promise.forget();
+  }
+
+  if (mCompleteCalled || mRetryPromise) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  nsresult rv = ValidatePaymentValidationErrors(aErrors);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(rv);
+    return promise.forget();
+  }
+
+  MOZ_ASSERT(mRequest);
+  rv = mRequest->RetryPayment(aCx, aErrors);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(rv);
+    return promise.forget();
+  }
+
+  mRetryPromise = promise;
+  return promise.forget();
+}
+
+void
+PaymentResponse::RespondRetry(const nsAString& aMethodName,
+                              const nsAString& aShippingOption,
+                              PaymentAddress* aShippingAddress,
+                              const nsAString& aDetails,
+                              const nsAString& aPayerName,
+                              const nsAString& aPayerEmail,
+                              const nsAString& aPayerPhone)
+{
+  mMethodName = aMethodName;
+  mShippingOption = aShippingOption;
+  mShippingAddress = aShippingAddress;
+  mDetails = aDetails;
+  mPayerName = aPayerName;
+  mPayerEmail = aPayerEmail;
+  mPayerPhone = aPayerPhone;
+
+  NS_NewTimerWithCallback(getter_AddRefs(mTimer),
+                          this,
+                          StaticPrefs::dom_payments_response_timeout(),
+                          nsITimer::TYPE_ONE_SHOT,
+                          mOwner->EventTargetFor(TaskCategory::Other));
+  MOZ_ASSERT(mRetryPromise);
+  mRetryPromise->MaybeResolve(JS::UndefinedHandleValue);
+  mRetryPromise = nullptr;
+}
+
+void
+PaymentResponse::RejectRetry(nsresult aRejectReason)
+{
+  MOZ_ASSERT(mRetryPromise);
+  mRetryPromise->MaybeReject(aRejectReason);
+  mRetryPromise = nullptr;
+}
+
+nsresult
+PaymentResponse::ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors)
+{
+  // Should not be empty errors
+  // check PaymentValidationErrors.error
+  if (aErrors.mError.WasPassed() && !aErrors.mError.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  // check PaymentValidationErrors.payer
+  PayerErrorFields payerErrors(aErrors.mPayer);
+  if (payerErrors.mName.WasPassed() && !payerErrors.mName.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (payerErrors.mEmail.WasPassed() && !payerErrors.mEmail.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (payerErrors.mPhone.WasPassed() && !payerErrors.mPhone.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  // check PaymentValidationErrors.paymentMethod
+  if (aErrors.mPaymentMethod.WasPassed()) {
+    return NS_OK;
+  }
+  // check PaymentValidationErrors.shippingAddress
+  AddressErrors addErrors(aErrors.mShippingAddress);
+  if (addErrors.mAddressLine.WasPassed() &&
+      !addErrors.mAddressLine.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mCity.WasPassed() && !addErrors.mCity.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mCountry.WasPassed() && !addErrors.mCountry.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mDependentLocality.WasPassed() &&
+      !addErrors.mDependentLocality.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mOrganization.WasPassed() &&
+      !addErrors.mOrganization.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mPhone.WasPassed() && !addErrors.mPhone.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mPostalCode.WasPassed() &&
+      !addErrors.mPostalCode.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mRecipient.WasPassed() &&
+      !addErrors.mRecipient.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mRegion.WasPassed() &&
+      !addErrors.mRegion.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mRegionCode.WasPassed() &&
+      !addErrors.mRegionCode.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  if (addErrors.mSortingCode.WasPassed() &&
+      !addErrors.mSortingCode.Value().IsEmpty()) {
+    return NS_OK;
+  }
+  return NS_ERROR_DOM_ABORT_ERR;
+}
+
 NS_IMETHODIMP
 PaymentResponse::Notify(nsITimer *timer)
 {
   mTimer = nullptr;
   if (mCompleteCalled) {
     return NS_OK;
   }
 
--- a/dom/payments/PaymentResponse.h
+++ b/dom/payments/PaymentResponse.h
@@ -28,17 +28,17 @@ public:
 
   NS_IMETHOD Notify(nsITimer* aTimer) override;
 
   PaymentResponse(nsPIDOMWindowInner* aWindow,
                   PaymentRequest* aRequest,
                   const nsAString& aRequestId,
                   const nsAString& aMethodName,
                   const nsAString& aShippingOption,
-                  RefPtr<PaymentAddress> aShippingAddress,
+                  PaymentAddress* aShippingAddress,
                   const nsAString& aDetails,
                   const nsAString& aPayerName,
                   const nsAString& aPayerEmail,
                   const nsAString& aPayerPhone);
 
   nsPIDOMWindowInner* GetParentObject() const
   {
     return mOwner;
@@ -64,19 +64,34 @@ public:
   void GetPayerPhone(nsString& aRetVal) const;
 
   // Return a raw pointer here to avoid refcounting, but make sure it's safe
   // (the object should be kept alive by the callee).
   already_AddRefed<Promise> Complete(PaymentComplete result, ErrorResult& aRv);
 
   void RespondComplete();
 
+  already_AddRefed<Promise> Retry(JSContext* aCx,
+                                  const PaymentValidationErrors& errorField,
+                                  ErrorResult& aRv);
+
+  void RespondRetry(const nsAString& aMethodName,
+                    const nsAString& aShippingOption,
+                    PaymentAddress* aShippingAddress,
+                    const nsAString& aDetails,
+                    const nsAString& aPayerName,
+                    const nsAString& aPayerEmail,
+                    const nsAString& aPayerPhone);
+  void RejectRetry(nsresult aRejectReason);
+
 protected:
   ~PaymentResponse();
 
+  nsresult ValidatePaymentValidationErrors(const PaymentValidationErrors& aErrors);
+
 private:
   nsCOMPtr<nsPIDOMWindowInner> mOwner;
   bool mCompleteCalled;
   PaymentRequest* mRequest;
   nsString mRequestId;
   nsString mMethodName;
   nsString mDetails;
   nsString mShippingOption;
@@ -84,14 +99,16 @@ private:
   nsString mPayerEmail;
   nsString mPayerPhone;
   RefPtr<PaymentAddress> mShippingAddress;
   // Promise for "PaymentResponse::Complete"
   RefPtr<Promise> mPromise;
   // Timer for timing out if the page doesn't call
   // complete()
   nsCOMPtr<nsITimer> mTimer;
+  // Promise for "PaymentResponse::Retry"
+  RefPtr<Promise> mRetryPromise;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_PaymentResponse_h
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -51,16 +51,18 @@ struct IPCPaymentDetails
 {
   nsString id;
   IPCPaymentItem total;
   IPCPaymentItem[] displayItems;
   IPCPaymentShippingOption[] shippingOptions;
   IPCPaymentDetailsModifier[] modifiers;
   nsString error;
   nsString shippingAddressErrors;
+  nsString payerErrors;
+  nsString paymentMethodErrors;
 };
 
 struct IPCPaymentOptions
 {
   bool requestPayerName;
   bool requestPayerEmail;
   bool requestPayerPhone;
   bool requestShipping;
@@ -105,25 +107,35 @@ struct IPCPaymentUpdateActionRequest
   nsString shippingOption;
 };
 
 struct IPCPaymentCloseActionRequest
 {
   nsString requestId;
 };
 
+struct IPCPaymentRetryActionRequest
+{
+  nsString requestId;
+  nsString error;
+  nsString payerErrors;
+  nsString paymentMethodErrors;
+  nsString shippingAddressErrors;
+};
+
 union IPCPaymentActionRequest
 {
   IPCPaymentCreateActionRequest;
   IPCPaymentCanMakeActionRequest;
   IPCPaymentShowActionRequest;
   IPCPaymentAbortActionRequest;
   IPCPaymentCompleteActionRequest;
   IPCPaymentUpdateActionRequest;
   IPCPaymentCloseActionRequest;
+  IPCPaymentRetryActionRequest;
 };
 
 struct IPCPaymentCanMakeActionResponse
 {
   nsString requestId;
   bool result;
 };
 
--- a/dom/payments/ipc/PaymentRequestParent.cpp
+++ b/dom/payments/ipc/PaymentRequestParent.cpp
@@ -2,185 +2,108 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "mozilla/ipc/InputStreamUtils.h"
 #include "nsArrayUtils.h"
 #include "nsCOMPtr.h"
-#include "nsIMutableArray.h"
 #include "nsIPaymentRequestService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
 #include "PaymentRequestData.h"
 #include "PaymentRequestParent.h"
+#include "PaymentRequestService.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_ISUPPORTS(PaymentRequestParent, nsIPaymentActionCallback)
-
 PaymentRequestParent::PaymentRequestParent(uint64_t aTabId)
   : mActorAlive(true)
   , mTabId(aTabId)
+  , mRequestId(EmptyString())
 {
 }
 
 mozilla::ipc::IPCResult
 PaymentRequestParent::RecvRequestPayment(const IPCPaymentActionRequest& aRequest)
 {
   if (!mActorAlive) {
     return IPC_FAIL_NO_REASON(this);
   }
-  nsCOMPtr<nsIPaymentActionRequest> action;
-  nsresult rv;
   switch (aRequest.type()) {
-    case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest: {
+    case IPCPaymentActionRequest::TIPCPaymentCreateActionRequest:{
       const IPCPaymentCreateActionRequest& request = aRequest;
-
-      nsCOMPtr<nsIMutableArray> methodData = do_CreateInstance(NS_ARRAY_CONTRACTID);
-      MOZ_ASSERT(methodData);
-      for (IPCPaymentMethodData data : request.methodData()) {
-        nsCOMPtr<nsIPaymentMethodData> method;
-        rv = payments::PaymentMethodData::Create(data, getter_AddRefs(method));
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return IPC_FAIL_NO_REASON(this);
-        }
-        rv = methodData->AppendElement(method);
-        if (NS_WARN_IF(NS_FAILED(rv))) {
-          return IPC_FAIL_NO_REASON(this);
-        }
-      }
-
-      nsCOMPtr<nsIPaymentDetails> details;
-      rv = payments::PaymentDetails::Create(request.details(), getter_AddRefs(details));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-
-      nsCOMPtr<nsIPaymentOptions> options;
-      rv = payments::PaymentOptions::Create(request.options(), getter_AddRefs(options));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-
-      nsCOMPtr<nsIPaymentCreateActionRequest> createAction =
-        do_CreateInstance(NS_PAYMENT_CREATE_ACTION_REQUEST_CONTRACT_ID);
-      if (NS_WARN_IF(!createAction)) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-      rv = createAction->InitRequest(request.requestId(),
-                                     this,
-                                     mTabId,
-                                     request.topLevelPrincipal(),
-                                     methodData,
-                                     details,
-                                     options,
-                                     request.shippingOption());
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-
-      action = do_QueryInterface(createAction);
-      MOZ_ASSERT(action);
+      mRequestId = request.requestId();
       break;
     }
-    case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest: {
+    case IPCPaymentActionRequest::TIPCPaymentCanMakeActionRequest:{
       const IPCPaymentCanMakeActionRequest& request = aRequest;
-      rv = CreateActionRequest(request.requestId(),
-                               nsIPaymentActionRequest::CANMAKE_ACTION,
-                               getter_AddRefs(action));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
+      mRequestId = request.requestId();
       break;
     }
-    case IPCPaymentActionRequest::TIPCPaymentShowActionRequest: {
+    case IPCPaymentActionRequest::TIPCPaymentShowActionRequest:{
       const IPCPaymentShowActionRequest& request = aRequest;
-      rv = CreateActionRequest(request.requestId(),
-                               nsIPaymentActionRequest::SHOW_ACTION,
-                               getter_AddRefs(action));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
+      mRequestId = request.requestId();
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentAbortActionRequest: {
       const IPCPaymentAbortActionRequest& request = aRequest;
-      rv = CreateActionRequest(request.requestId(),
-                               nsIPaymentActionRequest::ABORT_ACTION,
-                               getter_AddRefs(action));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
+      mRequestId = request.requestId();
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentCompleteActionRequest: {
       const IPCPaymentCompleteActionRequest& request = aRequest;
-      nsCOMPtr<nsIPaymentCompleteActionRequest> completeAction =
-        do_CreateInstance(NS_PAYMENT_COMPLETE_ACTION_REQUEST_CONTRACT_ID);
-      rv = completeAction->InitRequest(request.requestId(),
-                                       this,
-                                       request.completeStatus());
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-      action = do_QueryInterface(completeAction);
-      MOZ_ASSERT(action);
+      mRequestId = request.requestId();
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentUpdateActionRequest: {
       const IPCPaymentUpdateActionRequest& request = aRequest;
-
-      nsCOMPtr<nsIPaymentDetails> details;
-      rv = payments::PaymentDetails::Create(request.details(), getter_AddRefs(details));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
-
-      nsCOMPtr<nsIPaymentUpdateActionRequest> updateAction =
-        do_CreateInstance(NS_PAYMENT_UPDATE_ACTION_REQUEST_CONTRACT_ID);
-      rv = updateAction->InitRequest(request.requestId(),
-                                     this,
-                                     details,
-                                     request.shippingOption());
-      action = do_QueryInterface(updateAction);
-      MOZ_ASSERT(action);
+      mRequestId = request.requestId();
       break;
     }
     case IPCPaymentActionRequest::TIPCPaymentCloseActionRequest: {
       const IPCPaymentCloseActionRequest& request = aRequest;
-      rv = CreateActionRequest(request.requestId(),
-                               nsIPaymentActionRequest::CLOSE_ACTION,
-                               getter_AddRefs(action));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return IPC_FAIL_NO_REASON(this);
-      }
+      mRequestId = request.requestId();
       break;
     }
-    default: {
-      return IPC_FAIL(this, "Unexpected request type");
+    case IPCPaymentActionRequest::TIPCPaymentRetryActionRequest: {
+      const IPCPaymentRetryActionRequest& request = aRequest;
+      mRequestId = request.requestId();
+      break;
+    }
+    default : {
+      return IPC_FAIL(this, "Unknown PaymentRequest action type");
     }
   }
   nsCOMPtr<nsIPaymentRequestService> service =
     do_GetService(NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID);
   MOZ_ASSERT(service);
-  rv = service->RequestPayment(action);
+  PaymentRequestService* rowService =
+    static_cast<PaymentRequestService*>(service.get());
+  MOZ_ASSERT(rowService);
+  nsresult rv = rowService->RequestPayment(mRequestId, aRequest, this);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return IPC_FAIL(this, "nsIPaymentRequestService::RequestPayment failed");
   }
   return IPC_OK();
 }
 
-NS_IMETHODIMP
+uint64_t
+PaymentRequestParent::GetTabId()
+{
+  return mTabId;
+}
+
+nsresult
 PaymentRequestParent::RespondPayment(nsIPaymentActionResponse* aResponse)
 {
   if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIPaymentActionCallback> self = this;
+    RefPtr<PaymentRequestParent> self = this;
     nsCOMPtr<nsIPaymentActionResponse> response = aResponse;
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("PaymentRequestParent::RespondPayment",
                                                      [self, response] ()
     {
       self->RespondPayment(response);
     });
     return NS_DispatchToMainThread(r);
   }
@@ -263,22 +186,22 @@ PaymentRequestParent::RespondPayment(nsI
     }
     default: {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 PaymentRequestParent::ChangeShippingAddress(const nsAString& aRequestId,
                                             nsIPaymentAddress* aAddress)
 {
   if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIPaymentActionCallback> self = this;
+    RefPtr<PaymentRequestParent> self = this;
     nsCOMPtr<nsIPaymentAddress> address = aAddress;
     nsAutoString requestId(aRequestId);
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("dom::PaymentRequestParent::ChangeShippingAddress",
                                                      [self, requestId, address] ()
     {
       self->ChangeShippingAddress(requestId, address);
     });
     return NS_DispatchToMainThread(r);
@@ -345,22 +268,22 @@ PaymentRequestParent::ChangeShippingAddr
 
   nsAutoString requestId(aRequestId);
   if (!SendChangeShippingAddress(requestId, ipcAddress)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
+nsresult
 PaymentRequestParent::ChangeShippingOption(const nsAString& aRequestId,
                                            const nsAString& aOption)
 {
   if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIPaymentActionCallback> self = this;
+    RefPtr<PaymentRequestParent> self = this;
     nsAutoString requestId(aRequestId);
     nsAutoString option(aOption);
     nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction("dom::PaymentRequestParent::ChangeShippingOption",
                                                      [self, requestId, option] ()
     {
       self->ChangeShippingOption(requestId, option);
     });
     return NS_DispatchToMainThread(r);
@@ -385,33 +308,25 @@ PaymentRequestParent::Recv__delete__()
 
 void
 PaymentRequestParent::ActorDestroy(ActorDestroyReason aWhy)
 {
   mActorAlive = false;
   nsCOMPtr<nsIPaymentRequestService> service =
     do_GetService(NS_PAYMENT_REQUEST_SERVICE_CONTRACT_ID);
   MOZ_ASSERT(service);
-  nsresult rv = service->RemoveActionCallback(this);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    MOZ_ASSERT(false);
+  if (!mRequestId.Equals(EmptyString())) {
+    nsCOMPtr<nsIPaymentRequest> request;
+    nsresult rv = service->GetPaymentRequestById(mRequestId, getter_AddRefs(request));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    if (!request) {
+      return;
+    }
+    payments::PaymentRequest* rowRequest =
+      static_cast<payments::PaymentRequest*>(request.get());
+    MOZ_ASSERT(rowRequest);
+    rowRequest->SetIPC(nullptr);
   }
 }
-
-nsresult
-PaymentRequestParent::CreateActionRequest(const nsAString& aRequestId,
-                                          uint32_t aActionType,
-                                          nsIPaymentActionRequest** aAction)
-{
-  NS_ENSURE_ARG_POINTER(aAction);
-  nsCOMPtr<nsIPaymentActionRequest> action =
-    do_CreateInstance(NS_PAYMENT_ACTION_REQUEST_CONTRACT_ID);
-  MOZ_ASSERT(action);
-  nsresult rv = action->Init(aRequestId, aActionType, this);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-  action.forget(aAction);
-  return NS_OK;
-}
-
 } // end of namespace dom
 } // end of namespace mozilla
--- a/dom/payments/ipc/PaymentRequestParent.h
+++ b/dom/payments/ipc/PaymentRequestParent.h
@@ -3,43 +3,46 @@
 /* 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 mozilla_dom_PaymentRequestParent_h
 #define mozilla_dom_PaymentRequestParent_h
 
 #include "mozilla/dom/PPaymentRequestParent.h"
-#include "nsIPaymentActionRequest.h"
+#include "nsIPaymentAddress.h"
+#include "nsIPaymentActionResponse.h"
 
 namespace mozilla {
 namespace dom {
 
-class PaymentRequestParent final : public nsIPaymentActionCallback
-                                 , public PPaymentRequestParent
+class PaymentRequestParent final : public PPaymentRequestParent
 {
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSIPAYMENTACTIONCALLBACK
+  NS_INLINE_DECL_REFCOUNTING(PaymentRequestParent)
+public:
+  explicit PaymentRequestParent(uint64_t aTabId);
 
-  explicit PaymentRequestParent(uint64_t aTabId);
+  uint64_t GetTabId();
+  nsresult RespondPayment(nsIPaymentActionResponse* aResponse);
+  nsresult ChangeShippingAddress(const nsAString& aRequestId,
+                                 nsIPaymentAddress* aAddress);
+  nsresult ChangeShippingOption(const nsAString& aRequestId,
+                                const nsAString& aOption);
 
 protected:
   mozilla::ipc::IPCResult
   RecvRequestPayment(const IPCPaymentActionRequest& aRequest) override;
 
   mozilla::ipc::IPCResult Recv__delete__() override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
 private:
   ~PaymentRequestParent() = default;
 
-  nsresult CreateActionRequest(const nsAString& aRequestId,
-                               uint32_t aActionType,
-                               nsIPaymentActionRequest** aAction);
-
   bool mActorAlive;
   uint64_t mTabId;
+  nsString mRequestId;
 };
 
 } // end of namespace dom
 } // end of namespace mozilla
 
 #endif
--- a/dom/payments/moz.build
+++ b/dom/payments/moz.build
@@ -19,17 +19,16 @@ EXPORTS.mozilla.dom += [
     'PaymentRequest.h',
     'PaymentRequestManager.h',
     'PaymentRequestUpdateEvent.h',
     'PaymentResponse.h',
 ]
 
 UNIFIED_SOURCES += [
     'BasicCardPayment.cpp',
-    'PaymentActionRequest.cpp',
     'PaymentActionResponse.cpp',
     'PaymentAddress.cpp',
     'PaymentMethodChangeEvent.cpp',
     'PaymentRequest.cpp',
     'PaymentRequestData.cpp',
     'PaymentRequestManager.cpp',
     'PaymentRequestModule.cpp',
     'PaymentRequestService.cpp',
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/RetryPaymentChromeScript.js
@@ -0,0 +1,182 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+  sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+  sendAsyncMessage("test-pass", message);
+}
+
+const billingAddress = Cc["@mozilla.org/dom/payments/payment-address;1"].
+                           createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+billingAddress.init("USA",              // country
+                     addressLine,        // address line
+                     "CA",               // region
+                     "San Bruno",        // city
+                     "",                 // dependent locality
+                     "94066",            // postal code
+                     "123456",           // sorting code
+                     "",                 // organization
+                     "Bill A. Pacheco",  // recipient
+                     "+14344413879"); // phone
+
+function acceptPayment(requestId, mode) {
+  const basiccardResponseData = Cc["@mozilla.org/dom/payments/basiccard-response-data;1"].
+                                   createInstance(Ci.nsIBasicCardResponseData);
+  const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                          createInstance(Ci.nsIPaymentShowActionResponse);
+  basiccardResponseData.initData("Bill A. Pacheco",  // cardholderName
+                                 "4916855166538720", // cardNumber
+                                 "01",               // expiryMonth
+                                 "2024",             // expiryYear
+                                 "180",              // cardSecurityCode
+                                 billingAddress);   // billingAddress
+  if (mode === "show") {
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "",                   // payer email
+                      "");                  // payer phone
+  }
+  if (mode == "retry") {
+    showResponse.init(requestId,
+                      Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+                      "basic-card",         // payment method
+                      basiccardResponseData,// payment method data
+                      "Bill A. Pacheco",    // payer name
+                      "bpacheco@test.org",  // payer email
+                      "+123456789");        // payer phone
+  }
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+function rejectPayment(requestId) {
+  const responseData = Cc["@mozilla.org/dom/payments/general-response-data;1"].
+                          createInstance(Ci.nsIGeneralResponseData);
+  responseData.initData({});
+  const showResponse = Cc["@mozilla.org/dom/payments/payment-show-action-response;1"].
+                          createInstance(Ci.nsIPaymentShowActionResponse);
+  showResponse.init(requestId,
+                    Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+                    "",                 // payment method
+                    responseData,       // payment method data
+                    "",                 // payer name
+                    "",                 // payer email
+                    "");                // payer phone
+  paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+}
+
+function checkAddressErrors(testName, errors) {
+  if (!errors) {
+    emitTestFail(`${testName}: Expect non-null shippingAddressErrors, but got null.`);
+    return;
+  }
+  for (const [key, msg] of Object.entries(errors)) {
+    const expected = `${key} error`;
+    if (msg !== expected) {
+      emitTestFail(
+        `${testName}: Expected '${expected}' on shippingAddressErrors.${key}, but got '${msg}'.`
+      );
+      return;
+    }
+  }
+}
+
+function checkPayerErrors(testName, errors) {
+  if (!errors) {
+    emitTestFail(`${testName}: Expect non-null payerErrors, but got null.`);
+    return;
+  }
+  for (const [key, msg] of Object.entries(errors)) {
+    const expected = `${key} error`;
+    if (msg !== expected) {
+      emitTestFail(`${testName}: Expected '${expected}' on payerErrors.${key}, but got '${msg}'.`);
+      return;
+    }
+  }
+}
+
+function checkPaymentMethodErrors(testName, errors) {
+  if (!errors) {
+    emitTestFail(`${testName} :Expect non-null payerMethodErrors, but got null.`);
+    return;
+  }
+  for (const [key, msg] of Object.entries(errors)) {
+    const expected = `method ${key} error`;
+    if (msg !== expected) {
+      emitTestFail(
+        `${testName}: Expected '${expected}' on paymentMethodErrors.${key}, but got '${msg}'.`
+      );
+      return;
+    }
+  }
+}
+
+const DummyUIService = {
+  testName: "",
+  rejectRetry: false,
+  showPayment(requestId) {
+    acceptPayment(requestId, "show");
+  },
+  abortPaymen(requestId) {
+    respondRequestId = requestId
+  },
+  completePayment(requestId) {
+    let completeResponse = Cc["@mozilla.org/dom/payments/payment-complete-action-response;1"].
+                              createInstance(Ci.nsIPaymentCompleteActionResponse);
+    completeResponse.init(requestId, Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED);
+    paymentSrv.respondPayment(completeResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+  },
+  updatePayment(requestId) {
+    const payment = paymentSrv.getPaymentRequestById(requestId);
+    if (payment.paymentDetails.error !== "error") {
+      emitTestFail("Expect 'error' on details.error, but got '" +
+                   payment.paymentDetails.error + "'");
+    }
+    checkAddressErrors(this.testName, payment.paymentDetails.shippingAddressErrors)
+    checkPayerErrors(this.testName, payment.paymentDetails.payer);
+    checkPaymentMethodErrors(this.testName, payment.paymentDetails.paymentMethod);
+    if (this.rejectRetry) {
+      rejectPayment(requestId);
+    } else {
+      acceptPayment(requestId, "retry");
+    }
+  },
+  closePayment: (requestId) => {respondRequestId = requestId},
+  QueryInterface: ChromeUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
+paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+
+addMessageListener("start-test", function(testName) {
+  DummyUIService.testName = testName;
+  sendAsyncMessage("start-test-complete");
+});
+
+addMessageListener("finish-test", function() {
+  DummyUIService.testName = "";
+  sendAsyncMessage("finish-test-complete");
+});
+
+addMessageListener("reject-retry", function() {
+  DummyUIService.rejectRetry = true;
+  sendAsyncMessage("reject-retry-complete");
+});
+
+addMessageListener("teardown", function() {
+  paymentSrv.setTestingUIService(null);
+  sendAsyncMessage('teardown-complete');
+});
--- a/dom/payments/test/mochitest.ini
+++ b/dom/payments/test/mochitest.ini
@@ -9,16 +9,17 @@ support-files =
   BasiccardChromeScript.js
   ClosePaymentChromeScript.js
   ConstructorChromeScript.js
   CurrencyAmountValidationChromeScript.js
   DefaultData.js
   GeneralChromeScript.js
   PMIValidationChromeScript.js
   RequestShippingChromeScript.js
+  RetryPaymentChromeScript.js
   ShippingOptionsChromeScript.js
   ShowPaymentChromeScript.js
   UpdateErrorsChromeScript.js
 
 [test_abortPayment.html]
 run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
 [test_basiccard.html]
 [test_block_none10s.html]
@@ -28,11 +29,12 @@ run-if = nightly_build # Bug 1390737: De
 [test_closePayment.html]
 [test_constructor.html]
 [test_currency_amount_validation.html]
 skip-if = (verify && debug)
 [test_payment-request-in-iframe.html]
 [test_pmi_validation.html]
 skip-if = (verify && debug)
 [test_requestShipping.html]
+[test_retryPayment.html]
 [test_shippingOptions.html]
 [test_showPayment.html]
 [test_update_errors.html]
new file mode 100644
--- /dev/null
+++ b/dom/payments/test/test_retryPayment.html
@@ -0,0 +1,354 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1435161
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for retry PaymentRequest</title>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="DefaultData.js"></script>
+  <script type="application/javascript">
+
+  "use strict";
+  SimpleTest.waitForExplicitFinish();
+
+  const gUrl = SimpleTest.getTestFileURL('RetryPaymentChromeScript.js');
+  const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+  function testFailHandler(message) {
+    ok(false, message);
+  }
+  function testPassHandler(message) {
+    ok(true, message);
+  }
+  gScript.addMessageListener("test-fail", testFailHandler);
+  gScript.addMessageListener("test-pass", testPassHandler);
+
+  async function requestChromeAction(action, params) {
+    gScript.sendAsyncMessage(action, params);
+    await new Promise(resolve => {
+      gScript.addMessageListener(`${action}-complete`, function completeListener() {
+        gScript.removeMessageListener(`${action}-complete`, completeListener);
+        resolve();
+      });
+    });
+  }
+
+  const validationErrors = {
+    error: "error",
+    shippingAddress: {
+      addressLine: "addressLine error",
+      city: "city error",
+      country: "country error",
+      dependentLocality: "dependentLocality error",
+      organization: "organization error",
+      phone: "phone error",
+      postalCode: "postalCode error",
+      recipient: "recipient error",
+      region: "region error",
+      regionCode: "regionCode error",
+      sortingCode: "sortingCode error",
+    },
+    payer: {
+      name: "name error",
+      email: "email error",
+      phone: "phone error",
+    },
+    paymentMethod: {
+      account: "method account error",
+      password: "method password error",
+    },
+  };
+
+  const options = {
+    requestPayerName: true,
+    requestPayerEmail: true,
+    reqeustPayerPhone: true,
+    requestShipping: true,
+    shippingType: "shipping"
+  };
+
+  function checkShowResponse(testName, payResponse) {
+    const { payerName, payerEmail, payerPhone } = payResponse.toJSON();
+    is(
+      payerName,
+      "Bill A. Pacheco",
+      `${testName}: Expected 'Bill A. Pacheco' on payerName, but got '${payerName}' after show PaymentRequest`
+    );
+    is(
+      payerEmail,
+      "",
+      `${testName}: Expected '' on payerEmail, but got '${payerEmail}' after show PaymentRequest`
+    );
+    is(
+      payerPhone,
+      "",
+      `${testName}: Expected '' on payerPhone, but got '${payerPhone}' after show PaymentRequest`
+    );
+  }
+
+  function checkRetryResponse(testName, payResponse) {
+    const { payerName, payerEmail, payerPhone } = payResponse.toJSON();
+    is(
+      payerName,
+      "Bill A. Pacheco",
+      `${testName}: Expected 'Bill A. Pacheco' on payerName, but got '${payerName}' after retry PaymentRequest`
+    );
+    is(
+      payerEmail,
+      "bpacheco@test.org",
+      `${testName} : Expected 'bpacheco@test.org' on payerEmail, but got '${payerEmail}' after retry PaymentRequest`
+    );
+    is(
+      payerPhone,
+      "+123456789",
+      `${testName} : Expected '+123456789' on payerPhone, but got '${payerPhone}' after retry PaymentRequest`
+    );
+  }
+
+  function unexpectedErrMsg(testName, errName, timing) {
+    return `${testName}: Unexpected error(${errName}) when ${timing} the PaymentRequest.`;
+  }
+
+  function expectedErrMsg(testName, expectedErr, errName, timing) {
+    return `${testName}: Expected '${expectedErr}' when ${timing} PaymentResponse, but got '${errName}'.`;
+  }
+
+  async function testRetryAfterComplete() {
+    const testName = "testRetryAfterComplete";
+    await requestChromeAction("start-test", testName);
+    const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+    ok(payRequest, testName + ": failed to create PaymentRequest.");
+    if (!payRequest) {
+      await requestChromeAction("finish-test");
+      return;
+    }
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    let payResponse;
+    try {
+      payResponse = await payRequest.show();
+      await checkShowResponse(testName, payResponse);
+      handler.destruct();
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.Name, "showing"));
+      await requestChromeAction("finish-test");
+      handler.destruct();
+      return;
+    }
+    try {
+      await payResponse.complete("success");
+    } catch(err) {
+      let errName = err.Name;
+      ok(false, unexpectedErrMsg(testName, err.Name, "completing"));
+      await requestChromeAction("finish-test");
+      return;
+    }
+    try {
+      await payResponse.retry(validationErrors);
+      ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+      return;
+    } catch(err) {
+      is(err.name,
+         "InvalidStateError",
+         expectedErrMsg(testName, "InvalidStateError", err.name, "retrying"));
+      await requestChromeAction("finish-test");
+      return;
+    }
+    await requestChromeAction("finish-test");
+  }
+
+  async function testRetryAfterRetry() {
+    const testName = "testRetryAfterRetry";
+    await requestChromeAction("start-test", testName);
+    const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+    ok(payRequest, testName + ": failed to create PaymentRequest.");
+    if (!payRequest) {
+      await requestChromeAction("finish-test");
+      return;
+    }
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    let payResponse;
+    try {
+      payResponse = await payRequest.show();
+      await checkShowResponse(testName, payResponse);
+      handler.destruct();
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+      await requestChromeAction("finish-test");
+      handler.destruct();
+      return;
+    }
+    let retryPromise;
+    try {
+      retryPromise = payResponse.retry(validationErrors);
+      await payResponse.retry(validationErrors);
+      ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+      await requestChromeAction("finish-test");
+      return;
+    } catch(err) {
+      is(err.name,
+         "InvalidStateError",
+         expectedErrMsg(testName, "InvalidStateError", err.name, "retrying"));
+    }
+    try {
+      await retryPromise;
+      await payResponse.complete("success");
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+      await requestChromeAction("finish-test");
+      return;
+    }
+    await requestChromeAction("finish-test");
+  }
+
+  async function testRetryWithEmptyErrors() {
+    const testName = "testRetryWithEmptyErrors";
+    await requestChromeAction("start-test", testName);
+    const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+    ok(payRequest, testName + ": failed to create PaymentRequest.");
+    if (!payRequest) {
+      requestChromeAction("finish-test");
+      return;
+    }
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    let payResponse;
+    try {
+      payResponse = await payRequest.show();
+      await checkShowResponse(testName, payResponse);
+      handler.destruct();
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+      await requestChromeAction("finish-test");
+      handler.destruct();
+      return;
+    }
+    try {
+      await payResponse.retry();
+      ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+      await requestChromeAction("finish-test");
+      return;
+    } catch(err) {
+      is(err.name,
+         "AbortError",
+         expectedErrMsg(testName, "AbortError", err.name, "retrying"));
+    }
+    try {
+      await payResponse.complete("success");
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+      await requestChromeAction("finish-test");
+      return;
+    }
+    await requestChromeAction("finish-test");
+  }
+
+  async function testRetry() {
+    const testName = "testRetry";
+    await requestChromeAction("start-test", testName);
+    const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+    ok(payRequest, testName + ": failed to create PaymentRequest.");
+    if (!payRequest) {
+      await requestChromeAction("finish-test");
+      return;
+    }
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    let payResponse;
+    try {
+      payResponse = await payRequest.show();
+      await checkShowResponse(testName, payResponse);
+      handler.destruct();
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+      await requestChromeAction("finish-test");
+      handler.destruct();
+      return;
+    }
+    try {
+      await payResponse.retry(validationErrors);
+      await checkRetryResponse(testName, payResponse);
+      await payResponse.complete("success");
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "retrying"));
+      await requestChromeAction("finish-test");
+      return;
+    }
+    await requestChromeAction("finish-test");
+  }
+
+  async function testRetryAbortByUser() {
+    const testName = "testRetryAbortByUser";
+    await requestChromeAction("reject-retry");
+    const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+    ok(payRequest, testName + ": failed to create PaymentRequest.");
+    if (!payRequest) {
+      await requestChromeAction("finish-test");
+      return;
+    }
+    const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+    let payResponse;
+    try {
+      payResponse = await payRequest.show();
+      await checkShowResponse(testName, payResponse);
+      handler.destruct();
+    } catch(err) {
+      ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+      handler.destruct();
+      await requestChromeAction("finish-test");
+      return;
+    }
+    try {
+      await payResponse.retry(validationErrors);
+      ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+      await requestChromeAction("finish-test");
+      return;
+    } catch(err) {
+      is(err.name,
+         "AbortError",
+         expectedErrMsg(testName, "AbortError", err.name, "retrying"));
+    }
+    await requestChromeAction("finish-test");
+  }
+
+  function teardown() {
+    return new Promise((resolve, reject) => {
+      gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+        gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+        gScript.removeMessageListener("test-fail", testFailHandler);
+        gScript.removeMessageListener("test-pass", testPassHandler);
+        gScript.destroy();
+        SimpleTest.finish();
+        resolve();
+      });
+      gScript.sendAsyncMessage("teardown");
+    });
+  }
+
+  async function runTests() {
+    try {
+      await testRetryAfterComplete()
+      await testRetryAfterRetry()
+      await testRetryWithEmptyErrors()
+      await testRetry()
+      await testRetryAbortByUser()
+      await teardown()
+    } catch(e) {
+      ok(false, "Unexpected error: " + e.name);
+      SimpleTest.finish();
+    }
+  }
+
+  window.addEventListener('load', function() {
+    SpecialPowers.pushPrefEnv({
+      'set': [
+        ['dom.payments.request.enabled', true],
+      ]
+    }, runTests);
+  });
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1435161">Mozilla Bug 1435161</a>
+</body>
+</html>
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -63,19 +63,34 @@ dictionary AddressErrors {
   DOMString phone;
   DOMString postalCode;
   DOMString recipient;
   DOMString region;
   DOMString regionCode;
   DOMString sortingCode;
 };
 
+dictionary PaymentValidationErrors {
+  PayerErrorFields payer;
+  AddressErrors shippingAddress;
+  DOMString error;
+  object paymentMethod;
+};
+
+dictionary PayerErrorFields {
+  DOMString email;
+  DOMString name;
+  DOMString phone;
+};
+
 dictionary PaymentDetailsUpdate : PaymentDetailsBase {
   DOMString     error;
   AddressErrors shippingAddressErrors;
+  PayerErrorFields payerErrors;
+  object paymentMethodErrors;
   PaymentItem   total;
 };
 
 enum PaymentShippingType {
   "shipping",
   "delivery",
   "pickup"
 };
--- a/dom/webidl/PaymentResponse.webidl
+++ b/dom/webidl/PaymentResponse.webidl
@@ -24,9 +24,13 @@ interface PaymentResponse {
   readonly attribute PaymentAddress? shippingAddress;
   readonly attribute DOMString?      shippingOption;
   readonly attribute DOMString?      payerName;
   readonly attribute DOMString?      payerEmail;
   readonly attribute DOMString?      payerPhone;
 
   [NewObject]
   Promise<void> complete(optional PaymentComplete result = "unknown");
+
+  // If the dictionary argument has no required members, it must be optional.
+  [NewObject]
+  Promise<void> retry(optional PaymentValidationErrors errorFields);
 };
--- a/editor/libeditor/crashtests/1444630.html
+++ b/editor/libeditor/crashtests/1444630.html
@@ -2,18 +2,17 @@
 <html class="reftest-wait">
 <head>
 <script type="text/javascript">
 function load()
 {
   let textarea = document.getElementById("editor");
   textarea.focus();
 
-  SpecialPowers.Cu.import(
-    "chrome://reftest/content/AsyncSpellCheckTestHelper.jsm")
+  SpecialPowers.Cu.import("resource://reftest/AsyncSpellCheckTestHelper.jsm")
   .onSpellCheck(textarea, () => {
     let isc = SpecialPowers.wrap(textarea).editor.getInlineSpellChecker(false);
     let sc = isc.spellChecker;
 
     textarea.setAttribute("lang", "en-US");
     sc.UpdateCurrentDictionary(() => {
       document.documentElement.classList.remove("reftest-wait");
     });
--- a/editor/reftests/xul/autocomplete-ref.xul
+++ b/editor/reftests/xul/autocomplete-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input class="ac" value=" test"/>
--- a/editor/reftests/xul/empty-ref.xul
+++ b/editor/reftests/xul/empty-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input class="empty" value=" test"/>
--- a/editor/reftests/xul/emptyautocomplete-ref.xul
+++ b/editor/reftests/xul/emptyautocomplete-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input class="ac"/>
--- a/editor/reftests/xul/emptymultiline-1.xul
+++ b/editor/reftests/xul/emptymultiline-1.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <textbox multiline="true"/>
--- a/editor/reftests/xul/emptymultiline-2.xul
+++ b/editor/reftests/xul/emptymultiline-2.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <textbox multiline="true" rows="10"/>
--- a/editor/reftests/xul/emptymultiline-ref.xul
+++ b/editor/reftests/xul/emptymultiline-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:textarea rows="10" style="resize: none;"/>
--- a/editor/reftests/xul/emptytextbox-ref.xul
+++ b/editor/reftests/xul/emptytextbox-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input/>
--- a/editor/reftests/xul/number-ref.xul
+++ b/editor/reftests/xul/number-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input value="0" class="num"/>
--- a/editor/reftests/xul/numberwithvalue-ref.xul
+++ b/editor/reftests/xul/numberwithvalue-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input value="123" class="num"/>
--- a/editor/reftests/xul/passwd-ref.xul
+++ b/editor/reftests/xul/passwd-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input type="password" value="test"/>
--- a/editor/reftests/xul/plain-ref.xul
+++ b/editor/reftests/xul/plain-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:div class="plainfield"> test</html:div>
--- a/editor/reftests/xul/textbox-ref.xul
+++ b/editor/reftests/xul/textbox-ref.xul
@@ -1,11 +1,11 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://reftest/content/input.css" type="text/css"?>
+<?xml-stylesheet href="resource://reftest/input.css" type="text/css"?>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="Textbox tests">
 
   <script type="text/javascript" src="platform.js"/>
 
   <html:input value=" test"/>
--- a/gfx/webrender/src/glyph_cache.rs
+++ b/gfx/webrender/src/glyph_cache.rs
@@ -125,25 +125,25 @@ impl GlyphCache {
         // isn't desirable, so we completely remove the hash map instead of clearing it.
         self.glyph_key_caches = FastHashMap::default();
     }
 
     pub fn clear_fonts<F>(&mut self, key_fun: F)
     where
         for<'r> F: Fn(&'r &FontInstance) -> bool,
     {
-        let caches_to_destroy = self.glyph_key_caches
-            .keys()
-            .filter(&key_fun)
-            .cloned()
-            .collect::<Vec<_>>();
-        for key in caches_to_destroy {
-            let mut cache = self.glyph_key_caches.remove(&key).unwrap();
+        self.glyph_key_caches.retain(|k, cache| {
+            let should_clear = key_fun(&k);
+            if !should_clear {
+                return true;
+            }
+
             cache.clear();
-        }
+            false
+        })
     }
 
     // Clear out evicted entries from glyph key caches and, if possible,
     // also remove entirely any subsequently empty glyph key caches.
     fn clear_evicted(&mut self,
                      texture_cache: &TextureCache,
                      render_task_cache: &RenderTaskCache) {
         self.glyph_key_caches.retain(|_, cache| {
--- a/gfx/webrender/src/render_backend.rs
+++ b/gfx/webrender/src/render_backend.rs
@@ -92,61 +92,48 @@ struct Document {
     /// The id of the current frame.
     frame_id: FrameId,
 
     // the `Option` here is only to deal with borrow checker
     frame_builder: Option<FrameBuilder>,
     // A set of pipelines that the caller has requested be
     // made available as output textures.
     output_pipelines: FastHashSet<PipelineId>,
-    // A helper switch to prevent any frames rendering triggered by scrolling
-    // messages between `SetDisplayList` and `GenerateFrame`.
-    // If we allow them, then a reftest that scrolls a few layers before generating
-    // the first frame would produce inconsistent rendering results, because
-    // scroll events are not necessarily received in deterministic order.
-    render_on_scroll: Option<bool>,
 
     /// A data structure to allow hit testing against rendered frames. This is updated
     /// every time we produce a fully rendered frame.
     hit_tester: Option<HitTester>,
 
     /// Properties that are resolved during frame building and can be changed at any time
     /// without requiring the scene to be re-built.
     dynamic_properties: SceneProperties,
 }
 
 impl Document {
     pub fn new(
         window_size: DeviceUintSize,
         layer: DocumentLayer,
-        enable_render_on_scroll: bool,
         default_device_pixel_ratio: f32,
     ) -> Self {
-        let render_on_scroll = if enable_render_on_scroll {
-            Some(false)
-        } else {
-            None
-        };
         Document {
             scene: Scene::new(),
             removed_pipelines: Vec::new(),
             view: DocumentView {
                 window_size,
                 inner_rect: DeviceUintRect::new(DeviceUintPoint::zero(), window_size),
                 layer,
                 pan: DeviceIntPoint::zero(),
                 page_zoom_factor: 1.0,
                 pinch_zoom_factor: 1.0,
                 device_pixel_ratio: default_device_pixel_ratio,
             },
             clip_scroll_tree: ClipScrollTree::new(),
             frame_id: FrameId(0),
             frame_builder: None,
             output_pipelines: FastHashSet::default(),
-            render_on_scroll,
             hit_tester: None,
             dynamic_properties: SceneProperties::new(),
         }
     }
 
     fn can_render(&self) -> bool {
         self.frame_builder.is_some() && self.scene.has_root_pipeline()
     }
@@ -168,39 +155,36 @@ impl Document {
                     self.output_pipelines.insert(pipeline_id);
                 } else {
                     self.output_pipelines.remove(&pipeline_id);
                 }
             }
             FrameMsg::Scroll(delta, cursor) => {
                 profile_scope!("Scroll");
 
-                let mut should_render = true;
                 let node_index = match self.hit_tester {
                     Some(ref hit_tester) => {
                         // Ideally we would call self.scroll_nearest_scrolling_ancestor here, but
                         // we need have to avoid a double-borrow.
                         let test = HitTest::new(None, cursor, HitTestFlags::empty());
                         hit_tester.find_node_under_point(test)
                     }
                     None => {
-                        should_render = false;
                         None
                     }
                 };
 
-                let should_render =
-                    should_render &&
-                    self.scroll_nearest_scrolling_ancestor(delta, node_index) &&
-                    self.render_on_scroll == Some(true);
+                if self.hit_tester.is_some() {
+                    let _scrolled = self.scroll_nearest_scrolling_ancestor(delta, node_index);
+                }
 
                 return DocumentOps {
+                    // TODO: Does it make sense to track this as a scrolling even if we
+                    // ended up not scrolling anything?
                     scroll: true,
-                    build_frame: should_render,
-                    render_frame: should_render,
                     ..DocumentOps::nop()
                 };
             }
             FrameMsg::HitTest(pipeline_id, point, flags, tx) => {
 
                 let result = match self.hit_tester {
                     Some(ref hit_tester) => {
                         hit_tester.hit_test(HitTest::new(pipeline_id, point, flags))
@@ -211,23 +195,20 @@ impl Document {
                 tx.send(result).unwrap();
             }
             FrameMsg::SetPan(pan) => {
                 self.view.pan = pan;
             }
             FrameMsg::ScrollNodeWithId(origin, id, clamp) => {
                 profile_scope!("ScrollNodeWithScrollId");
 
-                let should_render = self.scroll_node(origin, id, clamp)
-                    && self.render_on_scroll == Some(true);
+                let _scrolled = self.scroll_node(origin, id, clamp);
 
                 return DocumentOps {
                     scroll: true,
-                    build_frame: should_render,
-                    render_frame: should_render,
                     ..DocumentOps::nop()
                 };
             }
             FrameMsg::GetScrollNodeState(tx) => {
                 profile_scope!("GetScrollNodeState");
                 tx.send(self.get_scroll_node_state()).unwrap();
             }
             FrameMsg::UpdateDynamicProperties(property_bindings) => {
@@ -345,17 +326,16 @@ impl DocumentOps {
 /// The unique id for WR resource identification.
 static NEXT_NAMESPACE_ID: AtomicUsize = ATOMIC_USIZE_INIT;
 
 #[cfg(any(feature = "capture", feature = "replay"))]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct PlainRenderBackend {
     default_device_pixel_ratio: f32,
-    enable_render_on_scroll: bool,
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, DocumentView>,
     resources: PlainResources,
     last_scene_id: u64,
 }
 
 /// The render backend is responsible for transforming high level display lists into
 /// GPU-friendly work which is then submitted to the renderer in the form of a frame::Frame.
@@ -379,34 +359,32 @@ pub struct RenderBackend {
     frame_config: FrameBuilderConfig,
     documents: FastHashMap<DocumentId, Document>,
 
     notifier: Box<RenderNotifier>,
     recorder: Option<Box<ApiRecordingReceiver>>,
     sampler: Option<Box<AsyncPropertySampler + Send>>,
 
     last_scene_id: u64,
-    enable_render_on_scroll: bool,
 }
 
 impl RenderBackend {
     pub fn new(
         api_rx: MsgReceiver<ApiMsg>,
         payload_rx: Receiver<Payload>,
         result_tx: Sender<ResultMsg>,
         scene_tx: Sender<SceneBuilderRequest>,
         low_priority_scene_tx: Sender<SceneBuilderRequest>,
         scene_rx: Receiver<SceneBuilderResult>,
         default_device_pixel_ratio: f32,
         resource_cache: ResourceCache,
         notifier: Box<RenderNotifier>,
         frame_config: FrameBuilderConfig,
         recorder: Option<Box<ApiRecordingReceiver>>,
         sampler: Option<Box<AsyncPropertySampler + Send>>,
-        enable_render_on_scroll: bool,
     ) -> RenderBackend {
         // The namespace_id should start from 1.
         NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed);
 
         RenderBackend {
             api_rx,
             payload_rx,
             result_tx,
@@ -418,17 +396,16 @@ impl RenderBackend {
             resource_cache,
             gpu_cache: GpuCache::new(),
             frame_config,
             documents: FastHashMap::default(),
             notifier,
             recorder,
             sampler,
             last_scene_id: 0,
-            enable_render_on_scroll,
         }
     }
 
     fn process_scene_msg(
         &mut self,
         document_id: DocumentId,
         message: SceneMsg,
         frame_counter: u32,
@@ -502,20 +479,16 @@ impl RenderBackend {
                     built_display_list,
                     pipeline_id,
                     epoch,
                     background,
                     viewport_size,
                     content_size,
                 });
 
-                if let Some(ref mut ros) = doc.render_on_scroll {
-                    *ros = false; //wait for `GenerateFrame`
-                }
-
                 // Note: this isn't quite right as auxiliary values will be
                 // pulled out somewhere in the prim_store, but aux values are
                 // really simple and cheap to access, so it's not a big deal.
                 let display_list_consumed_time = precise_time_ns();
 
                 ipc_profile_counters.set(
                     builder_start_time,
                     builder_finish_time,
@@ -592,28 +565,26 @@ impl RenderBackend {
 
                         self.resource_cache.add_rasterized_blob_images(
                             replace(&mut txn.rasterized_blobs, Vec::new())
                         );
                         if let Some(rasterizer) = txn.blob_rasterizer.take() {
                             self.resource_cache.set_blob_rasterizer(rasterizer);
                         }
 
-                        if txn.build_frame || !txn.resource_updates.is_empty() || !txn.frame_ops.is_empty() {
-                            self.update_document(
+                        self.update_document(
                             txn.document_id,
-                                replace(&mut txn.resource_updates, Vec::new()),
-                                replace(&mut txn.frame_ops, Vec::new()),
-                                txn.build_frame,
-                                txn.render_frame,
-                                &mut frame_counter,
-                                &mut profile_counters,
-                                has_built_scene,
-                            );
-                        }
+                            replace(&mut txn.resource_updates, Vec::new()),
+                            replace(&mut txn.frame_ops, Vec::new()),
+                            txn.build_frame,
+                            txn.render_frame,
+                            &mut frame_counter,
+                            &mut profile_counters,
+                            has_built_scene,
+                        );
                     },
                     SceneBuilderResult::FlushComplete(tx) => {
                         tx.send(()).ok();
                     }
                     SceneBuilderResult::Stopped => {
                         panic!("We haven't sent a Stop yet, how did we get a Stopped back?");
                     }
                 }
@@ -699,40 +670,32 @@ impl RenderBackend {
             }
             ApiMsg::CloneApi(sender) => {
                 sender.send(self.next_namespace_id()).unwrap();
             }
             ApiMsg::AddDocument(document_id, initial_size, layer) => {
                 let document = Document::new(
                     initial_size,
                     layer,
-                    self.enable_render_on_scroll,
                     self.default_device_pixel_ratio,
                 );
                 self.documents.insert(document_id, document);
             }
             ApiMsg::DeleteDocument(document_id) => {
                 self.documents.remove(&document_id);
                 self.low_priority_scene_tx.send(
                     SceneBuilderRequest::DeleteDocument(document_id)
                 ).unwrap();
             }
             ApiMsg::ExternalEvent(evt) => {
                 self.notifier.external_event(evt);
             }
             ApiMsg::ClearNamespace(namespace_id) => {
                 self.resource_cache.clear_namespace(namespace_id);
-                let document_ids = self.documents
-                    .keys()
-                    .filter(|did| did.0 == namespace_id)
-                    .cloned()
-                    .collect::<Vec<_>>();
-                for document in document_ids {
-                    self.documents.remove(&document);
-                }
+                self.documents.retain(|did, _doc| did.0 != namespace_id);
             }
             ApiMsg::MemoryPressure => {
                 // This is drastic. It will basically flush everything out of the cache,
                 // and the next frame will have to rebuild all of its resources.
                 // We may want to look into something less extreme, but on the other hand this
                 // should only be used in situations where are running low enough on memory
                 // that we risk crashing if we don't do something about it.
                 // The advantage of clearing the cache completely is that it gets rid of any
@@ -951,22 +914,16 @@ impl RenderBackend {
         // TODO: We could avoid some the cost of building the frame by only
         // building the information required for hit-testing (See #2807).
         build_frame |= has_built_scene;
 
         if doc.dynamic_properties.flush_pending_updates() {
             build_frame = true;
         }
 
-        if render_frame {
-            if let Some(ref mut ros) = doc.render_on_scroll {
-                *ros = true;
-            }
-        }
-
         if !doc.can_render() {
             // TODO: this happens if we are building the first scene asynchronously and
             // scroll at the same time. we should keep track of the fact that we skipped
             // composition here and do it as soon as we receive the scene.
             build_frame = false;
             render_frame = false;
         }
 
@@ -1220,17 +1177,16 @@ impl RenderBackend {
         }
 
         debug!("\tresource cache");
         let (resources, deferred) = self.resource_cache.save_capture(&config.root);
 
         info!("\tbackend");
         let backend = PlainRenderBackend {
             default_device_pixel_ratio: self.default_device_pixel_ratio,
-            enable_render_on_scroll: self.enable_render_on_scroll,
             frame_config: self.frame_config.clone(),
             documents: self.documents
                 .iter()
                 .map(|(id, doc)| (*id, doc.view.clone()))
                 .collect(),
             resources,
             last_scene_id: self.last_scene_id,
         };
@@ -1285,17 +1241,16 @@ impl RenderBackend {
         self.gpu_cache = match CaptureConfig::deserialize::<GpuCache, _>(root, "gpu_cache") {
             Some(gpu_cache) => gpu_cache,
             None => GpuCache::new(),
         };
 
         self.documents.clear();
         self.default_device_pixel_ratio = backend.default_device_pixel_ratio;
         self.frame_config = backend.frame_config;
-        self.enable_render_on_scroll = backend.enable_render_on_scroll;
 
         let mut scenes_to_build = Vec::new();
 
         let mut last_scene_id = backend.last_scene_id;
         for (id, view) in backend.documents {
             debug!("\tdocument {:?}", id);
             let scene_name = format!("scene-{}-{}", (id.0).0, id.1);
             let scene = CaptureConfig::deserialize::<Scene, _>(root, &scene_name)
@@ -1304,17 +1259,16 @@ impl RenderBackend {
             let mut doc = Document {
                 scene: scene.clone(),
                 removed_pipelines: Vec::new(),
                 view: view.clone(),
                 clip_scroll_tree: ClipScrollTree::new(),
                 frame_id: FrameId(0),
                 frame_builder: Some(FrameBuilder::empty()),
                 output_pipelines: FastHashSet::default(),
-                render_on_scroll: None,
                 dynamic_properties: SceneProperties::new(),
                 hit_tester: None,
             };
 
             let frame_name = format!("frame-{}-{}", (id.0).0, id.1);
             let frame = CaptureConfig::deserialize::<Frame, _>(root, frame_name);
             let build_frame = match frame {
                 Some(frame) => {
--- a/gfx/webrender/src/renderer.rs
+++ b/gfx/webrender/src/renderer.rs
@@ -453,58 +453,16 @@ pub(crate) mod desc {
             VertexAttribute {
                 name: "aClipDataResourceAddress",
                 count: 4,
                 kind: VertexAttributeKind::U16,
             },
         ],
     };
 
-    pub const BORDER_CORNER_DASH_AND_DOT: VertexDescriptor = VertexDescriptor {
-        vertex_attributes: &[
-            VertexAttribute {
-                name: "aPosition",
-                count: 2,
-                kind: VertexAttributeKind::F32,
-            },
-        ],
-        instance_attributes: &[
-            VertexAttribute {
-                name: "aClipRenderTaskAddress",
-                count: 1,
-                kind: VertexAttributeKind::I32,
-            },
-            VertexAttribute {
-                name: "aScrollNodeId",
-                count: 1,
-                kind: VertexAttributeKind::I32,
-            },
-            VertexAttribute {
-                name: "aClipSegment",
-                count: 1,
-                kind: VertexAttributeKind::I32,
-            },
-            VertexAttribute {
-                name: "aClipDataResourceAddress",
-                count: 4,
-                kind: VertexAttributeKind::U16,
-            },
-            VertexAttribute {
-                name: "aDashOrDot0",
-                count: 4,
-                kind: VertexAttributeKind::F32,
-            },
-            VertexAttribute {
-                name: "aDashOrDot1",
-                count: 4,
-                kind: VertexAttributeKind::F32,
-            },
-        ],
-    };
-
     pub const GPU_CACHE_UPDATE: VertexDescriptor = VertexDescriptor {
         vertex_attributes: &[
             VertexAttribute {
                 name: "aPosition",
                 count: 2,
                 kind: VertexAttributeKind::U16Norm,
             },
             VertexAttribute {
@@ -1344,17 +1302,16 @@ impl LazyInitializedDebugRenderer {
         }
     }
 }
 
 pub struct RendererVAOs {
     prim_vao: VAO,
     blur_vao: VAO,
     clip_vao: VAO,
-    dash_and_dot_vao: VAO,
     border_vao: VAO,
 }
 
 /// The renderer is responsible for submitting to the GPU the work prepared by the
 /// RenderBackend.
 pub struct Renderer {
     result_rx: Receiver<ResultMsg>,
     debug_server: DebugServer,
@@ -1641,18 +1598,16 @@ impl Renderer {
         let gpu_glyph_renderer = try!(GpuGlyphRenderer::new(&mut device,
                                                             &prim_vao,
                                                             options.precache_shaders));
 
         let blur_vao = device.create_vao_with_new_instances(&desc::BLUR, &prim_vao);
         let clip_vao = device.create_vao_with_new_instances(&desc::CLIP, &prim_vao);
         let border_vao =
             device.create_vao_with_new_instances(&desc::BORDER, &prim_vao);
-        let dash_and_dot_vao =
-            device.create_vao_with_new_instances(&desc::BORDER_CORNER_DASH_AND_DOT, &prim_vao);
         let texture_cache_upload_pbo = device.create_pbo();
 
         let texture_resolver = SourceTextureResolver::new(&mut device);
 
         let prim_header_f_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
         let prim_header_i_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAI32);
         let transforms_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
         let render_task_texture = VertexDataTexture::new(&mut device, ImageFormat::RGBAF32);
@@ -1705,17 +1660,16 @@ impl Renderer {
                         if let Some(ref thread_listener) = *thread_listener_for_rayon_end {
                             thread_listener.thread_stopped(&format!("WRWorker#{}", idx));
                         }
                     })
                     .build();
                 Arc::new(worker.unwrap())
             });
         let sampler = options.sampler;
-        let enable_render_on_scroll = options.enable_render_on_scroll;
 
         let blob_image_handler = options.blob_image_handler.take();
         let thread_listener_for_render_backend = thread_listener.clone();
         let thread_listener_for_scene_builder = thread_listener.clone();
         let thread_listener_for_lp_scene_builder = thread_listener.clone();
         let scene_builder_hooks = options.scene_builder_hooks;
         let rb_thread_name = format!("WRRenderBackend#{}", options.renderer_id.unwrap_or(0));
         let scene_thread_name = format!("WRSceneBuilder#{}", options.renderer_id.unwrap_or(0));
@@ -1787,17 +1741,16 @@ impl Renderer {
                 low_priority_scene_tx,
                 scene_rx,
                 device_pixel_ratio,
                 resource_cache,
                 backend_notifier,
                 config,
                 recorder,
                 sampler,
-                enable_render_on_scroll,
             );
             backend.run(backend_profile_counters);
             if let Some(ref thread_listener) = *thread_listener_for_render_backend {
                 thread_listener.thread_stopped(&rb_thread_name);
             }
         })?;
 
         let ext_debug_marker = device.supports_extension("GL_EXT_debug_marker");
@@ -1831,17 +1784,16 @@ impl Renderer {
             enable_clear_scissor: options.enable_clear_scissor,
             last_time: 0,
             gpu_profile,
             gpu_glyph_renderer,
             vaos: RendererVAOs {
                 prim_vao,
                 blur_vao,
                 clip_vao,
-                dash_and_dot_vao,
                 border_vao,
             },
             transforms_texture,
             prim_header_i_texture,
             prim_header_f_texture,
             render_task_texture,
             pipeline_info: PipelineInfo::default(),
             dither_matrix_texture,
@@ -4055,17 +4007,16 @@ impl Renderer {
         self.prim_header_f_texture.deinit(&mut self.device);
         self.prim_header_i_texture.deinit(&mut self.device);
         self.render_task_texture.deinit(&mut self.device);
         self.device.delete_pbo(self.texture_cache_upload_pbo);
         self.texture_resolver.deinit(&mut self.device);
         self.device.delete_vao(self.vaos.prim_vao);
         self.device.delete_vao(self.vaos.clip_vao);
         self.device.delete_vao(self.vaos.blur_vao);
-        self.device.delete_vao(self.vaos.dash_and_dot_vao);
         self.device.delete_vao(self.vaos.border_vao);
 
         #[cfg(feature = "debug_renderer")]
         {
             self.debug.deinit(&mut self.device);
         }
 
         for (_, target) in self.output_targets {
@@ -4235,17 +4186,16 @@ pub struct RendererOptions {
     pub enable_clear_scissor: bool,
     pub max_texture_size: Option<u32>,
     pub scatter_gpu_cache_updates: bool,
     pub upload_method: UploadMethod,
     pub workers: Option<Arc<ThreadPool>>,
     pub blob_image_handler: Option<Box<BlobImageHandler>>,
     pub recorder: Option<Box<ApiRecordingReceiver>>,
     pub thread_listener: Option<Box<ThreadListener + Send + Sync>>,
-    pub enable_render_on_scroll: bool,
     pub cached_programs: Option<Rc<ProgramCache>>,
     pub debug_flags: DebugFlags,
     pub renderer_id: Option<u64>,
     pub disable_dual_source_blending: bool,
     pub scene_builder_hooks: Option<Box<SceneBuilderHooks + Send>>,
     pub sampler: Option<Box<AsyncPropertySampler + Send>>,
     pub chase_primitive: ChasePrimitive,
     pub support_low_priority_transactions: bool,
@@ -4271,17 +4221,16 @@ impl Default for RendererOptions {
             scatter_gpu_cache_updates: false,
             // This is best as `Immediate` on Angle, or `Pixelbuffer(Dynamic)` on GL,
             // but we are unable to make this decision here, so picking the reasonable medium.
             upload_method: UploadMethod::PixelBuffer(VertexUsageHint::Stream),
             workers: None,
             blob_image_handler: None,
             recorder: None,
             thread_listener: None,
-            enable_render_on_scroll: true,
             renderer_id: None,
             cached_programs: None,
             disable_dual_source_blending: false,
             scene_builder_hooks: None,
             sampler: None,
             chase_primitive: ChasePrimitive::Nothing,
             support_low_priority_transactions: false,
         }
--- a/gfx/webrender/src/segment.rs
+++ b/gfx/webrender/src/segment.rs
@@ -390,22 +390,21 @@ impl SegmentBuilder {
 
     // Consume this segment builder and produce a list of segments.
     pub fn build<F>(self, mut f: F) where F: FnMut(&Segment) {
         let bounding_rect = match self.bounding_rect {
             Some(bounding_rect) => bounding_rect,
             None => return,
         };
 
+        let mut items = self.items;
+
         // First, filter out any items that don't intersect
         // with the visible bounding rect.
-        let mut items: Vec<Item> = self.items
-            .into_iter()
-            .filter(|item| item.rect.intersects(&bounding_rect))
-            .collect();
+        items.retain(|item| item.rect.intersects(&bounding_rect));
 
         // Create events for each item
         let mut x_events = Vec::new();
         let mut y_events = Vec::new();
 
         for (item_index, item) in items.iter().enumerate() {
             let p0 = item.rect.origin;
             let p1 = item.rect.bottom_right();
--- a/gfx/webrender_bindings/Moz2DImageRenderer.cpp
+++ b/gfx/webrender_bindings/Moz2DImageRenderer.cpp
@@ -395,16 +395,24 @@ static bool Moz2DRenderCallback(const Ra
       memcpy(&x1, buf + pos + 0 * sizeof(int32_t), sizeof(x1));
       memcpy(&y1, buf + pos + 1 * sizeof(int32_t), sizeof(y1));
       memcpy(&x2, buf + pos + 2 * sizeof(int32_t), sizeof(x2));
       memcpy(&y2, buf + pos + 3 * sizeof(int32_t), sizeof(y2));
       pos += sizeof(int32_t) * 4;
       return IntRectAbsolute(x1, y1, x2, y2);
     }
 
+    layers::BlobFont ReadBlobFont() {
+      MOZ_RELEASE_ASSERT(pos + sizeof(layers::BlobFont) <= len);
+      layers::BlobFont ret;
+      memcpy(&ret, buf + pos, sizeof(ret));
+      pos += sizeof(ret);
+      return ret;
+    }
+
   };
 
   MOZ_RELEASE_ASSERT(aBlob.length() > sizeof(size_t));
   size_t indexOffset = *(size_t*)(aBlob.end().get()-sizeof(size_t));
   MOZ_RELEASE_ASSERT(indexOffset + sizeof(size_t) <= aBlob.length());
   Reader reader(aBlob.begin().get()+indexOffset, aBlob.length()-sizeof(size_t)-indexOffset);
 
   bool ret;
@@ -416,20 +424,22 @@ static bool Moz2DRenderCallback(const Ra
     auto combinedBounds = absBounds.Intersect(reader.ReadBounds());
     if (combinedBounds.IsEmpty()) {
       offset = extra_end;
       continue;
     }
 
     layers::WebRenderTranslator translator(dt);
 
-    size_t count = *(size_t*)(aBlob.begin().get() + end);
+    MOZ_RELEASE_ASSERT(extra_end >= end);
+    MOZ_RELEASE_ASSERT(extra_end < aBlob.length());
+    Reader fontReader(aBlob.begin().get() + end, extra_end - end);
+    size_t count = fontReader.ReadSize();
     for (size_t i = 0; i < count; i++) {
-      layers::BlobFont blobFont =
-        *(layers::BlobFont*)(aBlob.begin() + end + sizeof(count) + sizeof(layers::BlobFont)*i).get();
+      layers::BlobFont blobFont = fontReader.ReadBlobFont();
       RefPtr<ScaledFont> scaledFont = GetScaledFont(&translator, blobFont.mFontInstanceKey);
       translator.AddScaledFont(blobFont.mScaledFontPtr, scaledFont);
     }
 
     Range<const uint8_t> blob(aBlob.begin() + offset, aBlob.begin() + end);
     ret = translator.TranslateRecording((char*)blob.begin().get(), blob.length());
     MOZ_RELEASE_ASSERT(ret);
     offset = extra_end;
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-ced27184ede63268f78ab56453e7ee39007826d7
+27e3bc4293d4a5057346739bfe8b7a163cf068f3
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -933,17 +933,16 @@ pub extern "C" fn wr_window_new(window_i
 
     let opts = RendererOptions {
         enable_aa: true,
         enable_subpixel_aa: true,
         recorder: recorder,
         blob_image_handler: Some(Box::new(Moz2dBlobImageHandler::new(workers.clone()))),
         workers: Some(workers.clone()),
         thread_listener: Some(Box::new(GeckoProfilerThreadListener::new())),
-        enable_render_on_scroll: false,
         resource_override_path: unsafe {
             let override_charptr = gfx_wr_resource_path_override();
             if override_charptr.is_null() {
                 None
             } else {
                 match CStr::from_ptr(override_charptr).to_str() {
                     Ok(override_str) => Some(PathBuf::from(override_str)),
                     _ => None
--- a/js/src/builtin/embedjs.py
+++ b/js/src/builtin/embedjs.py
@@ -35,16 +35,17 @@
 # header file that can be embedded into SpiderMonkey.
 #
 # It uses the C preprocessor to process its inputs.
 
 from __future__ import with_statement
 import re
 import sys
 import os
+import mozpack.path as mozpath
 import subprocess
 import shlex
 import which
 import buildconfig
 
 
 def ToCAsciiArray(lines):
     result = []
@@ -81,18 +82,21 @@ namespace %(namespace)s {
         return %(raw_total_length)i;
     }
 } // selfhosted
 } // js
 """  # NOQA: E501
 
 
 def embed(cxx, preprocessorOption, cppflags, msgs, sources, c_out, js_out, namespace, env):
+    objdir = os.getcwd()
+    # Use relative pathnames to avoid path translation issues in WSL.
     combinedSources = '\n'.join([msgs] + ['#include "%(s)s"' %
-                                          {'s': source} for source in sources])
+                                          {'s': mozpath.relpath(source, objdir)}
+                                          for source in sources])
     args = cppflags + ['-D%(k)s=%(v)s' % {'k': k, 'v': env[k]} for k in env]
     preprocessed = preprocess(cxx, preprocessorOption, combinedSources, args)
     processed = '\n'.join([line for line in preprocessed.splitlines() if
                            (line.strip() and not line.startswith('#'))])
 
     js_out.write(processed)
     import zlib
     compressed = zlib.compress(processed)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/baseline/bug1463375.js
@@ -0,0 +1,5 @@
+// |jit-test| allow-overrecursed
+function f() {
+   f.apply(null, new Array(20000));
+}
+f()
--- a/js/src/jit/arm64/vixl/Simulator-vixl.h
+++ b/js/src/jit/arm64/vixl/Simulator-vixl.h
@@ -2567,17 +2567,17 @@ class Simulator : public DecoderVisitor 
   static int CalcZFlag(uint64_t result) {
     return (result == 0) ? 1 : 0;
   }
 
   static const uint32_t kConditionFlagsMask = 0xf0000000;
 
   // Stack
   byte* stack_;
-  static const int stack_protection_size_ = 128 * KBytes;
+  static const int stack_protection_size_ = 512 * KBytes;
   static const int stack_size_ = (2 * MBytes) + (2 * stack_protection_size_);
   byte* stack_limit_;
 
   Decoder* decoder_;
   // Indicates if the pc has been modified by the instruction and should not be
   // automatically incremented.
   bool pc_modified_;
   const Instruction* pc_;
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -183,16 +183,17 @@ EXPORTS.js += [
     '../public/Utility.h',
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
     '../public/Wrapper.h',
 ]
 
 UNIFIED_SOURCES += [
+    'builtin/Array.cpp',
     'builtin/AtomicsObject.cpp',
     'builtin/Boolean.cpp',
     'builtin/DataViewObject.cpp',
     'builtin/Eval.cpp',
     'builtin/intl/Collator.cpp',
     'builtin/intl/CommonFunctions.cpp',
     'builtin/intl/DateTimeFormat.cpp',
     'builtin/intl/IntlObject.cpp',
@@ -377,16 +378,17 @@ UNIFIED_SOURCES += [
     'vm/ForOfIterator.cpp',
     'vm/GeckoProfiler.cpp',
     'vm/GeneratorObject.cpp',
     'vm/GlobalObject.cpp',
     'vm/HelperThreads.cpp',
     'vm/Id.cpp',
     'vm/Initialization.cpp',
     'vm/Iteration.cpp',
+    'vm/JSAtom.cpp',
     'vm/JSContext.cpp',
     'vm/JSFunction.cpp',
     'vm/JSObject.cpp',
     'vm/JSONParser.cpp',
     'vm/JSONPrinter.cpp',
     'vm/JSScript.cpp',
     'vm/MemoryMetrics.cpp',
     'vm/NativeObject.cpp',
@@ -438,18 +440,16 @@ UNIFIED_SOURCES += [
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
     'wasm/WasmTable.cpp',
     'wasm/WasmTextToBinary.cpp',
     'wasm/WasmTypes.cpp',
     'wasm/WasmValidate.cpp'
 ]
 
-# builtin/Array.cpp and vm/JSAtom.cpp cannot be built in unified mode because
-#   xpcshell is broken during packaging when compiled with gcc-4.8.2
 # builtin/RegExp.cpp cannot be built in unified mode because it causes huge
 #   win32 test slowdowns
 # frontend/Parser.cpp cannot be built in unified mode because of explicit
 #   template instantiations.
 # jsmath.cpp cannot be built in unified mode because it needs to re-#define the
 #   RtlGenRandom declaration's calling convention in <ntsecapi.h> on Windows.
 # jsutil.cpp cannot be built in unified mode because it is needed for
 #   check-vanilla-allocations.
@@ -459,25 +459,23 @@ UNIFIED_SOURCES += [
 # perf/ProfilingStack.cpp cannot be built in unified mode because we want to
 #   suppress warnings due to usage of the system allocator, and this allows it
 #   to have a deterministic object name.
 # util/DoubleToString.cpp cannot be built in unified mode because we want to
 #   suppress compiler warnings in third-party dtoa.c.
 # vm/Interpreter.cpp is gigantic and destroys incremental build times for any
 #   files unlucky enough to be unified with it.
 SOURCES += [
-    'builtin/Array.cpp',
     'builtin/RegExp.cpp',
     'frontend/Parser.cpp',
     'gc/StoreBuffer.cpp',
     'jsmath.cpp',
     'jsutil.cpp',
     'util/DoubleToString.cpp',
     'vm/Interpreter.cpp',
-    'vm/JSAtom.cpp',
     'vm/ProfilingStack.cpp',
 ]
 
 if CONFIG['ENABLE_BIGINT']:
     SOURCES += [
         'builtin/BigInt.cpp',
         'vm/BigIntType.cpp',
     ]
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -1657,22 +1657,16 @@ else
     if ! test -e "$_CL_STDIO_PATH"; then
         AC_MSG_ERROR([Unable to parse cl -showIncludes prefix. This compiler's locale has an unsupported formatting.])
     fi
     if test -z "$CL_INCLUDES_PREFIX"; then
         AC_MSG_ERROR([Cannot find cl -showIncludes prefix.])
     fi
     AC_SUBST(CL_INCLUDES_PREFIX)
     rm -f dummy-hello.c
-
-    dnl Make sure that the build system can handle non-ASCII characters
-    dnl in environment variables to prevent it from breaking silently on
-    dnl non-English systems.
-    NONASCII=$'\241\241'
-    AC_SUBST(NONASCII)
   fi
 fi
 
 dnl ========================================================
 dnl = Link js shell to system readline
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(readline,
 [  --enable-readline       Link js shell to system readline library],
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -423,56 +423,46 @@ static const JSClass parseTaskGlobalClas
     "internal-parse-task-global", JSCLASS_GLOBAL_FLAGS,
     &parseTaskGlobalClassOps
 };
 
 ParseTask::ParseTask(ParseTaskKind kind, JSContext* cx,
                      JS::OffThreadCompileCallback callback, void* callbackData)
   : kind(kind),
     options(cx),
-    alloc(JSContext::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     parseGlobal(nullptr),
     callback(callback), callbackData(callbackData),
-    scripts(cx), sourceObjects(cx),
     overRecursed(false), outOfMemory(false)
 {
+    // Note that |cx| is the main thread context here but the parse task will
+    // run with a different, helper thread, context.
+    MOZ_ASSERT(!cx->helperThread());
+
     MOZ_ALWAYS_TRUE(scripts.reserve(scripts.capacity()));
     MOZ_ALWAYS_TRUE(sourceObjects.reserve(sourceObjects.capacity()));
 }
 
 bool
 ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options, JSObject* global)
 {
+    MOZ_ASSERT(!cx->helperThread());
+
     if (!this->options.copy(cx, options))
         return false;
 
     parseGlobal = global;
     return true;
 }
 
 void
 ParseTask::activate(JSRuntime* rt)
 {
     rt->setUsedByHelperThread(parseGlobal->zone());
 }
 
-bool
-ParseTask::finish(JSContext* cx)
-{
-    for (auto& sourceObject : sourceObjects) {
-        RootedScriptSourceObject sso(cx, sourceObject);
-        if (!ScriptSourceObject::initFromOptions(cx, sso, options))
-            return false;
-        if (!sso->source()->tryCompressOffThread(cx))
-            return false;
-    }
-
-    return true;
-}
-
 ParseTask::~ParseTask() = default;
 
 void
 ParseTask::trace(JSTracer* trc)
 {
     if (parseGlobal->runtimeFromAnyThread() != trc->runtime())
         return;
 
@@ -486,34 +476,35 @@ ParseTask::trace(JSTracer* trc)
     scripts.trace(trc);
     sourceObjects.trace(trc);
 }
 
 size_t
 ParseTask::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
 {
     return options.sizeOfExcludingThis(mallocSizeOf) +
-           alloc.sizeOfExcludingThis(mallocSizeOf) +
            errors.sizeOfExcludingThis(mallocSizeOf);
 }
 
 ScriptParseTask::ScriptParseTask(JSContext* cx, JS::SourceBufferHolder& srcBuf,
                                  JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::Script, cx, callback, callbackData),
     data(std::move(srcBuf))
 {}
 
 void
 ScriptParseTask::parse(JSContext* cx)
 {
+    MOZ_ASSERT(cx->helperThread());
+
     Rooted<ScriptSourceObject*> sourceObject(cx);
 
     ScopeKind scopeKind = options.nonSyntacticScope ? ScopeKind::NonSyntactic : ScopeKind::Global;
 
-    JSScript* script = frontend::CompileGlobalScript(cx, alloc, scopeKind,
+    JSScript* script = frontend::CompileGlobalScript(cx, cx->tempLifoAlloc(), scopeKind,
                                                      options, data,
                                                      /* sourceObjectOut = */ &sourceObject.get());
     if (script)
         scripts.infallibleAppend(script);
     if (sourceObject)
         sourceObjects.infallibleAppend(sourceObject);
 }
 
@@ -521,40 +512,44 @@ ModuleParseTask::ModuleParseTask(JSConte
                                  JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::Module, cx, callback, callbackData),
     data(std::move(srcBuf))
 {}
 
 void
 ModuleParseTask::parse(JSContext* cx)
 {
+    MOZ_ASSERT(cx->helperThread());
+
     Rooted<ScriptSourceObject*> sourceObject(cx);
 
-    JSScript* script = frontend::CompileModule(cx, options, data, alloc, &sourceObject.get());
+    JSScript* script = frontend::CompileModule(cx, options, data, cx->tempLifoAlloc(), &sourceObject.get());
     if (script) {
         scripts.infallibleAppend(script);
         if (sourceObject)
             sourceObjects.infallibleAppend(sourceObject);
     }
 }
 
 ScriptDecodeTask::ScriptDecodeTask(JSContext* cx, const JS::TranscodeRange& range,
                                    JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::ScriptDecode, cx, callback, callbackData),
     range(range)
 {}
 
 void
 ScriptDecodeTask::parse(JSContext* cx)
 {
+    MOZ_ASSERT(cx->helperThread());
+
     RootedScript resultScript(cx);
     Rooted<ScriptSourceObject*> sourceObject(cx);
 
-    XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject.get(),
-                                range);
+    XDROffThreadDecoder decoder(cx, cx->tempLifoAlloc(), &options,
+                                /* sourceObjectOut = */ &sourceObject.get(), range);
     XDRResult res = decoder.codeScript(&resultScript);
     MOZ_ASSERT(bool(resultScript) == res.isOk());
     if (res.isOk()) {
         scripts.infallibleAppend(resultScript);
         if (sourceObject)
             sourceObjects.infallibleAppend(sourceObject);
     }
 }
@@ -565,19 +560,21 @@ BinASTDecodeTask::BinASTDecodeTask(JSCon
                                    JS::OffThreadCompileCallback callback, void* callbackData)
   : ParseTask(ParseTaskKind::BinAST, cx, callback, callbackData),
     data(buf, length)
 {}
 
 void
 BinASTDecodeTask::parse(JSContext* cx)
 {
+    MOZ_ASSERT(cx->helperThread());
+
     RootedScriptSourceObject sourceObject(cx);
 
-    JSScript* script = frontend::CompileGlobalBinASTScript(cx, alloc, options,
+    JSScript* script = frontend::CompileGlobalBinASTScript(cx, cx->tempLifoAlloc(), options,
                                                            data.begin().get(), data.length(),
                                                            &sourceObject.get());
     if (script) {
         scripts.infallibleAppend(script);
         if (sourceObject)
             sourceObjects.infallibleAppend(sourceObject);
     }
 }
@@ -589,30 +586,34 @@ MultiScriptsDecodeTask::MultiScriptsDeco
                                                void* callbackData)
   : ParseTask(ParseTaskKind::MultiScriptsDecode, cx, callback, callbackData),
     sources(&sources)
 {}
 
 void
 MultiScriptsDecodeTask::parse(JSContext* cx)
 {
+    MOZ_ASSERT(cx->helperThread());
+
     if (!scripts.reserve(sources->length()) ||
         !sourceObjects.reserve(sources->length()))
     {
+        ReportOutOfMemory(cx); // This sets |outOfMemory|.
         return;
     }
 
     for (auto& source : *sources) {
         CompileOptions opts(cx, options);
         opts.setFileAndLine(source.filename, source.lineno);
 
         RootedScript resultScript(cx);
         Rooted<ScriptSourceObject*> sourceObject(cx);
 
-        XDROffThreadDecoder decoder(cx, alloc, &opts, &sourceObject.get(), source.range);
+        XDROffThreadDecoder decoder(cx, cx->tempLifoAlloc(), &opts, &sourceObject.get(),
+                                    source.range);
         XDRResult res = decoder.codeScript(&resultScript);
         MOZ_ASSERT(bool(resultScript) == res.isOk());
 
         if (res.isErr())
             break;
         MOZ_ASSERT(resultScript);
         scripts.infallibleAppend(resultScript);
         sourceObjects.infallibleAppend(sourceObject);
@@ -1642,115 +1643,114 @@ GlobalHelperThreadState::removeFinishedP
     }
     MOZ_ASSERT(found);
 #endif
 
     task->remove();
     return task;
 }
 
-template <typename F, typename>
-bool
-GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
-                                         JS::OffThreadToken* token, F&& finishCallback)
+UniquePtr<ParseTask>
+GlobalHelperThreadState::finishParseTaskCommon(JSContext* cx, ParseTaskKind kind,
+                                               JS::OffThreadToken* token)
 {
+    MOZ_ASSERT(!cx->helperThread());
     MOZ_ASSERT(cx->realm());
 
     Rooted<UniquePtr<ParseTask>> parseTask(cx, removeFinishedParseTask(kind, token));
 
     // Make sure we have all the constructors we need for the prototype
     // remapping below, since we can't GC while that's happening.
     if (!EnsureParserCreatedClasses(cx, kind)) {
         LeaveParseTaskZone(cx->runtime(), parseTask.get().get());
-        return false;
+        return nullptr;
     }
 
     mergeParseTaskRealm(cx, parseTask.get().get(), cx->realm());
 
-    bool ok = finishCallback(parseTask.get().get());
-
     for (auto& script : parseTask->scripts)
         cx->releaseCheck(script);
 
-    if (!parseTask->finish(cx) || !ok)
-        return false;
+    for (auto& sourceObject : parseTask->sourceObjects) {
+        RootedScriptSourceObject sso(cx, sourceObject);
+        if (!ScriptSourceObject::initFromOptions(cx, sso, parseTask->options))
+            return nullptr;
+        if (!sso->source()->tryCompressOffThread(cx))
+            return nullptr;
+    }
 
     // Report out of memory errors eagerly, or errors could be malformed.
     if (parseTask->outOfMemory) {
         ReportOutOfMemory(cx);
-        return false;
+        return nullptr;
     }
 
     // Report any error or warnings generated during the parse.
     for (size_t i = 0; i < parseTask->errors.length(); i++)
         parseTask->errors[i]->throwError(cx);
     if (parseTask->overRecursed)
         ReportOverRecursed(cx);
     if (cx->isExceptionPending())
-        return false;
-
-    return true;
+        return nullptr;
+
+    return std::move(parseTask.get());
 }
 
 JSScript*
-GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
-                                         JS::OffThreadToken* token)
+GlobalHelperThreadState::finishSingleParseTask(JSContext* cx, ParseTaskKind kind,
+                                               JS::OffThreadToken* token)
 {
     JS::RootedScript script(cx);
 
-    bool ok = finishParseTask(cx, kind, token, [&script] (ParseTask* parseTask) {
-        MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
-
-        if (parseTask->scripts.length() > 0)
-            script = parseTask->scripts[0];
-
-        return true;
-    });
-
-    if (!ok)
+    Rooted<UniquePtr<ParseTask>> parseTask(cx, finishParseTaskCommon(cx, kind, token));
+    if (!parseTask) {
         return nullptr;
+    }
+
+    MOZ_RELEASE_ASSERT(parseTask->scripts.length() <= 1);
+
+    if (parseTask->scripts.length() > 0) {
+        script = parseTask->scripts[0];
+    }
 
     if (!script) {
         // No error was reported, but no script produced. Assume we hit out of
         // memory.
         MOZ_ASSERT(false, "Expected script");
         ReportOutOfMemory(cx);
         return nullptr;
     }
 
     // The Debugger only needs to be told about the topmost script that was compiled.
     Debugger::onNewScript(cx, script);
 
     return script;
 }
 
 bool
-GlobalHelperThreadState::finishParseTask(JSContext* cx, ParseTaskKind kind,
-                                         JS::OffThreadToken* token,
-                                         MutableHandle<ScriptVector> scripts)
+GlobalHelperThreadState::finishMultiParseTask(JSContext* cx, ParseTaskKind kind,
+                                              JS::OffThreadToken* token,
+                                              MutableHandle<ScriptVector> scripts)
 {
-    size_t expectedLength = 0;
-
-    bool ok = finishParseTask(cx, kind, token, [&scripts, &expectedLength] (ParseTask* parseTask) {
-        MOZ_ASSERT(parseTask->kind == ParseTaskKind::MultiScriptsDecode);
-        auto task = static_cast<MultiScriptsDecodeTask*>(parseTask);
-
-        expectedLength = task->sources->length();
-
-        if (!scripts.reserve(task->scripts.length()))
-            return false;
-
-        for (auto& script : task->scripts)
-            scripts.infallibleAppend(script);
-        return true;
-    });
-
-    if (!ok)
+    Rooted<UniquePtr<ParseTask>> parseTask(cx, finishParseTaskCommon(cx, kind, token));
+    if (!parseTask)
         return false;
 
+    MOZ_ASSERT(parseTask->kind == ParseTaskKind::MultiScriptsDecode);
+    auto task = static_cast<MultiScriptsDecodeTask*>(parseTask.get().get());
+    size_t expectedLength = task->sources->length();
+
+    if (!scripts.reserve(parseTask->scripts.length())) {
+        ReportOutOfMemory(cx);
+        return false;
+    }
+
+    for (auto& script : parseTask->scripts)
+        scripts.infallibleAppend(script);
+
     if (scripts.length() != expectedLength) {
         // No error was reported, but fewer scripts produced than expected.
         // Assume we hit out of memory.
         MOZ_ASSERT(false, "Expected more scripts");
         ReportOutOfMemory(cx);
         return false;
     }
 
@@ -1764,52 +1764,52 @@ GlobalHelperThreadState::finishParseTask
     }
 
     return true;
 }
 
 JSScript*
 GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, JS::OffThreadToken* token)
 {
-    JSScript* script = finishParseTask(cx, ParseTaskKind::Script, token);
+    JSScript* script = finishSingleParseTask(cx, ParseTaskKind::Script, token);
     MOZ_ASSERT_IF(script, script->isGlobalCode());
     return script;
 }
 
 JSScript*
 GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, JS::OffThreadToken* token)
 {
-    JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
+    JSScript* script = finishSingleParseTask(cx, ParseTaskKind::ScriptDecode, token);
     MOZ_ASSERT_IF(script, script->isGlobalCode());
     return script;
 }
 
 #if defined(JS_BUILD_BINAST)
 
 JSScript*
 GlobalHelperThreadState::finishBinASTDecodeTask(JSContext* cx, JS::OffThreadToken* token)
 {
-    JSScript* script = finishParseTask(cx, ParseTaskKind::BinAST, token);
+    JSScript* script = finishSingleParseTask(cx, ParseTaskKind::BinAST, token);
     MOZ_ASSERT_IF(script, script->isGlobalCode());
     return script;
 }
 
 #endif /* JS_BUILD_BINAST */
 
 bool
 GlobalHelperThreadState::finishMultiScriptsDecodeTask(JSContext* cx, JS::OffThreadToken* token,
                                                       MutableHandle<ScriptVector> scripts)
 {
-    return finishParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
+    return finishMultiParseTask(cx, ParseTaskKind::MultiScriptsDecode, token, scripts);
 }
 
 JSScript*
 GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, JS::OffThreadToken* token)
 {
-    RootedScript script(cx, finishParseTask(cx, ParseTaskKind::Module, token));
+    RootedScript script(cx, finishSingleParseTask(cx, ParseTaskKind::Module, token));
     if (!script)
         return nullptr;
 
     MOZ_ASSERT(script->module());
 
     RootedModuleObject module(cx, script->module());
     module->fixEnvironmentsAfterCompartmentMerge();
     if (!ModuleObject::Freeze(cx, module))
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -274,40 +274,33 @@ class GlobalHelperThreadState
     bool canStartIonFreeTask(const AutoLockHelperThreadState& lock);
     bool canStartParseTask(const AutoLockHelperThreadState& lock);
     bool canStartCompressionTask(const AutoLockHelperThreadState& lock);
     bool canStartGCParallelTask(const AutoLockHelperThreadState& lock);
 
     // Used by a major GC to signal processing enqueued compression tasks.
     void startHandlingCompressionTasks(const AutoLockHelperThreadState&);
 
+    jit::IonBuilder* highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock);
   private:
     void scheduleCompressionTasks(const AutoLockHelperThreadState&);
 
-  public:
-    jit::IonBuilder* highestPriorityPendingIonCompile(const AutoLockHelperThreadState& lock);
+    UniquePtr<ParseTask> finishParseTaskCommon(JSContext* cx, ParseTaskKind kind,
+                                               JS::OffThreadToken* token);
 
-    template <
-        typename F,
-        typename = typename mozilla::EnableIf<
-            // Matches when the type is a function or lambda with the signature `bool(ParseTask*)`
-            mozilla::IsSame<bool, decltype((*(F*)nullptr)((ParseTask*)nullptr))>::value
-        >::Type
-    >
-    bool finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token, F&& finishCallback);
+    JSScript* finishSingleParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token);
+    bool finishMultiParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token,
+                              MutableHandle<ScriptVector> scripts);
 
-    JSScript* finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token);
+    void mergeParseTaskRealm(JSContext* cx, ParseTask* parseTask, JS::Realm* dest);
 
-    bool finishParseTask(JSContext* cx, ParseTaskKind kind, JS::OffThreadToken* token, MutableHandle<ScriptVector> scripts);
-
+  public:
     void cancelParseTask(JSRuntime* rt, ParseTaskKind kind, JS::OffThreadToken* token);
     void destroyParseTask(JSRuntime* rt, ParseTask* parseTask);
 
-    void mergeParseTaskRealm(JSContext* cx, ParseTask* parseTask, JS::Realm* dest);
-
     void trace(JSTracer* trc);
 
     JSScript* finishScriptParseTask(JSContext* cx, JS::OffThreadToken* token);
     JSScript* finishScriptDecodeTask(JSContext* cx, JS::OffThreadToken* token);
     bool finishMultiScriptsDecodeTask(JSContext* cx, JS::OffThreadToken* token, MutableHandle<ScriptVector> scripts);
     JSScript* finishModuleParseTask(JSContext* cx, JS::OffThreadToken* token);
 
 #if defined(JS_BUILD_BINAST)
@@ -685,48 +678,45 @@ class MOZ_RAII AutoUnlockHelperThreadSta
     }
 };
 
 struct ParseTask : public mozilla::LinkedListElement<ParseTask>, public JS::OffThreadToken
 {
     ParseTaskKind kind;
     JS::OwningCompileOptions options;
 
-    LifoAlloc alloc;
-
     // The global object to use while parsing.
     JSObject* parseGlobal;
 
     // Callback invoked off thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void* callbackData;
 
     // Holds the final scripts between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
-    GCVector<JSScript*, 1> scripts;
+    GCVector<JSScript*, 1, SystemAllocPolicy> scripts;
 
     // Holds the ScriptSourceObjects generated for the script compilation.
-    GCVector<ScriptSourceObject*, 1> sourceObjects;
+    GCVector<ScriptSourceObject*, 1, SystemAllocPolicy> sourceObjects;
 
     // Any errors or warnings produced during compilation. These are reported
     // when finishing the script.
     Vector<UniquePtr<CompileError>, 0, SystemAllocPolicy> errors;
     bool overRecursed;
     bool outOfMemory;
 
     ParseTask(ParseTaskKind kind, JSContext* cx,
               JS::OffThreadCompileCallback callback, void* callbackData);
     virtual ~ParseTask();
 
     bool init(JSContext* cx, const JS::ReadOnlyCompileOptions& options, JSObject* global);
 
     void activate(JSRuntime* rt);
     virtual void parse(JSContext* cx) = 0;
-    bool finish(JSContext* cx);
 
     bool runtimeMatches(JSRuntime* rt) {
         return parseGlobal->runtimeFromAnyThread() == rt;
     }
 
     void trace(JSTracer* trc);
 
     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
--- a/js/xpconnect/src/xpc.msg
+++ b/js/xpconnect/src/xpc.msg
@@ -199,16 +199,17 @@ XPC_MSG_DEF(NS_ERROR_ILLEGAL_INPUT      
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_NOT_SIGNED          , "The JAR is not signed.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_MODIFIED_ENTRY      , "An entry in the JAR has been modified after the JAR was signed.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_UNSIGNED_ENTRY      , "An entry in the JAR has not been signed.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_MISSING       , "An entry is missing from the JAR file.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_WRONG_SIGNATURE     , "The JAR's signature is wrong.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_TOO_LARGE     , "An entry in the JAR is too large.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_ENTRY_INVALID       , "An entry in the JAR is invalid.")
 XPC_MSG_DEF(NS_ERROR_SIGNED_JAR_MANIFEST_INVALID    , "The JAR's manifest or signature file is invalid.")
+XPC_MSG_DEF(NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO     , "The PKCS#7 signature is malformed or invalid.")
 XPC_MSG_DEF(NS_ERROR_CMS_VERIFY_NOT_SIGNED          , "The PKCS#7 information is not signed.")
 
 /* Codes related to signed manifests */
 XPC_MSG_DEF(NS_ERROR_SIGNED_APP_MANIFEST_INVALID   , "The signed app manifest or signature file is invalid.")
 
 /* Codes for printing-related errors. */
 XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_NO_PRINTER_AVAILABLE , "No printers available.")
 XPC_MSG_DEF(NS_ERROR_GFX_PRINTER_NAME_NOT_FOUND       , "The selected printer could not be found.")
@@ -231,9 +232,9 @@ XPC_MSG_DEF(NS_ERROR_DOM_NOT_FOUND_ERR  
 XPC_MSG_DEF(NS_ERROR_DOM_NOT_ALLOWED_ERR              , "The request is not allowed.")
 
 /* Codes related to the URIClassifier service */
 XPC_MSG_DEF(NS_ERROR_MALWARE_URI                      , "The URI is malware")
 XPC_MSG_DEF(NS_ERROR_PHISHING_URI                     , "The URI is phishing")
 XPC_MSG_DEF(NS_ERROR_TRACKING_URI                     , "The URI is tracking")
 XPC_MSG_DEF(NS_ERROR_UNWANTED_URI                     , "The URI is unwanted")
 XPC_MSG_DEF(NS_ERROR_BLOCKED_URI                      , "The URI is blocked")
-XPC_MSG_DEF(NS_ERROR_HARMFUL_URI                      , "The URI is harmful")
\ No newline at end of file
+XPC_MSG_DEF(NS_ERROR_HARMFUL_URI                      , "The URI is harmful")
--- a/layout/generic/TextOverflow.cpp
+++ b/layout/generic/TextOverflow.cpp
@@ -365,32 +365,32 @@ TextOverflow::TextOverflow(nsDisplayList
   } else {
     mIStart.Init(style->mTextOverflow.GetRight(direction));
     mIEnd.Init(style->mTextOverflow.GetLeft(direction));
   }
   // The left/right marker string is setup in ExamineLineFrames when a line
   // has overflow on that side.
 }
 
-/* static */ UniquePtr<TextOverflow>
+/* static */ Maybe<TextOverflow>
 TextOverflow::WillProcessLines(nsDisplayListBuilder*   aBuilder,
                                nsIFrame*               aBlockFrame)
 {
   // Ignore 'text-overflow' for event and frame visibility processing.
   if (aBuilder->IsForEventDelivery() ||
       aBuilder->IsForFrameVisibility() ||
       !CanHaveTextOverflow(aBlockFrame)) {
-    return nullptr;
+    return Nothing();
   }
   nsIScrollableFrame* scrollableFrame = nsLayoutUtils::GetScrollableFrameFor(aBlockFrame);
   if (scrollableFrame && scrollableFrame->IsTransformingByAPZ()) {
     // If the APZ is actively scrolling this, don't bother with markers.
-    return nullptr;
+    return Nothing();
   }
-  return MakeUnique<TextOverflow>(aBuilder, aBlockFrame);
+  return Some(TextOverflow(aBuilder, aBlockFrame));
 }
 
 void
 TextOverflow::ExamineFrameSubtree(nsIFrame*       aFrame,
                                   const LogicalRect& aContentArea,
                                   const LogicalRect& aInsideMarkersArea,
                                   FrameHashtable* aFramesToHide,
                                   AlignmentEdges* aAlignmentEdges,
--- a/layout/generic/TextOverflow.h
+++ b/layout/generic/TextOverflow.h
@@ -22,34 +22,41 @@ namespace mozilla {
 namespace css {
 
 /**
  * A class for rendering CSS3 text-overflow.
  * Usage:
  *  1. allocate an object using WillProcessLines
  *  2. then call ProcessLine for each line you are building display lists for
  */
-class MOZ_HEAP_CLASS TextOverflow final {
+class TextOverflow final {
  public:
   /**
    * Allocate an object for text-overflow processing.
    * @return nullptr if no processing is necessary.  The caller owns the object.
    */
-  static UniquePtr<TextOverflow>
+  static Maybe<TextOverflow>
   WillProcessLines(nsDisplayListBuilder* aBuilder,
                    nsIFrame*             aBlockFrame);
 
   /**
    * Constructor, which client code SHOULD NOT use directly. Instead, clients
    * should call WillProcessLines(), which is basically the factory function
    * for TextOverflow instances.
    */
   TextOverflow(nsDisplayListBuilder* aBuilder,
                nsIFrame* aBlockFrame);
 
+  TextOverflow() = default;
+  ~TextOverflow() = default;
+  TextOverflow(TextOverflow&&) = default;
+  TextOverflow(const TextOverflow&) = delete;
+  TextOverflow& operator=(TextOverflow&&) = default;
+  TextOverflow& operator=(const TextOverflow&) = delete;
+
   /**
    * Analyze the display lists for text overflow and what kind of item is at
    * the content edges.  Add display items for text-overflow markers as needed
    * and remove or clip items that would overlap a marker.
    */
   void ProcessLine(const nsDisplayListSet& aLists, nsLineBox* aLine, uint32_t aLineNumber);
 
   /**
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -6807,63 +6807,62 @@ nsBlockFrame::BuildDisplayList(nsDisplay
       if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)
          BuildDisplayListForChild(aBuilder, f, aLists);
     }
   }
 
   aBuilder->MarkFramesForDisplayList(this, mFloats);
 
   // Prepare for text-overflow processing.
-  UniquePtr<TextOverflow> textOverflow =
-    TextOverflow::WillProcessLines(aBuilder, this);
+  Maybe<TextOverflow> textOverflow = TextOverflow::WillProcessLines(aBuilder, this);
 
   // We'll collect our lines' display items here, & then append this to aLists.
   nsDisplayListCollection linesDisplayListCollection(aBuilder);
 
   // Don't use the line cursor if we might have a descendant placeholder ...
   // it might skip lines that contain placeholders but don't themselves
   // intersect with the dirty area.
   // In particular, we really want to check ShouldDescendIntoFrame()
   // on all our child frames, but that might be expensive.  So we
   // approximate it by checking it on |this|; if it's true for any
   // frame in our child list, it's also true for |this|.
   // Also skip the cursor if we're creating text overflow markers,
   // since we need to know what line number we're up to in order
   // to generate unique display item keys.
-  nsLineBox* cursor = (aBuilder->ShouldDescendIntoFrame(this, true) || textOverflow) ?
+  nsLineBox* cursor = (aBuilder->ShouldDescendIntoFrame(this, true) || textOverflow.isSome()) ?
     nullptr : GetFirstLineContaining(aBuilder->GetDirtyRect().y);
   LineIterator line_end = LinesEnd();
 
   if (cursor) {
     for (LineIterator line = mLines.begin(cursor);
          line != line_end;
          ++line) {
       nsRect lineArea = line->GetVisualOverflowArea();
       if (!lineArea.IsEmpty()) {
         // Because we have a cursor, the combinedArea.ys are non-decreasing.
         // Once we've passed aDirtyRect.YMost(), we can never see it again.
         if (lineArea.y >= aBuilder->GetDirtyRect().YMost()) {
           break;
         }
-        MOZ_ASSERT(!textOverflow);
+        MOZ_ASSERT(textOverflow.isNothing());
         DisplayLine(aBuilder, lineArea, line, depth, drawnLines,
                     linesDisplayListCollection, this, nullptr, 0);
       }
     }
   } else {
     bool nonDecreasingYs = true;
     uint32_t lineCount = 0;
     nscoord lastY = INT32_MIN;
     nscoord lastYMost = INT32_MIN;
     for (LineIterator line = LinesBegin();
          line != line_end;
          ++line) {
       nsRect lineArea = line->GetVisualOverflowArea();
       DisplayLine(aBuilder, lineArea, line, depth, drawnLines,
-                  linesDisplayListCollection, this, textOverflow.get(), lineCount);
+                  linesDisplayListCollection, this, textOverflow.ptrOr(nullptr), lineCount);
       if (!lineArea.IsEmpty()) {
         if (lineArea.y < lastY
             || lineArea.YMost() < lastYMost) {
           nonDecreasingYs = false;
         }
         lastY = lineArea.y;
         lastYMost = lineArea.YMost();
       }
@@ -6874,17 +6873,17 @@ nsBlockFrame::BuildDisplayList(nsDisplay
       SetupLineCursor();
     }
   }
 
   // Pick up the resulting text-overflow markers.  We append them to
   // PositionedDescendants just before we append the lines' display items,
   // so that our text-overflow markers will appear on top of this block's
   // normal content but below any of its its' positioned children.
-  if (textOverflow) {
+  if (textOverflow.isSome()) {
     aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers());
   }
   linesDisplayListCollection.MoveTo(aLists);
 
   if (HasOutsideBullet()) {
     // Display outside bullets manually
     nsIFrame* bullet = GetOutsideBullet();
     BuildDisplayListForChild(aBuilder, bullet, aLists);
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -154,18 +154,17 @@ public:
 
 #ifdef DEBUG
   void SetInitialChildList(ChildListID  aListID,
                            nsFrameList& aChildList) override;
 #endif
 
   /**
    * Return the containing block for aChild which MUST be an abs.pos. child
-   * of a grid container.  This is just a helper method for
-   * nsAbsoluteContainingBlock::Reflow - it's not meant to be used elsewhere.
+   * of a grid container and that container must have been reflowed.
    */
   static const nsRect& GridItemCB(nsIFrame* aChild);
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridItemContainingBlockRect, nsRect)
 
   /**
    * These properties are created by a call to
    * nsGridContainerFrame::GetGridFrameWithComputedInfo, typically from
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -3249,16 +3249,50 @@ public:
 
   virtual ~nsDisplayList()
   {
     if (mSentinel.mAbove) {
       NS_WARNING("Nonempty list left over?");
     }
   }
 
+  nsDisplayList(nsDisplayList&& aOther)
+  {
+    mIsOpaque = aOther.mIsOpaque;
+    mForceTransparentSurface = aOther.mForceTransparentSurface;
+
+    if (aOther.mSentinel.mAbove) {
+      AppendToTop(&aOther);
+    } else {
+      mTop = &mSentinel;
+      mLength = 0;
+    }
+  }
+
+  nsDisplayList& operator=(nsDisplayList&& aOther)
+  {
+    if (this != &aOther) {
+      if (aOther.mSentinel.mAbove) {
+        nsDisplayList tmp;
+        tmp.AppendToTop(&aOther);
+        aOther.AppendToTop(this);
+        AppendToTop(&tmp);
+      } else {
+        mTop = &mSentinel;
+        mLength = 0;
+      }
+      mIsOpaque = aOther.mIsOpaque;
+      mForceTransparentSurface = aOther.mForceTransparentSurface;
+    }
+    return *this;
+  }
+
+  nsDisplayList(const nsDisplayList&) = delete;
+  nsDisplayList& operator=(const nsDisplayList& aOther) = delete;
+
   /**
    * Append an item to the top of the list. The item must not currently
    * be in a list and cannot be null.
    */
   void AppendToTop(nsDisplayItem* aItem)
   {
     MOZ_ASSERT(aItem, "No item to append!");
     MOZ_ASSERT(!aItem->mAbove, "Already in a list!");
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -862,17 +862,17 @@ fuzzy-if(skiaContent,0-2,0-5) == 402629-
 != 402940-2.html 402940-2-notref.html
 != 402940-3.html 402940-3-notref.html
 == 402950-1.html 402950-1-ref.html
 == 403129-1.html 403129-1-ref.html
 == 403129-2.html 403129-2-ref.html
 == 403129-3.html 403129-3-ref.html
 == 403129-4.html 403129-4-ref.html
 random == 403134-1.html 403134-1-ref.html # bug 405377
-fuzzy-if(webrender,130-131,177-177) == 403181-1.xml 403181-1-ref.xml
+fuzzy-if(webrender,130-131,177-177) skip-if(winWidget&&!isDebugBuild&&webrender) == 403181-1.xml 403181-1-ref.xml
 == 403249-1a.html 403249-1-ref.html
 == 403249-1b.html 403249-1-ref.html
 == 403249-2a.html 403249-2-ref.html
 == 403249-2b.html 403249-2-ref.html
 == 403328-1.html 403328-1-ref.html
 == 403426-1.html 403426-1-ref.html
 == 403455-1.html 403455-1-ref.html
 == 403505-1.xml 403505-1-ref.xul
--- a/layout/reftests/forms/progress/bar-pseudo-element-ref.html
+++ b/layout/reftests/forms/progress/bar-pseudo-element-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) > .progress-bar { margin: 10px; padding: 0px; }
     body > div:nth-child(2) > .progress-bar { margin: 0px; padding: 10px; }
     body > div:nth-child(3) > .progress-bar { margin: 10px; padding: 10px; }
     body > div:nth-child(4) > .progress-bar { margin: 5px; padding: 5px; }
     body > div:nth-child(5) > .progress-bar { margin: 50px; padding: 50px; }
     body > div:nth-child(6) > .progress-bar { margin: 100px; padding: 100px; }
     body > div:nth-child(7) > .progress-bar { margin: 10px 0px 0px 0px; padding: 0px; }
--- a/layout/reftests/forms/progress/bar-pseudo-element-rtl-ref.html
+++ b/layout/reftests/forms/progress/bar-pseudo-element-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) > .progress-bar { margin: 0px 10px 0px 0px; padding: 0px; }
     body > div:nth-child(2) > .progress-bar { margin: 0px 0px 0px 10px; padding: 0px; }
     body > div:nth-child(3) > .progress-bar { margin: 0px; padding: 0px 10px 0px 0px; }
     body > div:nth-child(4) > .progress-bar { margin: 0px; padding: 0px 0px 0px 10px; }
     /* 15 - 18 should have 100% width, no need to specify. */
   </style>
   <body dir='rtl'>
--- a/layout/reftests/forms/progress/bar-pseudo-element-vertical-ref.html
+++ b/layout/reftests/forms/progress/bar-pseudo-element-vertical-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     .progress-element { margin: 10px; }
     body > div:nth-child(1) > .progress-bar { position: relative; top: 4px; left: 4px;
                                               height: calc(100% - 8px); }
     body > div:nth-child(2) > .progress-bar { }
     body > div:nth-child(3) > .progress-bar { position: relative; top: 4px; left: 4px;
                                               height: calc(100% - 8px); }
     body > div:nth-child(4) > .progress-bar { position: relative; top: 10px; height: calc(100% - 10px); }
--- a/layout/reftests/forms/progress/bar-pseudo-element-vertical-rtl-ref.html
+++ b/layout/reftests/forms/progress/bar-pseudo-element-vertical-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     .progress-element { margin: 10px; }
     body > div:nth-child(1) > .progress-bar { position: relative; top: 4px; left: 4px;
                                               height: calc(100% - 8px); }
     body > div:nth-child(2) > .progress-bar { }
     body > div:nth-child(3) > .progress-bar { position: relative; top: 4px; left: 4px;
                                               height: calc(100% - 8px); }
     body > div:nth-child(4) > .progress-bar { position: relative; top: 10px; height: calc(100% - 10px); }
--- a/layout/reftests/forms/progress/block-invalidate-ref.html
+++ b/layout/reftests/forms/progress/block-invalidate-ref.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     progress { display: block; }
   </style>
   <body>
     <progress value='0.5'></progress>
   </body>
 </html>
--- a/layout/reftests/forms/progress/in-cells-ref.html
+++ b/layout/reftests/forms/progress/in-cells-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     progress { width: 10em; height: 1em; }
     progress.vertical { -moz-orient: vertical; width: 1em; height: 10em; }
   </style>
   <body>
     <table>
       <tr>
         <td>foo</td>
--- a/layout/reftests/forms/progress/indeterminate-style-height-ref.html
+++ b/layout/reftests/forms/progress/indeterminate-style-height-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div.progress-element {
       height: 12em;
       width: 2em;
     }
     div.progress-bar {
       height: 100%;
     }
--- a/layout/reftests/forms/progress/indeterminate-style-width-ref.html
+++ b/layout/reftests/forms/progress/indeterminate-style-width-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div.progress-bar {
       width:100%;
     }
     body > div:nth-child(1) > .progress-bar { width: 20px; }
     body > div:nth-child(2) > .progress-bar { width: 0px; }
     body > div:nth-child(3) > .progress-bar { width: 50%; }
     body > div:nth-child(4) > .progress-bar { width: 1em; }
--- a/layout/reftests/forms/progress/margin-padding-ref.html
+++ b/layout/reftests/forms/progress/margin-padding-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) { margin: 10px; padding: 0px; }
     body > div:nth-child(2) { margin: 0px; padding: 10px; }
     body > div:nth-child(3) { margin: 10px; padding: 10px; }
     body > div:nth-child(4) { margin: 5px; padding: 5px; }
     body > div:nth-child(5) { margin: 50px; padding: 50px; }
     body > div:nth-child(6) { margin: 100px; padding: 100px; }
     body > div:nth-child(7) { margin: 10px 0px 0px 0px; padding: 0px; }
--- a/layout/reftests/forms/progress/margin-padding-rtl-ref.html
+++ b/layout/reftests/forms/progress/margin-padding-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) { margin: 10px; padding: 0px; }
     body > div:nth-child(2) { margin: 0px; padding: 10px; }
     body > div:nth-child(3) { margin: 10px; padding: 10px; }
     body > div:nth-child(4) { margin: 5px; padding: 5px; }
     body > div:nth-child(5) { margin: 50px; padding: 50px; }
     body > div:nth-child(6) { margin: 100px; padding: 100px; }
     body > div:nth-child(7) { margin: 10px 0px 0px 0px; padding: 0px; }
--- a/layout/reftests/forms/progress/margin-padding-vertical-ref.html
+++ b/layout/reftests/forms/progress/margin-padding-vertical-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) { margin: 10px; padding: 0px; }
     body > div:nth-child(2) { margin: 0px; padding: 10px; }
     body > div:nth-child(3) { margin: 10px; padding: 10px; }
     body > div:nth-child(4) { margin: 5px; padding: 5px; }
     body > div:nth-child(5) { margin: 50px; padding: 50px; }
     body > div:nth-child(6) { margin: 100px; padding: 100px; }
     body > div:nth-child(7) { margin: 10px 0px 0px 0px; padding: 0px; }
--- a/layout/reftests/forms/progress/margin-padding-vertical-rtl-ref.html
+++ b/layout/reftests/forms/progress/margin-padding-vertical-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) { margin: 10px; padding: 0px; }
     body > div:nth-child(2) { margin: 0px; padding: 10px; }
     body > div:nth-child(3) { margin: 10px; padding: 10px; }
     body > div:nth-child(4) { margin: 5px; padding: 5px; }
     body > div:nth-child(5) { margin: 50px; padding: 50px; }
     body > div:nth-child(6) { margin: 100px; padding: 100px; }
     body > div:nth-child(7) { margin: 10px 0px 0px 0px; padding: 0px; }
--- a/layout/reftests/forms/progress/transformations-ref.html
+++ b/layout/reftests/forms/progress/transformations-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     body > div:nth-child(1) { -moz-transform: matrix(1, -0.2, 0, 1, 0, 0); }
     body > div:nth-child(2) { -moz-transform: translateX(15em) matrix(1, 0, 0.6, 1, 0, 0); }
     body > div:nth-child(3) { -moz-transform: rotate(30deg); }
     body > div:nth-child(4) { -moz-transform: scale(2, 4); }
     body > div:nth-child(5) { -moz-transform: scale(0.1, 0.4); }
     body > div:nth-child(6) { -moz-transform: scale(1, 0.4); }
     body > div:nth-child(7) { -moz-transform: scale(0.1, 1); }
--- a/layout/reftests/forms/progress/values-ref.html
+++ b/layout/reftests/forms/progress/values-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div:nth-child(1) > .progress-bar { width: 100%; }
     div:nth-child(2) > .progress-bar { width: 0%; }
     div:nth-child(3) > .progress-bar { width: 10%; }
     div:nth-child(4) > .progress-bar { width: 50%; }
     div:nth-child(5) > .progress-bar { width: 0%; }
     div:nth-child(6) > .progress-bar { width: 100%; }
     div:nth-child(7) > .progress-bar { width: 42%; }
--- a/layout/reftests/forms/progress/values-rtl-ref.html
+++ b/layout/reftests/forms/progress/values-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div:nth-child(1) > .progress-bar { width: 100%; }
     div:nth-child(2) > .progress-bar { width: 0%; }
     div:nth-child(3) > .progress-bar { width: 10%; }
     div:nth-child(4) > .progress-bar { width: 50%; }
     div:nth-child(5) > .progress-bar { width: 0%; }
     div:nth-child(6) > .progress-bar { width: 100%; }
     div:nth-child(7) > .progress-bar { width: 42%; }
--- a/layout/reftests/forms/progress/values-vertical-ref.html
+++ b/layout/reftests/forms/progress/values-vertical-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div:nth-child(1) > .progress-bar { position: relative; top: 0%; height: 100%; }
     div:nth-child(2) > .progress-bar { position: relative; top: 100%; height: 0%; }
     div:nth-child(3) > .progress-bar { position: relative; top: 90%; height: 10%; }
     div:nth-child(4) > .progress-bar { position: relative; top: 50%; height: 50%; }
     div:nth-child(5) > .progress-bar { position: relative; top: 100%; height: 0%; }
     div:nth-child(6) > .progress-bar { position: relative; top: 0%; height: 100%; }
     div:nth-child(7) > .progress-bar { position: relative; top: 58%; height: 42%; }
--- a/layout/reftests/forms/progress/values-vertical-rtl-ref.html
+++ b/layout/reftests/forms/progress/values-vertical-rtl-ref.html
@@ -1,11 +1,11 @@
 <!DOCTYPE html>
 <html>
-  <link rel='stylesheet' href='chrome://reftest/content/progress.css'>
+  <link rel='stylesheet' href='resource://reftest/progress.css'>
   <style>
     div:nth-child(1) > .progress-bar { position: relative; top: 0%; height: 100%; }
     div:nth-child(2) > .progress-bar { position: relative; top: 100%; height: 0%; }
     div:nth-child(3) > .progress-bar { position: relative; top: 90%; height: 10%; }
     div:nth-child(4) > .progress-bar { position: relative; top: 50%; height: 50%; }
     div:nth-child(5) > .progress-bar { position: relative; top: 100%; height: 0%; }
     div:nth-child(6) > .progress-bar { position: relative; top: 0%; height: 100%; }
     div:nth-child(7) > .progress-bar { position: relative; top: 58%; height: 42%; }
--- a/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html
+++ b/layout/reftests/w3c-css/submitted/contain/contain-layout-formatting-context-margin-001.html
@@ -1,17 +1,17 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>CSS Test: 'contain: layout' with a vertical margin child. Margin collapse should not occur, and neither should overflow clipping.</title>
   <link rel="author" title="Kyle Zentner" href="mailto:zentner.kyle@gmail.com">
   <link rel="author" title="Morgan Rae Reschenberg" href="mailto:mreschenberg@berkeley.edu">
   <link rel="help" href="http://www.w3.org/TR/css-containment-1/#containment-layout">
-  <link rel="match" href="contain-paint-formatting-context-margin-001-ref.html">
+  <link rel="match" href="contain-layout-formatting-context-margin-001-ref.html">
   <style>
   #a {
       contain:layout;
       background: blue;
       margin: 10px;
       width: 50px;
       height: 50px;
   }
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -4157,16 +4157,20 @@ nsComputedDOMStyle::GetAbsoluteOffset(mo
     // the containing block is the viewport, which _does_ include
     // scrollbars.  We have to do some extra work.
     // the first child in the default frame list is what we want
     nsIFrame* scrollingChild = container->PrincipalChildList().FirstChild();
     nsIScrollableFrame *scrollFrame = do_QueryFrame(scrollingChild);
     if (scrollFrame) {
       scrollbarSizes = scrollFrame->GetActualScrollbarSizes();
     }
+  } else if (container->IsGridContainerFrame() &&
+             (mOuterFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))) {
+    containerRect = nsGridContainerFrame::GridItemCB(mOuterFrame);
+    rect.MoveBy(-containerRect.x, -containerRect.y);
   }
 
   nscoord offset = 0;
   switch (aSide) {
     case eSideTop:
       offset = rect.y - margin.top - border.top - scrollbarSizes.top;
 
       break;
rename from layout/tools/reftest/bootstrap.js
rename to layout/tools/reftest/api.js
--- a/layout/tools/reftest/bootstrap.js
+++ b/layout/tools/reftest/api.js
@@ -1,86 +1,114 @@
 /* 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/. */
 
 const Cm = Components.manager;
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+                                   "@mozilla.org/network/protocol;1?name=resource",
+                                   "nsISubstitutingProtocolHandler");
+
+XPCOMUtils.defineLazyServiceGetter(this, "aomStartup",
+                                   "@mozilla.org/addons/addon-manager-startup;1",
+                                   "amIAddonManagerStartup");
 
 function processTerminated() {
   return new Promise(resolve => {
     Services.obs.addObserver(function observe(subject, topic) {
       if (topic == "ipc:content-shutdown") {
         Services.obs.removeObserver(observe, topic);
         resolve();
       }
     }, "ipc:content-shutdown");
   });
 }
 
+function startAndroid(win) {
+  // Add setTimeout here because windows.innerWidth/Height are not set yet.
+  win.setTimeout(function() {OnRefTestLoad(win);}, 0);
+}
+
 var WindowListener = {
   onOpenWindow: function(xulWin) {
     Services.wm.removeListener(WindowListener);
 
     let win = xulWin.docShell.domWindow;
     win.addEventListener("load", function listener() {
       // Load into any existing windows.
       for (win of Services.wm.getEnumerator("navigator:browser")) {
         break;
       }
 
-      ChromeUtils.import("chrome://reftest/content/reftest.jsm");
       win.addEventListener("pageshow", function() {
-        // Add setTimeout here because windows.innerWidth/Height are not set yet.
-        win.setTimeout(function() {OnRefTestLoad(win);}, 0);
+        startAndroid(win);
       }, {once: true});
     }, {once: true});
   }
 };
 
-function startup(data, reason) {
-  if (Services.appinfo.OS == "Android") {
-    Cm.addBootstrappedManifestLocation(data.installPath);
-    Services.wm.addListener(WindowListener);
-    return;
+this.reftest = class extends ExtensionAPI {
+  onStartup() {
+    let uri = Services.io.newURI("chrome/reftest/res/", null, this.extension.rootURI);
+    resProto.setSubstitutionWithFlags("reftest", uri, resProto.ALLOW_CONTENT_ACCESS);
+
+    const manifestURI = Services.io.newURI("manifest.json", null, this.extension.rootURI);
+    this.chromeHandle = aomStartup.registerChrome(manifestURI, [
+      ["content", "reftest", "chrome/reftest/content/", "contentaccessible=yes"],
+    ]);
+
+    // Starting tests is handled quite differently on android and desktop.
+    // On Android, OnRefTestLoad() takes over the main browser window so
+    // we just need to call it as soon as the browser window is available.
+    // On desktop, a separate window (dummy) is created and explicitly given
+    // focus (see bug 859339 for details), then tests are launched in a new
+    // top-level window.
+    let win = Services.wm.getMostRecentWindow("navigator:browser");
+
+    if (Services.appinfo.OS == "Android") {
+      ChromeUtils.import("resource://reftest/reftest.jsm");
+      if (win) {
+        startAndroid(win);
+      } else {
+        Services.wm.addListener(WindowListener);
+      }
+      return;
+    }
+
+    Services.io.manageOfflineStatus = false;
+    Services.io.offline = false;
+
+    let dummy = Services.ww.openWindow(null, "about:blank", "dummy",
+                                       "chrome,dialog=no,left=800,height=200,width=200,all",null);
+    dummy.onload = async function() {
+      // Close pre-existing window
+      win.close();
+
+      ChromeUtils.import("resource://reftest/PerTestCoverageUtils.jsm");
+      if (PerTestCoverageUtils.enabled) {
+        // In PerTestCoverage mode, wait for the process belonging to the window we just closed
+        // to be terminated, to avoid its shutdown interfering when we reset the counters.
+        await processTerminated();
+      }
+
+      dummy.focus();
+      Services.ww.openWindow(null, "chrome://reftest/content/reftest.xul",
+                             "_blank", "chrome,dialog=no,all", {});
+    };
   }
 
-  let orig = Services.wm.getMostRecentWindow("navigator:browser");
-
-  let ios = Cc["@mozilla.org/network/io-service;1"]
-              .getService(Ci.nsIIOService);
-  ios.manageOfflineStatus = false;
-  ios.offline = false;
+  onShutdown() {
+    resProto.setSubstitution("reftest", null);
 
-  let wwatch = Cc["@mozilla.org/embedcomp/window-watcher;1"]
-                .getService(Ci.nsIWindowWatcher);
-  let dummy = wwatch.openWindow(null, "about:blank", "dummy",
-                                "chrome,dialog=no,left=800,height=200,width=200,all",null);
-  dummy.onload = async function() {
-    // Close pre-existing window
-    orig.close();
+    this.chromeHandle.destruct();
+    this.chromeHandle = null;
 
-    ChromeUtils.import("chrome://reftest/content/PerTestCoverageUtils.jsm");
-    if (PerTestCoverageUtils.enabled) {
-      // In PerTestCoverage mode, wait for the process belonging to the window we just closed
-      // to be terminated, to avoid its shutdown interfering when we reset the counters.
-      await processTerminated();
+    if (Services.appinfo.OS == "Android") {
+      Services.wm.removeListener(WindowListener);
+      OnRefTestUnload();
+      Cu.unload("resource://reftest/reftest.jsm");
     }
-
-    dummy.focus();
-    wwatch.openWindow(null, "chrome://reftest/content/reftest.xul", "_blank",
-                      "chrome,dialog=no,all", {});
-  };
-}
-
-function shutdown(data, reason) {
-  if (Services.appinfo.OS == "Android") {
-    Services.wm.removeListener(WindowListener);
-    Cm.removedBootstrappedManifestLocation(data.installPath);
-    OnRefTestUnload();
-    Cu.unload("chrome://reftest/content/reftest.jsm");
   }
-}
-
-
-function install(data, reason) {}
-function uninstall(data, reason) {}
+};
--- a/layout/tools/reftest/jar.mn
+++ b/layout/tools/reftest/jar.mn
@@ -1,14 +1,14 @@
 reftest.jar:
-% content reftest %content/ contentaccessible=yes
-  content/globals.jsm (globals.jsm)
-  content/reftest-content.js (reftest-content.js)
-  content/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
-  content/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
-  content/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
-  content/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm)
-  content/input.css (../../../editor/reftests/xul/input.css)
   content/moz-bool-pref.css (../../../layout/reftests/css-parsing/moz-bool-pref.css)
-  content/progress.css (../../../layout/reftests/forms/progress/style.css)
-*  content/manifest.jsm (manifest.jsm)
-*  content/reftest.jsm (reftest.jsm)
   content/reftest.xul (reftest.xul)
+
+  res/globals.jsm (globals.jsm)
+  res/reftest-content.js (reftest-content.js)
+  res/AsyncSpellCheckTestHelper.jsm (../../../editor/AsyncSpellCheckTestHelper.jsm)
+  res/httpd.jsm (../../../netwerk/test/httpserver/httpd.js)
+  res/StructuredLog.jsm (../../../testing/modules/StructuredLog.jsm)
+  res/PerTestCoverageUtils.jsm (../../../tools/code-coverage/PerTestCoverageUtils.jsm)
+  res/input.css (../../../editor/reftests/xul/input.css)
+  res/progress.css (../../../layout/reftests/forms/progress/style.css)
+*  res/manifest.jsm (manifest.jsm)
+*  res/reftest.jsm (reftest.jsm)
--- a/layout/tools/reftest/manifest.jsm
+++ b/layout/tools/reftest/manifest.jsm
@@ -2,18 +2,18 @@
 /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
 /* 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/. */
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ReadTopManifest", "CreateUrls"];
 
-Cu.import("chrome://reftest/content/globals.jsm", this);
-Cu.import("chrome://reftest/content/reftest.jsm", this);
+Cu.import("resource://reftest/globals.jsm", this);
+Cu.import("resource://reftest/reftest.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 
 const NS_SCRIPTSECURITYMANAGER_CONTRACTID = "@mozilla.org/scriptsecuritymanager;1";
 const NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX = "@mozilla.org/network/protocol;1?name=";
 const NS_XREAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
 
 const RE_PROTOCOL = /^\w+:/;
rename from layout/tools/reftest/install.rdf
rename to layout/tools/reftest/manifest.json
--- a/layout/tools/reftest/install.rdf
+++ b/layout/tools/reftest/manifest.json
@@ -1,22 +1,22 @@
-<?xml version="1.0"?>
+{
+  "manifest_version": 2,
+  "name": "Reftest",
+  "version": "1.0",
+
+  "applications": {
+    "gecko": {
+      "id": "reftest@mozilla.org"
+    }
+  },
 
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-  <Description about="urn:mozilla:install-manifest">
-    <em:id>reftest@mozilla.org</em:id>
-    <em:version>1.0</em:version>
-    <em:type>2</em:type>
-    <em:bootstrap>true</em:bootstrap>
-    <em:targetApplication>
-      <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-#expand        <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
-#expand        <em:maxVersion>__MOZILLA_VERSION_U__</em:maxVersion>
-      </Description>
-    </em:targetApplication>
-    <!-- Front End MetaData -->
-    <em:name>Reftest</em:name>
-    <em:description>Run layout comparison tests.</em:description>
-    <em:creator>L. David Baron</em:creator>
-  </Description>
-</RDF>
+  "experiment_apis": {
+    "reftest": {
+      "schema": "schema.json",
+      "parent": {
+        "scopes": ["addon_parent"],
+        "script": "api.js",
+        "events": ["startup"]
+      }
+    }
+  }
+}
--- a/layout/tools/reftest/moz.build
+++ b/layout/tools/reftest/moz.build
@@ -6,18 +6,21 @@
 
 with Files('**'):
     BUG_COMPONENT = ('Testing', 'Reftest')
     SCHEDULES.exclusive = ['reftest']
 
 XPI_NAME = 'reftest'
 USE_EXTENSION_MANIFEST = True
 JAR_MANIFESTS += ['jar.mn']
-FINAL_TARGET_PP_FILES += ['install.rdf']
-FINAL_TARGET_FILES += ['bootstrap.js']
+FINAL_TARGET_FILES += [
+    'api.js',
+    'manifest.json',
+    'schema.json',
+]
 
 TEST_HARNESS_FILES.reftest += [
     '/build/mobile/remoteautomation.py',
     '/build/pgo/server-locations.txt',
     '/testing/mochitest/server.js',
     'mach_test_package_commands.py',
     'output.py',
     'reftestcommandline.py',
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -12,17 +12,17 @@ const ENVIRONMENT_CONTRACTID = "@mozilla
 const NS_OBSERVER_SERVICE_CONTRACTID = "@mozilla.org/observer-service;1";
 const NS_GFXINFO_CONTRACTID = "@mozilla.org/gfx/info;1";
 const IO_SERVICE_CONTRACTID = "@mozilla.org/network/io-service;1"
 
 // "<!--CLEAR-->"
 const BLANK_URL_FOR_CLEARING = "data:text/html;charset=UTF-8,%3C%21%2D%2DCLEAR%2D%2D%3E";
 
 Cu.import("resource://gre/modules/Timer.jsm");
-Cu.import("chrome://reftest/content/AsyncSpellCheckTestHelper.jsm");
+Cu.import("resource://reftest/AsyncSpellCheckTestHelper.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 var gBrowserIsRemote;
 var gIsWebRenderEnabled;
 var gHaveCanvasSnapshot = false;
 // Plugin layers can be updated asynchronously, so to make sure that all
 // layer surfaces have the right content, we need to listen for explicit
 // "MozPaintWait" and "MozPaintWaitFinished" events that signal when it's OK
--- a/layout/tools/reftest/reftest.jsm
+++ b/layout/tools/reftest/reftest.jsm
@@ -7,21 +7,21 @@
 
 var EXPORTED_SYMBOLS = [
     "OnRefTestLoad",
     "OnRefTestUnload",
     "getTestPlugin"
 ];
 
 Cu.import("resource://gre/modules/FileUtils.jsm");
-Cu.import("chrome://reftest/content/globals.jsm", this);
-Cu.import("chrome://reftest/content/httpd.jsm", this);
-Cu.import("chrome://reftest/content/manifest.jsm", this);
-Cu.import("chrome://reftest/content/StructuredLog.jsm", this);
-Cu.import("chrome://reftest/content/PerTestCoverageUtils.jsm", this);
+Cu.import("resource://reftest/globals.jsm", this);
+Cu.import("resource://reftest/httpd.jsm", this);
+Cu.import("resource://reftest/manifest.jsm", this);
+Cu.import("resource://reftest/StructuredLog.jsm", this);
+Cu.import("resource://reftest/PerTestCoverageUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 XPCOMUtils.defineLazyGetter(this, "OS", function() {
     const { OS } = Cu.import("resource://gre/modules/osfile.jsm");
     return OS;
 });
@@ -1450,17 +1450,17 @@ function RegisterMessageListenersAndLoad
         "reftest:UpdateWholeCanvasForInvalidation",
         function (m) { RecvUpdateWholeCanvasForInvalidation(); }
     );
     g.browserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
 
-    g.browserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true, true);
+    g.browserMessageManager.loadFrameScript("resource://reftest/reftest-content.js", true, true);
 }
 
 function RecvAssertionCount(count)
 {
     DoAssertionCheck(count);
 }
 
 function RecvContentReady(info)
--- a/layout/tools/reftest/reftest.xul
+++ b/layout/tools/reftest/reftest.xul
@@ -4,11 +4,11 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         id="reftest-window"
         hidechrome="true"
         onload="OnRefTestLoad();"
         onunload="OnRefTestUnload();"
         style="background:white; overflow:hidden"
         >
-    <script type="application/ecmascript" src="reftest.jsm" />
+    <script type="application/ecmascript" src="resource://reftest/reftest.jsm" />
     <!-- The reftest browser element is dynamically created, here -->
 </window>
new file mode 100644
--- /dev/null
+++ b/layout/tools/reftest/schema.json
@@ -0,0 +1,1 @@
+[]
--- a/modules/libpref/parser/src/lib.rs
+++ b/modules/libpref/parser/src/lib.rs
@@ -49,26 +49,20 @@
 //!
 //! The parser performs error recovery. On a syntax error, it will scan forward
 //! to the next ';' token and then continue parsing. If the syntax error occurs
 //! in the middle of a token, it will first finish obtaining the current token
 //! in an appropriate fashion.
 
 // This parser uses several important optimizations.
 //
-// - Because "'\0' means EOF" is part of the grammar (see above) we can match
-//   EOF as a normal char/token, which means we can avoid a typical "do we
-//   still have chars remaining?" test in get_char(), which gives a speedup
-//   because get_char() is a very hot function. (Actually, Rust would
-//   bounds-check this function anyway, so we have get_char_unchecked() which
-//   is used for the two hottest call sites.)
-//
-//   This also means EOF is representable by a u8. If EOF was represented by an
-//   out-of-band value such as -1 or 256, we'd have to return a larger type
-//   such as u16 or i16 from get_char().
+// - Because "'\0' means EOF" is part of the grammar (see above), EOF is
+//   representable by a u8. If EOF was represented by an out-of-band value such
+//   as -1 or 256, we'd have to return a larger type such as u16 or i16 from
+//   get_char().
 //
 // - When starting a new token, it uses a lookup table with the first char,
 //   which quickly identifies what kind of token it will be. Furthermore, if
 //   that token is an unambiguous single-char token (e.g. '(', ')', '+', ',',
 //   '-', ';'), the parser will return the appropriate token kind value at
 //   minimal cost because the single-char tokens have a uniform representation.
 //
 // - It has a lookup table that identifies chars in string literals that need
@@ -187,17 +181,17 @@ enum Token {
     ErrorAtLine(&'static str, u32),
 }
 
 // We categorize every char by what action should be taken when it appears at
 // the start of a new token.
 #[derive(Clone, Copy, PartialEq)]
 enum CharKind {
     // These are ordered by frequency. See the comment in GetToken().
-    SingleChar, // Unambiguous single-char tokens: [()+,-]
+    SingleChar, // Unambiguous single-char tokens: [()+,-] or EOF
     SpaceNL,    // [\t\v\f \n]
     Keyword,    // [A-Za-z_]
     Quote,      // ["']
     Slash,      // /
     Digit,      // [0-9]
     Hash,       // #
     CR,         // \r
     Other       // Everything else; invalid except within strings and comments.
@@ -530,23 +524,31 @@ impl<'t> Parser<'t> {
                 _ => {}
             }
             token = self.get_token(&mut dummy_str);
         }
     }
 
     #[inline(always)]
     fn get_char(&mut self) -> u8 {
-        let c = self.buf[self.i];
-        self.i += 1;
-        c
+        // We do the bounds check ourselves so we can return EOF on failure.
+        // (Although the buffer is guaranteed to end in an EOF char, we might
+        // go one char past that, whereupon we must return EOF again.)
+        if self.i < self.buf.len() {
+            let c = unsafe { *self.buf.get_unchecked(self.i) };
+            self.i += 1;
+            c
+        } else {
+            debug_assert!(self.i == self.buf.len());
+            EOF
+        }
     }
 
-    // This function skips the bounds check in non-optimized builds. Using it
-    // at the hottest two call sites gives a ~15% parsing speed boost.
+    // This function skips the bounds check in optimized builds. Using it at
+    // the hottest two call sites gives a ~15% parsing speed boost.
     #[inline(always)]
     unsafe fn get_char_unchecked(&mut self) -> u8 {
         debug_assert!(self.i < self.buf.len());
         let c = *self.buf.get_unchecked(self.i);
         self.i += 1;
         c
     }
 
@@ -563,19 +565,21 @@ impl<'t> Parser<'t> {
             return true;
         }
         false
     }
 
     #[inline(always)]
     fn match_single_line_comment(&mut self) {
         loop {
-            // To reach here, the previous char must have been '/', and
-            // assertions elsewhere ensure that there must be at least one
-            // subsequent char (the '\0' for EOF).
+            // To reach here, the previous char must have been '/' (if this is
+            // the first loop iteration) or non-special (if this is the second
+            // or subsequent iteration), and assertions elsewhere ensure that
+            // there must be at least one subsequent char after those chars
+            // (the '\0' for EOF).
             let c = unsafe { self.get_char_unchecked() };
 
             // All the special chars have value <= b'\r'.
             if c > b'\r' {
                 continue;
             }
             match c {
                 b'\n' => {
@@ -583,63 +587,57 @@ impl<'t> Parser<'t> {
                     break;
                 }
                 b'\r' => {
                     self.line_num += 1;
                     self.match_char(b'\n');
                     break;
                 }
                 EOF => {
-                    // Unget EOF so subsequent calls to get_char() are safe.
-                    self.unget_char();
                     break;
                 }
                 _ => continue
             }
         }
     }
 
     // Returns false if we hit EOF without closing the comment.
-    fn match_multi_line_comment(&mut self) -> bool
-    {
+    fn match_multi_line_comment(&mut self) -> bool {
         loop {
             match self.get_char() {
                 b'*' => {
                     if self.match_char(b'/') {
                         return true;
                     }
                 }
                 b'\n' => {
                     self.line_num += 1;
                 }
                 b'\r' => {
                     self.line_num += 1;
                     self.match_char(b'\n');
                 }
                 EOF => {
-                    // Unget EOF so subsequent calls to get_char() are safe.
-                    self.unget_char();
                     return false
                 }
                 _ => continue
             }
         }
     }
 
     fn match_hex_digits(&mut self, ndigits: i32) -> Option<u16> {
         debug_assert!(ndigits == 2 || ndigits == 4);
         let mut value: u16 = 0;
         for _ in 0..ndigits {
             value = value << 4;
             match self.get_char() {
-                c @ b'0'... b'9' => value += (c - b'0') as u16,
+                c @ b'0'...b'9' => value += (c - b'0') as u16,
                 c @ b'A'...b'F' => value += (c - b'A') as u16 + 10,
                 c @ b'a'...b'f' => value += (c - b'a') as u16 + 10,
                 _ => {
-                    // Unget in case the char was a closing quote or EOF.
                     self.unget_char();
                     return None;
                 }
             }
         }
         Some(value)
     }
 
@@ -711,17 +709,25 @@ impl<'t> Parser<'t> {
                         b'/' => {
                             self.match_single_line_comment();
                         }
                         b'*' => {
                             if !self.match_multi_line_comment() {
                                 return Token::Error("unterminated /* comment");
                             }
                         }
-                        _ => return Token::Error("expected '/' or '*' after '/'")
+                        c @ _ =>  {
+                            if c == b'\n' || c == b'\r' {
+                                // Unget the newline char; the outer loop will
+                                // reget it and adjust self.line_num
+                                // appropriately.
+                                self.unget_char();
+                            }
+                            return Token::Error("expected '/' or '*' after '/'");
+                        }
                     }
                     continue;
                 }
                 CharKind::Digit => {
                     let mut value = Some((c - b'0') as u32);
                     loop {
                         let c = self.get_char();
                         match Parser::char_kind(c) {
@@ -842,43 +848,51 @@ impl<'t> Parser<'t> {
                                 if self.match_char(b'\\') && self.match_char(b'u') {
                                     if let Some(lo) = self.match_hex_digits(4) {
                                         if 0xdc00 == (0xfc00 & lo) {
                                             // Found a valid low surrogate.
                                             utf16.push(lo);
                                         } else {
                                             self.string_error_token(
                                                 &mut token,
-                                                "invalid low surrogate value after high surrogate");
+                                                "invalid low surrogate after high surrogate");
                                             continue;
                                         }
                                     }
                                 }
                                 if utf16.len() != 2 {
                                     self.string_error_token(
                                         &mut token, "expected low surrogate after high surrogate");
                                     continue;
                                 }
+                            } else if 0xdc00 == (0xfc00 & value) {
+                                // Unaccompanied low surrogate value.
+                                self.string_error_token(
+                                    &mut token, "expected high surrogate before low surrogate");
+                                continue;
                             } else if value == 0 {
                                 self.string_error_token(&mut token, "\\u0000 is not allowed");
                                 continue;
                             }
 
                             // Insert the UTF-16 sequence as UTF-8.
                             let utf8 = String::from_utf16(&utf16).unwrap();
                             str_buf.extend(utf8.as_bytes());
                         } else {
                             self.string_error_token(&mut token, "malformed \\u escape sequence");
                             continue;
                         }
                         continue; // We don't want to str_buf.push(c2) below.
                     }
-                    _ => {
-                        // Unget in case the char is an EOF.
-                        self.unget_char();
+                    c @ _ => {
+                        if c == b'\n' || c == b'\r' {
+                            // Unget the newline char; the outer loop will
+                            // reget it and adjust self.line_num appropriately.
+                            self.unget_char();
+                        }
                         self.string_error_token(
                             &mut token, "unexpected escape sequence character after '\\'");
                         continue;
                     }
                 }
 
             } else if c == b'\n' {
                 self.line_num += 1;
@@ -889,18 +903,16 @@ impl<'t> Parser<'t> {
                 if self.match_char(b'\n') {
                     str_buf.push(b'\r');
                     b'\n'
                 } else {
                     c
                 }
 
             } else if c == EOF {
-                // Unget EOF so subsequent calls to get_char() are safe.
-                self.unget_char();
                 self.string_error_token(&mut token, "unterminated string literal");
                 break;
 
             } else {
                 // This case is only hit for the non-closing quote char.
                 debug_assert!((c == b'\'' || c == b'\"') && c != quote_char);
                 c
             };
--- a/modules/libpref/test/gtest/Parser.cpp
+++ b/modules/libpref/test/gtest/Parser.cpp
@@ -46,27 +46,30 @@ TEST(PrefsParser, Errors)
 pref("bool", true);
 sticky_pref("int", 123);
 user_pref("string", "value");
     )",
     ""
   );
 
   // Totally empty input.
-  DEFAULT("",
-    ""
-  );
+  DEFAULT("", "");
 
   // Whitespace-only input.
   DEFAULT(R"(
 
     )" "\v \t \v \f",
     ""
   );
 
+  // Comment-only inputs.
+  DEFAULT(R"(// blah)", "");
+  DEFAULT(R"(# blah)", "");
+  DEFAULT(R"(/* blah */)", "");
+
   //-------------------------------------------------------------------------
   // All the lexing errors. (To be pedantic, some of the integer literal
   // overflows are triggered in the parser, but put them all here so they're all
   // in the one spot.)
   //-------------------------------------------------------------------------
 
   // Integer overflow errors.
   DEFAULT(R"(
@@ -191,24 +194,34 @@ pref("int.ok", 0);
   // an invalid universal-character-name.)
   DEFAULT(R"(
 pref("string.bad-u-surrogate", "foo\)" R"(ud83c,blah");
 pref("int.ok", 0);
     )",
     "test:2: prefs parse error: expected low surrogate after high surrogate\n"
   );
 
-  // High surrogate followed by invalid low surrogate value.
+  // High surrogate followed by invalid low surrogate.
   // (The string literal is broken in two so that MSVC doesn't complain about
   // an invalid universal-character-name.)
   DEFAULT(R"(
 pref("string.bad-u-surrogate", "foo\)" R"(ud83c\u1234");
 pref("int.ok", 0);
     )",
-    "test:2: prefs parse error: invalid low surrogate value after high surrogate\n"
+    "test:2: prefs parse error: invalid low surrogate after high surrogate\n"
+  );
+
+  // Low surrogate not preceded by high surrogate.
+  // (The string literal is broken in two so that MSVC doesn't complain about
+  // an invalid universal-character-name.)
+  DEFAULT(R"(
+pref("string.bad-u-surrogate", "foo\)" R"(udc00");
+pref("int.ok", 0);
+    )",
+    "test:2: prefs parse error: expected high surrogate before low surrogate\n"
   );
 
   // Unlike in JavaScript, \b, \f, \t, \v aren't allowed.
   DEFAULT(R"(
 pref("string.bad-escape", "foo\b");
 pref("string.bad-escape", "foo\f");
 pref("string.bad-escape", "foo\t");
 pref("string.bad-escape", "foo\v");
@@ -288,21 +301,24 @@ pref("string.bad-keyword", TRUE);
 
   // Unterminated C-style comment.
   DEFAULT(R"(
 /* comment
     )",
     "test:3: prefs parse error: unterminated /* comment\n"
   );
 
-  // Malformed comment.
+  // Malformed comments (single slashes), followed by whitespace, newline, EOF.
   DEFAULT(R"(
-/ comment
-    )",
+/ comment;
+/
+; /)",
     "test:2: prefs parse error: expected '/' or '*' after '/'\n"
+    "test:3: prefs parse error: expected '/' or '*' after '/'\n"
+    "test:4: prefs parse error: expected '/' or '*' after '/'\n"
   );
 
   // C++-style comment ending in EOF (1).
   DEFAULT(R"(
 // comment)",
     ""
   );
 
--- a/netwerk/base/nsIBrowserSearchService.idl
+++ b/netwerk/base/nsIBrowserSearchService.idl
@@ -514,16 +514,26 @@ interface nsIBrowserSearchService : nsIS
    * The expected URI parameter for the search terms must exist in the query
    * string, but other parameters are ignored.
    *
    * @param url
    *        String containing the URL to parse, for example
    *        "https://www.google.com/search?q=terms".
    */
   nsISearchParseSubmissionResult parseSubmissionURL(in AString url);
+
+  /**
+   * Determines if a URL is related to search and if so, records the 
+   * appropriate telemtry.
+   *
+   * @param url
+   *        String containing the URL to parse, for example
+   *        "https://www.google.com/search?q=terms".
+   */
+  boolean recordSearchURLTelemetry(in AString url);
 };
 
 %{ C++
 /**
  * The observer topic to listen to for actions performed on installed
  * search engines.
  */
 #define SEARCH_ENGINE_TOPIC "browser-search-engine-modified"
--- a/old-configure.in
+++ b/old-configure.in
@@ -3800,22 +3800,16 @@ else
     if ! test -e "$_CL_STDIO_PATH"; then
         AC_MSG_ERROR([Unable to parse cl -showIncludes prefix. This compiler's locale has an unsupported formatting.])
     fi
     if test -z "$CL_INCLUDES_PREFIX"; then
         AC_MSG_ERROR([Cannot find cl -showIncludes prefix.])
     fi
     AC_SUBST(CL_INCLUDES_PREFIX)
     rm -f dummy-hello.c
-
-    dnl Make sure that the build system can handle non-ASCII characters
-    dnl in environment variables to prevent it from breaking silently on
-    dnl non-English systems.
-    NONASCII=$'\241\241'
-    AC_SUBST(NONASCII)
   fi
 fi
 fi # COMPILE_ENVIRONMENT
 
 dnl ========================================================
 dnl =
 dnl = Static Build Options
 dnl =
--- a/python/mozbuild/mozbuild/backend/configenvironment.py
+++ b/python/mozbuild/mozbuild/backend/configenvironment.py
@@ -236,19 +236,20 @@ class PartialConfigDict(object):
         try:
             with open(self._config_track) as fh:
                 existing_files.update(fh.read().splitlines())
         except IOError:
             pass
         return existing_files
 
     def _write_file(self, key, value):
+        encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
         filename = mozpath.join(self._datadir, key)
         with FileAvoidWrite(filename) as fh:
-            json.dump(value, fh, indent=4)
+            json.dump(value, fh, indent=4, encoding=encoding)
         return filename
 
     def _fill_group(self, values):
         # Clear out any cached values. This is mostly for tests that will check
         # the environment, write out a new set of variables, and then check the
         # environment again. Normally only configure ends up calling this
         # function, and other consumers create their own
         # PartialConfigEnvironments in new python processes.
--- a/security/manager/ssl/tests/unit/test_signed_apps.js
+++ b/security/manager/ssl/tests/unit/test_signed_apps.js
@@ -678,13 +678,19 @@ add_signature_test(PKCS7WithSHA1OrSHA256
   certdb.openSignedAppFileAsync(
     Ci.nsIX509CertDB.AppXPCShellRoot,
     original_app_path("cose_tampered_good_pkcs7"),
     check_open_result("tampered COSE with good PKCS7 signature should succeed" +
                       "when COSE is not processed",
                       Cr.NS_OK));
 });
 
+add_test(function () {
+  certdb.openSignedAppFileAsync(
+    Ci.nsIX509CertDB.AppXPCShellRoot, original_app_path("bug_1411458"),
+    check_open_result("bug 1411458", Cr.NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO));
+});
+
 // TODO: tampered MF, tampered SF
 // TODO: too-large MF, too-large RSA, too-large SF
 // TODO: MF and SF that end immediately after the last main header
 //       (no CR nor LF)
 // TODO: broken headers to exercise the parser
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0b296945ab6b030376713a84381b305fb4a5fcce
GIT binary patch
literal 2698
zc$^FHW@Zs#00D#OAaAJ{jZJ<)HVE?qac*K>W?E`-iC$K5eqJ?~f`SrEB)%-Qs5mn}
zPsvKbNCza5mzbLh<S2yzB^6vNN>cN{(qK7+S|tNL13g0}u3D}D6pO!k80u&P*&xgY
z#6hl(F21fI8JWcjKva^Es!)`gn39{Skd|Kr=9Yl0NrhRXhi2UDCQEiPCI$u&<^zhQ
zB$g!VXXYlRr|K2trFRDSx$|;Kae+MP>ERLtq^E%}2gqg{#==Jo42*X@T^vIy=Da<(
zk(0qd;K+vC^)Js0F*X-2jJy4A(*F+*b4JmSMLlu}1xi)>WQ6yC(u=37pUXO@geF)D
z;$XPa9O1o8IJnP<g@Iuc3n&H2FaQ&yqpqi)o4%u7P_ScY2qy!x{Mq-BAY59(&A`a=
zf|-E<Oc-Y*MV>yk^6ImT-{wxAIJeYM__R&4+O$ZYo*uS2Icsj!obd@-!{ab9tVKy}
z!6Kt+YSWY!6vzcER5afdl-J^PjMdktop;uZZ_*})>tYU^IdbBNO3>*K7neRRdEfui
zaL?w~^Zq7(TJyh}!INKkd*hOYI~p~li#527`R99QMI~kWbvC6J7G`EmY@Eche`4dp
zl?xY6Y-N;iX=K&m>EZEN$ix=ZCd8H`@b-_v+k;CzBUheObx7Bc{GjwEDJ3-_FX;(;
zRsY29m7NnOCWgB|;F@l?F}tt+^S06rGdXYE;<#V)^~ja%)3eu0KD5-j%HF?vT}6R&
z-TPyfIh#K(xn-AXA0;0;Ywpyb6M<VzvUX<wE}ywtt9Zt%b5{Q+p5BwNaDipz)<cQs
z)wh@bk~wwmVYT+HBmXz%<u1E&?6gnV-Kf6z)5L8aJb(Ll_OZ2l9aAq&x;0m}bE>de
zoBxKrDYah?^6~!^eKy^C&i&QhcRB9P6Wh9~FSBW1=vD{j*{39D&Uu-=T-7bV-o*EX
z!^($A^E%77&-wK)eeIf8spr01AN@Hk-R4fr+VxZB|E%!7e#PZxdDiui=j-O3c$i)m
z<<{z&zCU97zsvo9jgRt{=lpxV^3Q6G4;>FfpP6n~+2M2ln~|Zhk-?%PY{nW9kLGXi
z3)z=??v2!$#Wg;+tuNhmDy{m|cd7O1nM?D2Cs}=svWcnNQ#@DV<~OH_7k@R}*!bvq
zVX<{=mH*aLdb6yjr>^Wi*SF=~m(@A?c3Yz(CFkGXnvolH?6mrR>5{2QM&aiQGdFbq
z&0M20G2lz!?z391zt2Ufy*r-B<vrb{x{d23SJ7JC<UgU6zkbc!d(JQT`>OSyX09#&
z?YwyPi_7Kqv--K?|5!igc|LjdxhTu!Q<z1Te7YQ^mha(hRh{(d{jJ-L{B;(^b?g4^
zIFJ|h`H+p={mi3#iqzk)$=-Wmdt5;qgZt5)-&X5>)8*rNbiCf}Y~9N@UluKV;MceK
z%DblXY&^^T-~6;me^mH;`f(Yf`MIV!Njc2RCaMK2GdMEYu5d}7%d?0D$qEeoF@9ps
zX<}|{%$zM8n-z=RIeM?s@$KPxx&G$zh#6h$s?|bj&R2fgGGAQi%sq~4Mz3GTZ|SZv
z`Fri@p_7`aT~0Bte+J)QdV02*wAzL@H}>!LdDr~Gk?~yewBJkpcDbfUp0!@T@#>*3
z*JR?tJHsuXwF-J|Y@c6Kr<E0dFZtJBJ@)x8-_LE__v@eiulsrV998+RUzxqW<oM82
zd&|`SHuLPBU)bhsT)N}#*X`%Nhwa_><;Z0H{b%<+iYT*~c504A$j0EmH)D#eU3WJ3
zzn>re``+cP6<uGa_u77s-*w;aT#e1Y?=A1-Z8z<Yn{CZfoA_|re672c-!3nj?dueN
zFaNWThL7%+xmTY344q$nbDmY_|65C=KVSFVnN@6RyYb<?XLA3=MY-=w91;7>c(|LJ
z0k!T*UzhvD3&{3@*1<?6Pq15XW_n&?Noi54ZWyFawNfzDGvEsLa5U6)$xH`Uin_kg
z8rn+1%hk*?*u}{-IV;J@Fv=vdFgZOkEilT+CDg<gt-@qW-gfXTkPX5j$d>v#`gyv!
z28ZbRy1@)awVKNhSn*paptKZ_-D9N?l^EibRuC27p5tR&l9}mQ8kA*V;Fg_ao?mVY
z(+;WcQT4f%gnBq;Sw!R)W|%rhl_ZB-Sa_6$Xy+GZ`oi_0R_v(yjWeC{%u3xd%~RdI
z^@DP}{3;AW11dwZgU$0n-e+VIVaDC^fq@1_2K06ix>oeI3qorpyd{IK2fcxU&~pN#
zxdPHF$Be6Ll7Q>o(#XZkfKgGgLMke>N($XQ=!p*Do)&hrB#5pRJ?S8{{$$6VkOI6}
R!QsRPgf+nM{lNj^0RXE1VITki
deleted file mode 100644
--- a/testing/web-platform/meta/svg/linking/scripted/a-download-click.svg.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[a-download-click.svg]
-  [Clicking on an <a> element with a download attribute must not throw an exception]
-    expected: FAIL
-
--- a/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg
+++ b/testing/web-platform/tests/svg/linking/scripted/a-download-click.svg
@@ -17,17 +17,17 @@
             frame.contentWindow.addEventListener(
                 "beforeunload", t.unreached_func("Navigated instead of downloading"));
             const string = "test";
             const blob = new Blob([string], { type: "text/html" });
 
             const link = frame.contentDocument.querySelector("#blob-url");
             link.href.baseVal = URL.createObjectURL(blob);
 
-            link.click();
+            link.dispatchEvent(new Event('click'));
 
             t.step_timeout(() => t.done(), 1000);
         }));
         frame.src = "resources/a-download-click.svg";
         root.appendChild(frame);
     }, "Clicking on an <a> element with a download attribute must not throw an exception");
   ]]></script>
-</svg>
\ No newline at end of file
+</svg>
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { PerformanceStats } = ChromeUtils.import("resource://gre/modules/PerformanceStats.jsm", {});
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 const { ObjectUtils } = ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm", {});
+const { ExtensionParent } = ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm", {});
 
 const {WebExtensionPolicy} = Cu.getGlobalForObject(Services);
 
 // about:performance observes notifications on this topic.
 // if a notification is sent, this causes the page to be updated immediately,
 // regardless of whether the page is on pause.
 const TEST_DRIVER_TOPIC = "test-about:performance-test-driver";
 
@@ -67,16 +68,20 @@ const MODE_GLOBAL = "global";
 const MODE_RECENT = "recent";
 
 // Decide if we show the old style about:performance or if we can show data
 // based on the new performance counters.
 function performanceCountersEnabled() {
   return Services.prefs.getBoolPref("dom.performance.enable_scheduler_timing", false);
 }
 
+function extensionCountersEnabled() {
+  return Services.prefs.getBoolPref("extensions.webextensions.enablePerformanceCounters", false);
+}
+
 let tabFinder = {
   update() {
     this._map = new Map();
     for (let win of Services.wm.getEnumerator("navigator:browser")) {
       let tabbrowser = win.gBrowser;
       for (let browser of tabbrowser.browsers) {
         let id = browser.outerWindowID; // May be `null` if the browser isn't loaded yet
         if (id != null) {
@@ -395,43 +400,73 @@ var State = {
    */
   _firstSeen: new Map(),
 
   async _promiseSnapshot() {
     if (!performanceCountersEnabled()) {
       return this._monitor.promiseSnapshot();
     }
 
+    let addons = WebExtensionPolicy.getActiveExtensions();
+    let addonHosts = new Map();
+    for (let addon of addons)
+      addonHosts.set(addon.mozExtensionHostname, addon.id);
+
     let counters = await ChromeUtils.requestPerformanceMetrics();
     let tabs = {};
     for (let counter of counters) {
       let {items, host, windowId, duration, isWorker, isTopLevel} = counter;
       // If a worker has a windowId of 0 or max uint64, attach it to the
       // browser UI (doc group with id 1).
       if (isWorker && (windowId == 18446744073709552000 || !windowId))
         windowId = 1;
       let dispatchCount = 0;