merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 23 Jul 2017 11:27:03 +0200
changeset 419158 fd3634c4b16d89e71adce46be4cf80297e2fbc56
parent 419083 c023ce417c98641c0dfacaaf236dcfdcb7794f88 (current diff)
parent 419157 680e1321476c3c58df4a53cf118a40b23b8a7f3b (diff)
child 419159 23d9d005d92afdb5afabeae2ad672323c7d903ad
child 419168 6ebe13962237030e0965bb65f0848026c3f7e097
child 419206 5decf9441f50df54458f9d35fe0d583acbbec12f
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: Lanp1XedkmZ
browser/base/content/browser-refreshblocker.js
--- a/browser/base/content/browser-addons.js
+++ b/browser/base/content/browser-addons.js
@@ -1,13 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 // Removes a doorhanger notification if all of the installs it was notifying
 // about have ended in some way.
 function removeNotificationOnEnd(notification, installs) {
   let count = installs.length;
 
   function maybeRemove(install) {
     install.removeListener(this);
 
@@ -24,17 +27,17 @@ function removeNotificationOnEnd(notific
       onDownloadCancelled: maybeRemove,
       onDownloadFailed: maybeRemove,
       onInstallFailed: maybeRemove,
       onInstallEnded: maybeRemove
     });
   }
 }
 
-const gXPInstallObserver = {
+var gXPInstallObserver = {
   _findChildShell(aDocShell, aSoughtShell) {
     if (aDocShell == aSoughtShell)
       return aDocShell;
 
     var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeItem);
     for (var i = 0; i < node.childCount; ++i) {
       var docShell = node.getChildAt(i);
       docShell = this._findChildShell(docShell, aSoughtShell);
@@ -477,17 +480,17 @@ const gXPInstallObserver = {
   },
   _removeProgressNotification(aBrowser) {
     let notification = PopupNotifications.getNotification("addon-progress", aBrowser);
     if (notification)
       notification.remove();
   }
 };
 
-const gExtensionsNotifications = {
+var gExtensionsNotifications = {
   initialized: false,
   init() {
     this.updateAlerts();
     this.boundUpdate = this.updateAlerts.bind(this);
     ExtensionsUI.on("change", this.boundUpdate);
     this.initialized = true;
   },
 
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -1,13 +1,15 @@
-
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 /**
  * Tab previews utility, produces thumbnails
  */
 var tabPreviews = {
   init: function tabPreviews_init() {
     if (this._selectedTab)
       return;
     this._selectedTab = gBrowser.selectedTab;
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -1,13 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 /**
  * Customization handler prepares this browser window for entering and exiting
  * customization mode by handling customizationstarting and customizationending
  * events.
  */
 var CustomizationHandler = {
   handleEvent(aEvent) {
     switch (aEvent.type) {
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -1,13 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 var PointerlockFsWarning = {
 
   _element: null,
   _origin: null,
 
   /**
    * Timeout object for managing timeout request. If it is started when
    * the previous call hasn't finished, it would automatically cancelled
--- a/browser/base/content/browser-fullZoom.js
+++ b/browser/base/content/browser-fullZoom.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 /**
  * Controls the "full zoom" setting and its site-specific preferences.
  */
 var FullZoom = {
   // Identifies the setting in the content prefs database.
   name: "browser.content.full-zoom",
 
   // browser.zoom.siteSpecific preference cache
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 // Simple gestures support
 //
 // As per bug #412486, web content must not be allowed to receive any
 // simple gesture events.  Multi-touch gesture APIs are in their
 // infancy and we do NOT want to be forced into supporting an API that
 // will probably have to change in the future.  (The current Mac OS X
 // API is undocumented and was reverse-engineered.)  Until support is
 // implemented in the event dispatcher to keep these events as
@@ -525,17 +528,17 @@ var gGestureSupport = {
     return this._currentRotation;
   },
 
   /**
    * When the location/tab changes, need to reload the current rotation for the
    * image
    */
   restoreRotationState() {
-    // Bug 863514 - Make gesture support work in electrolysis
+    // Bug 1108553 - Cannot rotate images in stand-alone image documents with e10s
     if (gMultiProcessBrowser)
       return;
 
     if (!(window.content.document instanceof ImageDocument))
       return;
 
     let contentElement = window.content.document.body.firstElementChild;
     let transformValue = window.content.window.getComputedStyle(contentElement)
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1,12 +1,19 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
+XPCOMUtils.defineLazyScriptGetter(this, ["PlacesToolbar", "PlacesMenu",
+                                         "PlacesPanelview", "PlacesPanelMenuView"],
+                                  "chrome://browser/content/places/browserPlacesViews.js");
+
 var StarUI = {
   _itemId: -1,
   uri: null,
   _batching: false,
   _isNewBookmark: false,
   _isComposing: false,
   _autoCloseTimer: 0,
   // The autoclose timer is diasbled if the user interacts with the
@@ -1934,16 +1941,21 @@ var BookmarkingUI = {
     for (let i = 0, l = staticButtons.length; i < l; ++i)
       CustomizableUI.addShortcut(staticButtons[i]);
     // Setup the Places view.
     if (gPhotonStructure) {
       // We restrict the amount of results to 42. Not 50, but 42. Why? Because 42.
       let query = "place:queryType=" + Ci.nsINavHistoryQueryOptions.QUERY_TYPE_BOOKMARKS +
         "&sort=" + Ci.nsINavHistoryQueryOptions.SORT_BY_DATEADDED_DESCENDING +
         "&maxResults=42&excludeQueries=1";
+
+      // XPCOMUtils.defineLazyScriptGetter can't return class constructors, so
+      // trigger the getter once without using the result before calling
+      // PlacesPanelview as a constructor.
+      PlacesPanelview;
       this._panelMenuView = new PlacesPanelview(document.getElementById("panelMenu_bookmarksMenu"),
         panelview, query);
     } else {
       this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU",
         "panelMenu_bookmarksMenu", "panelMenu_bookmarksMenu", {
           extraClasses: {
             entry: "subviewbutton",
             footer: "panel-subview-footer"
deleted file mode 100644
--- a/browser/base/content/browser-refreshblocker.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
-
-/**
- * If the user has opted into blocking refresh and redirect attempts by
- * default, this handles showing the notification to the user which
- * gives them the option to let the refresh or redirect proceed.
- */
-var RefreshBlocker = {
-  init() {
-    gBrowser.addEventListener("RefreshBlocked", this);
-  },
-
-  uninit() {
-    gBrowser.removeEventListener("RefreshBlocked", this);
-  },
-
-  handleEvent(event) {
-    if (event.type == "RefreshBlocked") {
-      this.block(event.originalTarget, event.detail);
-    }
-  },
-
-  /**
-   * Shows the blocked refresh / redirect notification for some browser.
-   *
-   * @param browser (<xul:browser>)
-   *        The browser that had the refresh blocked. This will be the browser
-   *        for which we'll show the notification on.
-   * @param data (object)
-   *        An object with the following properties:
-   *
-   *        URI (string)
-   *          The URI that a page is attempting to refresh or redirect to.
-   *
-   *        delay (int)
-   *          The delay (in milliseconds) before the page was going to reload
-   *          or redirect.
-   *
-   *        sameURI (bool)
-   *          true if we're refreshing the page. false if we're redirecting.
-   *
-   *        outerWindowID (int)
-   *          The outerWindowID of the frame that requested the refresh or
-   *          redirect.
-   */
-  block(browser, data) {
-    let brandBundle = document.getElementById("bundle_brand");
-    let brandShortName = brandBundle.getString("brandShortName");
-    let message =
-      gNavigatorBundle.getFormattedString(data.sameURI ? "refreshBlocked.refreshLabel"
-                                                       : "refreshBlocked.redirectLabel",
-                                          [brandShortName]);
-
-    let notificationBox = gBrowser.getNotificationBox(browser);
-    let notification = notificationBox.getNotificationWithValue("refresh-blocked");
-
-    if (notification) {
-      notification.label = message;
-    } else {
-      let refreshButtonText =
-        gNavigatorBundle.getString("refreshBlocked.goButton");
-      let refreshButtonAccesskey =
-        gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
-
-      let buttons = [{
-        label: refreshButtonText,
-        accessKey: refreshButtonAccesskey,
-        callback() {
-          if (browser.messageManager) {
-            browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
-          }
-        }
-      }];
-
-      notificationBox.appendNotification(message, "refresh-blocked",
-                                         "chrome://browser/skin/Info.png",
-                                         notificationBox.PRIORITY_INFO_MEDIUM,
-                                         buttons);
-    }
-  }
-};
--- a/browser/base/content/browser-safebrowsing.js
+++ b/browser/base/content/browser-safebrowsing.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 var gSafeBrowsing = {
 
   setReportPhishingMenu() {
     // In order to detect whether or not we're at the phishing warning
     // page, we have to check the documentURI instead of the currentURI.
     // This is because when the DocShell loads an error page, the
     // currentURI stays at the original target, while the documentURI
     // will point to the internal error page we loaded instead.
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -20,22 +20,16 @@ XPCOMUtils.defineLazyGetter(this, "OpenG
 });
 
 XPCOMUtils.defineLazyGetter(this, "DynamicResizeWatcher", function() {
   let tmp = {};
   Cu.import("resource:///modules/Social.jsm", tmp);
   return tmp.DynamicResizeWatcher;
 });
 
-XPCOMUtils.defineLazyGetter(this, "Utils", function() {
-  let tmp = {};
-  Cu.import("resource://gre/modules/sessionstore/Utils.jsm", tmp);
-  return tmp.Utils;
-});
-
 let messageManager = window.messageManager;
 let openUILinkIn = window.openUILinkIn;
 
 SocialUI = {
   _initialized: false,
 
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
--- a/browser/base/content/browser-sync.js
+++ b/browser/base/content/browser-sync.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 Cu.import("resource://services-sync/UIState.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EnsureFxAccountsWebChannel",
   "resource://gre/modules/FxAccountsWebChannel.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Weave",
   "resource://services-sync/main.js");
 
 const MIN_STATUS_ANIMATION_DURATION = 1600;
--- a/browser/base/content/browser-tabsintitlebar.js
+++ b/browser/base/content/browser-tabsintitlebar.js
@@ -254,17 +254,17 @@ var TabsInTitlebar = {
       titlebarContent.style.marginTop = "";
       titlebarContent.style.marginBottom = "";
       titlebar.style.marginBottom = "";
       menubar.style.paddingBottom = "";
     }
 
     ToolbarIconColor.inferFromText("tabsintitlebar", TabsInTitlebar.enabled);
 
-    if (CustomizationHandler.isCustomizing()) {
+    if (document.documentElement.hasAttribute("customizing")) {
       gCustomizeMode.updateLWTStyling();
     }
   },
 
   _sizePlaceholder(type, width) {
     Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='" + type + "']"),
                   function(node) { node.width = width; });
   },
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -1,12 +1,15 @@
 /* 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/. */
 
+// This file is loaded into the browser window scope.
+/* eslint-env mozilla/browser-window */
+
 /**
  * Keeps thumbnails of open web pages up-to-date.
  */
 var gBrowserThumbnails = {
   /**
    * Pref that controls whether we can store SSL content on disk
    */
   PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -7,41 +7,43 @@
 
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cc = Components.classes;
 var Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/NotificationDB.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
-                                  "resource://gre/modules/Preferences.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
-                                  "resource://gre/modules/ContextualIdentityService.jsm");
-
 XPCOMUtils.defineLazyGetter(this, "extensionNameFromURI", () => {
   return Cu.import("resource://gre/modules/ExtensionParent.jsm", {}).extensionNameFromURI;
 });
 
+XPCOMUtils.defineLazyPreferenceGetter(this, "gPhotonStructure",
+  "browser.photon.structure.enabled", false);
+
 // lazy module getters
 
 /* global AboutHome:false,
           BrowserUITelemetry:false, BrowserUsageTelemetry:false, BrowserUtils:false,
           CastingApps:false, CharsetMenu:false, Color:false, ContentSearch:false,
+          CustomizableUI: false, DownloadsCommon: false,
           Deprecated:false, E10SUtils:false, ExtensionsUI: false, FormValidationHandler:false,
           GMPInstallManager:false, LightweightThemeManager:false, Log:false,
           LoginManagerParent:false, NewTabUtils:false, PageThumbs:false,
           PluralForm:false, Preferences:false, PrivateBrowsingUtils:false,
           ProcessHangMonitor:false, PromiseUtils:false, ReaderMode:false,
-          ReaderParent:false, RecentWindow:false, SessionStore:false,
+          ReaderParent:false, RecentWindow:false, SafeBrowsing: false,
+          SessionStore:false,
           ShortcutUtils:false, SimpleServiceDiscovery:false, SitePermissions:false,
           Social:false, TabCrashHandler:false, Task:false, TelemetryStopwatch:false,
-          Translation:false, UITour:false, UpdateUtils:false, Weave:false,
+          Translation:false, UITour:false, Utils:false, UpdateUtils:false,
+          Weave:false,
           WebNavigationFrames: false, fxAccounts:false, gDevTools:false,
           gDevToolsBrowser:false, webrtcUI:false, ZoomUI:false,
           Marionette:false,
  */
 
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
@@ -50,17 +52,20 @@ XPCOMUtils.defineLazyGetter(this, "exten
   ["AboutHome", "resource:///modules/AboutHome.jsm"],
   ["BrowserUITelemetry", "resource:///modules/BrowserUITelemetry.jsm"],
   ["BrowserUsageTelemetry", "resource:///modules/BrowserUsageTelemetry.jsm"],
   ["BrowserUtils", "resource://gre/modules/BrowserUtils.jsm"],
   ["CastingApps", "resource:///modules/CastingApps.jsm"],
   ["CharsetMenu", "resource://gre/modules/CharsetMenu.jsm"],
   ["Color", "resource://gre/modules/Color.jsm"],
   ["ContentSearch", "resource:///modules/ContentSearch.jsm"],
+  ["ContextualIdentityService", "resource://gre/modules/ContextualIdentityService.jsm"],
+  ["CustomizableUI", "resource:///modules/CustomizableUI.jsm"],
   ["Deprecated", "resource://gre/modules/Deprecated.jsm"],
+  ["DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"],
   ["E10SUtils", "resource:///modules/E10SUtils.jsm"],
   ["ExtensionsUI", "resource:///modules/ExtensionsUI.jsm"],
   ["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
   ["GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm"],
   ["LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"],
   ["Log", "resource://gre/modules/Log.jsm"],
   ["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
   ["NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"],
@@ -68,44 +73,92 @@ XPCOMUtils.defineLazyGetter(this, "exten
   ["PluralForm", "resource://gre/modules/PluralForm.jsm"],
   ["Preferences", "resource://gre/modules/Preferences.jsm"],
   ["PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"],
   ["ProcessHangMonitor", "resource:///modules/ProcessHangMonitor.jsm"],
   ["PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"],
   ["ReaderMode", "resource://gre/modules/ReaderMode.jsm"],
   ["ReaderParent", "resource:///modules/ReaderParent.jsm"],
   ["RecentWindow", "resource:///modules/RecentWindow.jsm"],
+  ["SafeBrowsing", "resource://gre/modules/SafeBrowsing.jsm"],
   ["SessionStore", "resource:///modules/sessionstore/SessionStore.jsm"],
   ["ShortcutUtils", "resource://gre/modules/ShortcutUtils.jsm"],
   ["SimpleServiceDiscovery", "resource://gre/modules/SimpleServiceDiscovery.jsm"],
   ["SitePermissions", "resource:///modules/SitePermissions.jsm"],
   ["Social", "resource:///modules/Social.jsm"],
   ["TabCrashHandler", "resource:///modules/ContentCrashHandlers.jsm"],
   ["Task", "resource://gre/modules/Task.jsm"],
   ["TelemetryStopwatch", "resource://gre/modules/TelemetryStopwatch.jsm"],
   ["Translation", "resource:///modules/translation/Translation.jsm"],
   ["UITour", "resource:///modules/UITour.jsm"],
   ["UpdateUtils", "resource://gre/modules/UpdateUtils.jsm"],
+  ["Utils", "resource://gre/modules/sessionstore/Utils.jsm"],
   ["Weave", "resource://services-sync/main.js"],
-  ["WebNavigationFrames", "resource://gre/modules/WebNavigationFrames.js"],
+  ["WebNavigationFrames", "resource://gre/modules/WebNavigationFrames.jsm"],
   ["fxAccounts", "resource://gre/modules/FxAccounts.jsm"],
   ["gDevTools", "resource://devtools/client/framework/gDevTools.jsm"],
   ["gDevToolsBrowser", "resource://devtools/client/framework/gDevTools.jsm"],
   ["webrtcUI", "resource:///modules/webrtcUI.jsm"],
   ["ZoomUI", "resource:///modules/ZoomUI.jsm"],
 ].forEach(([name, resource]) => XPCOMUtils.defineLazyModuleGetter(this, name, resource));
 
-XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
-  "resource://gre/modules/SafeBrowsing.jsm");
-
 if (AppConstants.MOZ_CRASHREPORTER) {
   XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
     "resource:///modules/ContentCrashHandlers.jsm");
 }
 
+XPCOMUtils.defineLazyScriptGetter(this, "PrintUtils",
+                                  "chrome://global/content/printUtils.js");
+XPCOMUtils.defineLazyScriptGetter(this, "ZoomManager",
+                                  "chrome://global/content/viewZoomOverlay.js");
+XPCOMUtils.defineLazyScriptGetter(this, "FullZoom",
+                                  "chrome://browser/content/browser-fullZoom.js");
+XPCOMUtils.defineLazyScriptGetter(this, "PanelUI",
+                                  "chrome://browser/content/customizableui/panelUI.js");
+XPCOMUtils.defineLazyScriptGetter(this, "gViewSourceUtils",
+                                  "chrome://global/content/viewSourceUtils.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["LightWeightThemeWebInstaller",
+                                         "gExtensionsNotifications",
+                                         "gXPInstallObserver"],
+                                  "chrome://browser/content/browser-addons.js");
+XPCOMUtils.defineLazyScriptGetter(this, "ctrlTab",
+                                  "chrome://browser/content/browser-ctrlTab.js");
+XPCOMUtils.defineLazyScriptGetter(this, "CustomizationHandler",
+                                  "chrome://browser/content/browser-customization.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["PointerLock", "FullScreen"],
+                                  "chrome://browser/content/browser-fullScreenAndPointerLock.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["gGestureSupport", "gHistorySwipeAnimation"],
+                                  "chrome://browser/content/browser-gestureSupport.js");
+XPCOMUtils.defineLazyScriptGetter(this, "gSafeBrowsing",
+                                  "chrome://browser/content/browser-safebrowsing.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["SocialUI",
+                                         "SocialShare",
+                                         "SocialActivationListener"],
+                                  "chrome://browser/content/browser-social.js");
+XPCOMUtils.defineLazyScriptGetter(this, "gSync",
+                                  "chrome://browser/content/browser-sync.js");
+XPCOMUtils.defineLazyScriptGetter(this, "gBrowserThumbnails",
+                                  "chrome://browser/content/browser-thumbnails.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["setContextMenuContentData",
+                                         "openContextMenu", "nsContextMenu"],
+                                  "chrome://browser/content/nsContextMenu.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["DownloadsPanel",
+                                         "DownloadsOverlayLoader",
+                                         "DownloadsView", "DownloadsViewUI",
+                                         "DownloadsViewController",
+                                         "DownloadsSummary", "DownloadsFooter",
+                                         "DownloadsBlockedSubview"],
+                                  "chrome://browser/content/downloads/downloads.js");
+XPCOMUtils.defineLazyScriptGetter(this, ["DownloadsButton",
+                                         "DownloadsIndicatorView"],
+                                  "chrome://browser/content/downloads/indicator.js");
+XPCOMUtils.defineLazyScriptGetter(this, "gEditItemOverlay",
+                                  "chrome://browser/content/places/editBookmarkOverlay.js");
+
+
 // lazy service getters
 
 /* global Favicons:false, WindowsUIUtils:false, gAboutNewTabService:false,
           gDNSService:false
 */
 /**
  * IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
  * XXX Bug 1325373 is for making eslint detect these automatically.
@@ -1266,17 +1319,16 @@ var gBrowserInit = {
     DOMLinkHandler.init();
     gPageStyleMenu.init();
     LanguageDetectionListener.init();
     BrowserOnClick.init();
     FeedHandler.init();
     CompactTheme.init();
     AboutPrivateBrowsingListener.init();
     TrackingProtection.init();
-    RefreshBlocker.init();
     CaptivePortalWatcher.init();
     ZoomUI.init(window);
 
     let mm = window.getGroupMessageManager("browsers");
     mm.loadFrameScript("chrome://browser/content/tab-content.js", true);
     mm.loadFrameScript("chrome://browser/content/content.js", true);
     mm.loadFrameScript("chrome://browser/content/content-UITour.js", true);
     mm.loadFrameScript("chrome://global/content/manifestMessages.js", true);
@@ -1293,22 +1345,16 @@ var gBrowserInit = {
         .getService(Ci.nsIEventListenerService)
         .addSystemEventListener(gBrowser, "click", contentAreaClick, true);
     }
 
     // hook up UI through progress listener
     gBrowser.addProgressListener(window.XULBrowserWindow);
     gBrowser.addTabsProgressListener(window.TabsProgressListener);
 
-    // setup simple gestures support
-    gGestureSupport.init(true);
-
-    // setup history swipe animation
-    gHistorySwipeAnimation.init();
-
     SidebarUI.init();
 
     // Certain kinds of automigration rely on this notification to complete
     // their tasks BEFORE the browser window is shown. SessionStore uses it to
     // restore tabs into windows AFTER important parts like gMultiProcessBrowser
     // have been initialized.
     Services.obs.notifyObservers(window, "browser-window-before-show");
 
@@ -1584,18 +1630,17 @@ var gBrowserInit = {
 
     // Initialize the download manager some time after the app starts so that
     // auto-resume downloads begin (such as after crashing or quitting with
     // active downloads) and speeds up the first-load of the download manager UI.
     // If the user manually opens the download manager before the timeout, the
     // downloads will start right away, and initializing again won't hurt.
     setTimeout(function() {
       try {
-        Cu.import("resource:///modules/DownloadsCommon.jsm", {})
-          .DownloadsCommon.initializeAllDataLinks();
+        DownloadsCommon.initializeAllDataLinks();
         Cu.import("resource:///modules/DownloadsTaskbar.jsm", {})
           .DownloadsTaskbar.registerIndicator(window);
       } catch (ex) {
         Cu.reportError(ex);
       }
     }, 10000);
 
     // Load the Login Manager data from disk off the main thread, some time
@@ -1637,17 +1682,25 @@ var gBrowserInit = {
     // initialize the sync UI
     requestIdleCallback(() => {
       gSync.init();
     }, {timeout: 1000 * 5});
 
     if (AppConstants.MOZ_DATA_REPORTING)
       gDataNotificationInfoBar.init();
 
-    gBrowserThumbnails.init();
+    requestIdleCallback(() => {
+      // setup simple gestures support
+      gGestureSupport.init(true);
+
+      // setup history swipe animation
+      gHistorySwipeAnimation.init();
+    });
+
+    requestIdleCallback(() => { gBrowserThumbnails.init(); });
 
     gExtensionsNotifications.init();
 
     let wasMinimized = window.windowState == window.STATE_MINIMIZED;
     window.addEventListener("sizemodechange", () => {
       let isMinimized = window.windowState == window.STATE_MINIMIZED;
       if (wasMinimized != isMinimized) {
         wasMinimized = isMinimized;
@@ -1796,18 +1849,16 @@ var gBrowserInit = {
     BrowserOnClick.uninit();
 
     FeedHandler.uninit();
 
     CompactTheme.uninit();
 
     TrackingProtection.uninit();
 
-    RefreshBlocker.uninit();
-
     CaptivePortalWatcher.uninit();
 
     SidebarUI.uninit();
 
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
@@ -1929,17 +1980,19 @@ if (AppConstants.platform == "macosx") {
 
     // initialise the offline listener
     BrowserOffline.init();
 
     // initialize the private browsing UI
     gPrivateBrowsingUI.init();
 
     // initialize the sync UI
-    gSync.init();
+    requestIdleCallback(() => {
+      gSync.init();
+    }, {timeout: 1000 * 5});
 
     if (AppConstants.E10S_TESTING_ONLY) {
       gRemoteTabsUI.init();
     }
   };
 
   gBrowserInit.nonBrowserWindowShutdown = function() {
     let dockSupport = Cc["@mozilla.org/widget/macdocksupport;1"]
@@ -4728,17 +4781,18 @@ var XULBrowserWindow = {
       } else if (CustomizationHandler.isEnteringCustomizeMode ||
                  CustomizationHandler.isCustomizing()) {
         gCustomizeMode.exit();
       }
     }
     UpdateBackForwardCommands(gBrowser.webNavigation);
     ReaderParent.updateReaderButton(gBrowser.selectedBrowser);
 
-    gGestureSupport.restoreRotationState();
+    if (!gMultiProcessBrowser) // Bug 1108553 - Cannot rotate images with e10s
+      gGestureSupport.restoreRotationState();
 
     // See bug 358202, when tabs are switched during a drag operation,
     // timers don't fire on windows (bug 203573)
     if (aRequest)
       setTimeout(function() { XULBrowserWindow.asyncUpdateUI(); }, 0);
     else
       this.asyncUpdateUI();
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -59,24 +59,19 @@
         sizemode="normal"
         retargetdocumentfocus="urlbar"
         persist="screenX screenY width height sizemode">
 
 # All JS files which are not content (only) dependent that browser.xul
 # wishes to include *must* go into the global-scripts.inc file
 # so that they can be shared by macBrowserOverlay.xul.
 #include global-scripts.inc
-<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
 
 <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
 
-<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
-<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
-<script type="application/javascript" src="chrome://browser/content/places/editBookmarkOverlay.js"/>
-
 # All sets except for popupsets (commands, keys, stringbundles and broadcasters) *must* go into the
 # browser-sets.inc file for sharing with hiddenWindow.xul.
 #define FULL_BROWSER_WINDOW
 #include browser-sets.inc
 #undef FULL_BROWSER_WINDOW
 
   <popupset id="mainPopupSet">
     <menupopup id="tabContextMenu"
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -198,17 +198,17 @@ var handleContentContextMenu = function(
                    }, {
                      event,
                      popupNode: event.target,
                    });
   } else {
     // Break out to the parent window and pass the add-on info along
     let browser = docShell.chromeEventHandler;
     let mainWin = browser.ownerGlobal;
-    mainWin.gContextMenuContentData = {
+    mainWin.setContextMenuContentData({
       isRemote: false,
       event,
       popupNode: event.target,
       popupNodeSelectors,
       browser,
       addonInfo,
       documentURIObject: doc.documentURIObject,
       docLocation,
@@ -217,17 +217,17 @@ var handleContentContextMenu = function(
       referrerPolicy,
       contentType,
       contentDisposition,
       selectionInfo,
       disableSetDesktopBackground: disableSetDesktopBg,
       loginFillInfo,
       parentAllowsMixedContent,
       userContextId,
-    };
+    });
   }
 }
 
 Cc["@mozilla.org/eventlistenerservice;1"]
   .getService(Ci.nsIEventListenerService)
   .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
 
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -2,39 +2,23 @@
 # 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/.
 
 # If you update this list, you may need to add a mapping within the following
 # file so that ESLint works correctly:
 # tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
 
-<script type="application/javascript" src="chrome://global/content/printUtils.js"/>
-<script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
-<script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser.js"/>
-<script type="application/javascript" src="chrome://browser/content/customizableui/panelUI.js"/>
-<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
 
-<script type="application/javascript" src="chrome://browser/content/browser-addons.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-captivePortal.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-ctrlTab.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-customization.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-compacttheme.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-fullScreenAndPointerLock.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-media.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-refreshblocker.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-sync.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
-<script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/>
 
 #ifdef MOZ_DATA_REPORTING
 <script type="application/javascript" src="chrome://browser/content/browser-data-submission-info-bar.js"/>
 #endif
--- a/browser/base/content/moz.build
+++ b/browser/base/content/moz.build
@@ -125,19 +125,16 @@ with Files("browser-media.js"):
     BUG_COMPONENT = ("Core", "Audio/Video: Playback")
 
 with Files("browser-places.js"):
     BUG_COMPONENT = ("Firefox", "Bookmarks & History")
 
 with Files("browser-plugins.js"):
     BUG_COMPONENT = ("Core", "Plug-ins")
 
-with Files("browser-refreshblocker.js"):
-    BUG_COMPONENT = ("Firefox", "Disability Access")
-
 with Files("browser-safebrowsing.js"):
     BUG_COMPONENT = ("Toolkit", "Safe Browsing")
 
 with Files("*social*"):
     BUG_COMPONENT = ("Firefox", "SocialAPI")
 
 with Files("browser-sync.js"):
     BUG_COMPONENT = ("Firefox", "Sync")
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -20,16 +20,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/WebNavigationFrames.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
   "resource://gre/modules/ContextualIdentityService.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DevToolsShim",
   "chrome://devtools-shim/content/DevToolsShim.jsm");
 
 var gContextMenuContentData = null;
 
+function setContextMenuContentData(data) {
+  gContextMenuContentData = data;
+}
+
 function openContextMenu(aMessage) {
   let data = aMessage.data;
   let browser = aMessage.target;
 
   let spellInfo = data.spellInfo;
   if (spellInfo)
     spellInfo.target = aMessage.target.messageManager;
   let documentURIObject = makeURI(data.docLocation,
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -5271,24 +5271,62 @@
                 if (shouldFastFind) {
                   // Make sure we return the result.
                   return this.getFindBar(tab).receiveMessage(aMessage);
                 }
               }
               break;
             }
             case "RefreshBlocker:Blocked": {
-              let event = new CustomEvent("RefreshBlocked", {
-                bubbles: true,
-                cancelable: false,
-                detail: data,
-              });
-
-              browser.dispatchEvent(event);
-
+              // The data object is expected to contain the following properties:
+              //  - URI (string)
+              //     The URI that a page is attempting to refresh or redirect to.
+              //  - delay (int)
+              //     The delay (in milliseconds) before the page was going to
+              //     reload or redirect.
+              //  - sameURI (bool)
+              //     true if we're refreshing the page. false if we're redirecting.
+              //  - outerWindowID (int)
+              //     The outerWindowID of the frame that requested the refresh or
+              //     redirect.
+
+              let brandBundle = document.getElementById("bundle_brand");
+              let brandShortName = brandBundle.getString("brandShortName");
+              let message =
+                gNavigatorBundle.getFormattedString("refreshBlocked." +
+                                                    (data.sameURI ? "refreshLabel"
+                                                                  : "redirectLabel"),
+                                                    [brandShortName]);
+
+              let notificationBox = this.getNotificationBox(browser);
+              let notification = notificationBox.getNotificationWithValue("refresh-blocked");
+
+              if (notification) {
+                notification.label = message;
+              } else {
+                let refreshButtonText =
+                  gNavigatorBundle.getString("refreshBlocked.goButton");
+                let refreshButtonAccesskey =
+                  gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
+
+                let buttons = [{
+                  label: refreshButtonText,
+                  accessKey: refreshButtonAccesskey,
+                  callback() {
+                    if (browser.messageManager) {
+                      browser.messageManager.sendAsyncMessage("RefreshBlocker:Refresh", data);
+                    }
+                  }
+                }];
+
+                notificationBox.appendNotification(message, "refresh-blocked",
+                                                   "chrome://browser/skin/Info.png",
+                                                   notificationBox.PRIORITY_INFO_MEDIUM,
+                                                   buttons);
+              }
               break;
             }
 
             case "Prerender:Request": {
               let sendCancelPrerendering = () => {
                 browser.frameloader.messageManager.
                   sendAsyncMessage("Prerender:Canceled", { id: data.id });
               };
--- a/browser/base/content/test/performance/browser_startup.js
+++ b/browser/base/content/test/performance/browser_startup.js
@@ -59,16 +59,18 @@ const startupPhases = {
   // This means that anything already loaded at this point has been loaded
   // before first paint and delayed it.
   "before first paint": {blacklist: {
     components: new Set([
       "UnifiedComplete.js",
       "nsSearchService.js",
     ]),
     modules: new Set([
+      "chrome://webcompat-reporter/content/TabListener.jsm",
+      "chrome://webcompat-reporter/content/WebCompatReporter.jsm",
       "resource:///modules/AboutNewTab.jsm",
       "resource:///modules/BrowserUITelemetry.jsm",
       "resource:///modules/BrowserUsageTelemetry.jsm",
       "resource:///modules/ContentCrashHandlers.jsm",
       "resource:///modules/DirectoryLinksProvider.jsm",
       "resource://gre/modules/NewTabUtils.jsm",
       "resource://gre/modules/PageThumbs.jsm",
       "resource://gre/modules/Promise.jsm", // imported by devtools during _delayedStartup
@@ -83,18 +85,16 @@ const startupPhases = {
   // interacting with the first browser window.
   "before handling user events": {blacklist: {
     components: new Set([
       "PageIconProtocolHandler.js",
       "PlacesCategoriesStarter.js",
       "nsPlacesExpiration.js",
     ]),
     modules: new Set([
-      "chrome://webcompat-reporter/content/TabListener.jsm",
-      "chrome://webcompat-reporter/content/WebCompatReporter.jsm",
       "resource:///modules/RecentWindow.jsm",
       "resource://gre/modules/BookmarkHTMLUtils.jsm",
       "resource://gre/modules/Bookmarks.jsm",
       "resource://gre/modules/ContextualIdentityService.jsm",
       "resource://gre/modules/CrashSubmit.jsm",
       "resource://gre/modules/FxAccounts.jsm",
       "resource://gre/modules/FxAccountsStorage.jsm",
       "resource://gre/modules/PlacesSyncUtils.jsm",
--- a/browser/base/content/test/sync/head.js
+++ b/browser/base/content/test/sync/head.js
@@ -1,11 +1,13 @@
 /* global sinon */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
+Cu.import("resource://services-sync/UIState.jsm", this);
+
 registerCleanupFunction(function() {
   delete window.sinon;
 });
 
 function promiseSyncReady() {
   let service = Cc["@mozilla.org/weave/service;1"]
                   .getService(Components.interfaces.nsISupports)
                   .wrappedJSObject;
--- a/browser/base/content/test/urlbar/browser_page_action_menu.js
+++ b/browser/base/content/test/urlbar/browser_page_action_menu.js
@@ -2,16 +2,18 @@
 
 /* global sinon */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js");
 
 registerCleanupFunction(function() {
   delete window.sinon;
 });
 
+Cu.import("resource://services-sync/UIState.jsm");
+
 const mockRemoteClients = [
   { id: "0", name: "foo", type: "mobile" },
   { id: "1", name: "bar", type: "desktop" },
   { id: "2", name: "baz", type: "mobile" },
 ];
 
 add_task(async function bookmark() {
   // Open a unique page.
--- a/browser/base/content/web-panels.xul
+++ b/browser/base/content/web-panels.xul
@@ -18,19 +18,16 @@
 
 <page id="webpanels-window"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="load()" onunload="unload()">
   <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
-  <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
-  <script type="application/javascript" src="chrome://browser/content/browser-sync.js"/>
-  <script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
   <script type="application/javascript" src="chrome://browser/content/web-panels.js"/>
 
   <stringbundleset id="stringbundleset"> 
     <stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
   </stringbundleset>
 
   <broadcasterset id="mainBroadcasterSet">
     <broadcaster id="isFrameImage"/>
--- a/browser/base/content/webext-panels.xul
+++ b/browser/base/content/webext-panels.xul
@@ -19,19 +19,16 @@
 
 <page id="webextpanels-window"
         xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         onload="load()">
   <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
-  <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
-  <script type="application/javascript" src="chrome://browser/content/browser-sync.js"/>
-  <script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
   <script type="application/javascript" src="chrome://browser/content/webext-panels.js"/>
 
   <stringbundleset id="stringbundleset">
     <stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
   </stringbundleset>
 
   <broadcasterset id="mainBroadcasterSet">
     <broadcaster id="isFrameImage"/>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -71,17 +71,16 @@ browser.jar:
         content/browser/browser-compacttheme.js       (content/browser-compacttheme.js)
         content/browser/browser-feeds.js              (content/browser-feeds.js)
         content/browser/browser-fullScreenAndPointerLock.js  (content/browser-fullScreenAndPointerLock.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
         content/browser/browser-media.js              (content/browser-media.js)
         content/browser/browser-places.js             (content/browser-places.js)
         content/browser/browser-plugins.js            (content/browser-plugins.js)
-        content/browser/browser-refreshblocker.js     (content/browser-refreshblocker.js)
         content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-social.js             (content/browser-social.js)
         content/browser/browser-sync.js               (content/browser-sync.js)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
 #ifdef CAN_DRAW_IN_TITLEBAR
         content/browser/browser-tabsintitlebar.js       (content/browser-tabsintitlebar.js)
 #else
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -1,26 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
-                                  "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
                                   "resource:///modules/ScrollbarSampler.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
-                                  "resource://gre/modules/ShortcutUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
-                                  "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppMenuNotifications",
                                   "resource://gre/modules/AppMenuNotifications.jsm");
 
-XPCOMUtils.defineLazyPreferenceGetter(this, "gPhotonStructure",
-  "browser.photon.structure.enabled", false);
-
 /**
  * Maintains the state and dispatches events for the main menu panel.
  */
 
 const PanelUI = {
   /** Panel events that we listen for. **/
   get kEvents() {
     return ["popupshowing", "popupshown", "popuphiding", "popuphidden"];
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -61,35 +61,33 @@
  */
 
 "use strict";
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
-                                  "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsViewUI",
                                   "resource:///modules/DownloadsViewUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
 // DownloadsPanel
 
 /**
  * Main entry point for the downloads panel interface.
  */
-const DownloadsPanel = {
+var DownloadsPanel = {
   // Initialization and termination
 
   /**
    * Internal state of the downloads panel, based on one of the kState
    * constants.  This is not the same state as the XUL panel element.
    */
   _state: 0,
 
@@ -572,17 +570,17 @@ const DownloadsPanel = {
 XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel);
 
 // DownloadsOverlayLoader
 
 /**
  * Allows loading the downloads panel and the status indicator interfaces on
  * demand, to improve startup performance.
  */
-const DownloadsOverlayLoader = {
+var DownloadsOverlayLoader = {
   /**
    * We cannot load two overlays at the same time, thus we use a queue of
    * pending load requests.
    */
   _loadRequests: [],
 
   /**
    * True while we are waiting for an overlay to be loaded.
@@ -652,17 +650,17 @@ XPCOMUtils.defineConstant(this, "Downloa
 
 // DownloadsView
 
 /**
  * Builds and updates the downloads list widget, responding to changes in the
  * download state and real-time data.  In addition, handles part of the user
  * interaction events raised by the downloads list widget.
  */
-const DownloadsView = {
+var DownloadsView = {
   // Functions handling download items in the list
 
   /**
    * Maximum number of items shown by the list at any given time.
    */
   kItemCountLimit: 5,
 
   /**
@@ -1184,17 +1182,17 @@ DownloadsViewItem.prototype = {
 
 // DownloadsViewController
 
 /**
  * Handles part of the user interaction events raised by the downloads list
  * widget, in particular the "commands" that apply to multiple items, and
  * dispatches the commands that apply to individual items.
  */
-const DownloadsViewController = {
+var DownloadsViewController = {
   // Initialization and termination
 
   initialize() {
     window.controllers.insertControllerAt(0, this);
   },
 
   terminate() {
     window.controllers.removeController(this);
@@ -1287,17 +1285,17 @@ const DownloadsViewController = {
 XPCOMUtils.defineConstant(this, "DownloadsViewController", DownloadsViewController);
 
 // DownloadsSummary
 
 /**
  * Manages the summary at the bottom of the downloads panel list if the number
  * of items in the list exceeds the panels limit.
  */
-const DownloadsSummary = {
+var DownloadsSummary = {
 
   /**
    * Sets the active state of the summary. When active, the summary subscribes
    * to the DownloadsCommon DownloadsSummaryData singleton.
    *
    * @param aActive
    *        Set to true to activate the summary.
    */
@@ -1471,17 +1469,17 @@ const DownloadsSummary = {
 XPCOMUtils.defineConstant(this, "DownloadsSummary", DownloadsSummary);
 
 // DownloadsFooter
 
 /**
  * Manages events sent to to the footer vbox, which contains both the
  * DownloadsSummary as well as the "Show All Downloads" button.
  */
-const DownloadsFooter = {
+var DownloadsFooter = {
 
   /**
    * Focuses the appropriate element within the footer. If the summary
    * is visible, focus it. If not, focus the "Show All Downloads"
    * button.
    */
   focus() {
     if (this._showingSummary) {
@@ -1525,17 +1523,17 @@ const DownloadsFooter = {
 XPCOMUtils.defineConstant(this, "DownloadsFooter", DownloadsFooter);
 
 
 // DownloadsBlockedSubview
 
 /**
  * Manages the blocked subview that slides in when you click a blocked download.
  */
-const DownloadsBlockedSubview = {
+var DownloadsBlockedSubview = {
 
   get subview() {
     let subview = document.getElementById("downloadsPanel-blockedSubview");
     delete this.subview;
     return this.subview = subview;
   },
 
   /**
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -1,18 +1,14 @@
 /* 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/. */
 
 /* eslint-env mozilla/browser-window */
 
-Components.utils.import("resource://gre/modules/AppConstants.jsm");
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/Services.jsm");
-
 /**
  * The base view implements everything that's common to the toolbar and
  * menu views.
  */
 function PlacesViewBase(aPlace, aOptions = {}) {
   if ("rootElt" in aOptions)
     this._rootElt = aOptions.rootElt;
   if ("viewElt" in aOptions)
--- a/browser/components/preferences/applicationManager.js
+++ b/browser/components/preferences/applicationManager.js
@@ -12,20 +12,22 @@ var gAppManagerDialog = {
 
   init: function appManager_init() {
     this.handlerInfo = window.arguments[0];
     // The applicationManager will be used
     // in in-content's gApplicationsPane and in-content-new's gMainPane.
     // Remove this once we use the in-content-new preferences page.
     var pane;
     if (Services.prefs.getBoolPref("browser.preferences.useOldOrganization")) {
-      Services.scriptloader.loadSubScript("chrome://browser/content/preferences/in-content/applications.js");
+      Services.scriptloader.loadSubScript("chrome://browser/content/preferences/in-content/applications.js",
+                                          window);
       pane = gApplicationsPane;
     } else {
-      Services.scriptloader.loadSubScript("chrome://browser/content/preferences/in-content-new/main.js");
+      Services.scriptloader.loadSubScript("chrome://browser/content/preferences/in-content-new/main.js",
+                                          window);
       pane = gMainPane;
     }
     var bundle = document.getElementById("appManagerBundle");
     var contentText;
     if (this.handlerInfo.type == TYPE_MAYBE_FEED)
       contentText = bundle.getString("handleWebFeeds");
     else {
       var description = pane._describeType(this.handlerInfo);
--- a/browser/components/uitour/test/browser_fxa.js
+++ b/browser/components/uitour/test/browser_fxa.js
@@ -1,15 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
+Cu.import("resource://services-sync/UIState.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
                                   "resource://gre/modules/FxAccounts.jsm");
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 
 function test() {
--- 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: 1.8.557
+Current extension version is: 1.8.564
 
-Taken from upstream commit: 4a74cc41
+Taken from upstream commit: e7cddcce
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -1428,34 +1428,42 @@ Object.defineProperty(exports, "__esModu
 });
 exports.DOMCMapReaderFactory = exports.DOMCanvasFactory = exports.DEFAULT_LINK_REL = exports.getDefaultSetting = exports.LinkTarget = exports.getFilenameFromUrl = exports.isValidUrl = exports.isExternalLinkTargetSet = exports.addLinkAttributes = exports.RenderingCancelledException = exports.CustomStyle = undefined;
 
 var _util = __w_pdfjs_require__(0);
 
 var DEFAULT_LINK_REL = 'noopener noreferrer nofollow';
 class DOMCanvasFactory {
   create(width, height) {
-    (0, _util.assert)(width > 0 && height > 0, 'invalid canvas size');
+    if (width <= 0 || height <= 0) {
+      throw new Error('invalid canvas size');
+    }
     let canvas = document.createElement('canvas');
     let context = canvas.getContext('2d');
     canvas.width = width;
     canvas.height = height;
     return {
       canvas,
       context
     };
   }
   reset(canvasAndContext, width, height) {
-    (0, _util.assert)(canvasAndContext.canvas, 'canvas is not specified');
-    (0, _util.assert)(width > 0 && height > 0, 'invalid canvas size');
+    if (!canvasAndContext.canvas) {
+      throw new Error('canvas is not specified');
+    }
+    if (width <= 0 || height <= 0) {
+      throw new Error('invalid canvas size');
+    }
     canvasAndContext.canvas.width = width;
     canvasAndContext.canvas.height = height;
   }
   destroy(canvasAndContext) {
-    (0, _util.assert)(canvasAndContext.canvas, 'canvas is not specified');
+    if (!canvasAndContext.canvas) {
+      throw new Error('canvas is not specified');
+    }
     canvasAndContext.canvas.width = 0;
     canvasAndContext.canvas.height = 0;
     canvasAndContext.canvas = null;
     canvasAndContext.context = null;
   }
 }
 class DOMCMapReaderFactory {
   constructor({ baseUrl = null, isCompressed = false }) {
@@ -3688,18 +3696,18 @@ var _UnsupportedManager = function Unsup
       for (var i = 0, ii = listeners.length; i < ii; i++) {
         listeners[i](featureId);
       }
     }
   };
 }();
 var version, build;
 {
-  exports.version = version = '1.8.557';
-  exports.build = build = '4a74cc41';
+  exports.version = version = '1.8.564';
+  exports.build = build = 'e7cddcce';
 }
 exports.getDocument = getDocument;
 exports.LoopbackPort = LoopbackPort;
 exports.PDFDataRangeTransport = PDFDataRangeTransport;
 exports.PDFWorker = PDFWorker;
 exports.PDFDocumentProxy = PDFDocumentProxy;
 exports.PDFPageProxy = PDFPageProxy;
 exports._UnsupportedManager = _UnsupportedManager;
@@ -4738,18 +4746,18 @@ var _text_layer = __w_pdfjs_require__(5)
 var _svg = __w_pdfjs_require__(4);
 
 var isWorker = typeof window === 'undefined';
 if (!_util.globalScope.PDFJS) {
   _util.globalScope.PDFJS = {};
 }
 var PDFJS = _util.globalScope.PDFJS;
 {
-  PDFJS.version = '1.8.557';
-  PDFJS.build = '4a74cc41';
+  PDFJS.version = '1.8.564';
+  PDFJS.build = 'e7cddcce';
 }
 PDFJS.pdfBug = false;
 if (PDFJS.verbosity !== undefined) {
   (0, _util.setVerbosityLevel)(PDFJS.verbosity);
 }
 delete PDFJS.verbosity;
 Object.defineProperty(PDFJS, 'verbosity', {
   get() {
@@ -9211,17 +9219,19 @@ var CanvasGraphics = function CanvasGrap
       }
       if (group.knockout) {
         (0, _util.warn)('Knockout groups not supported.');
       }
       var currentTransform = currentCtx.mozCurrentTransform;
       if (group.matrix) {
         currentCtx.transform.apply(currentCtx, group.matrix);
       }
-      (0, _util.assert)(group.bbox, 'Bounding box is required.');
+      if (!group.bbox) {
+        throw new Error('Bounding box is required.');
+      }
       var bounds = _util.Util.getAxialAlignedBoundingBox(group.bbox, currentCtx.mozCurrentTransform);
       var canvasBounds = [0, 0, currentCtx.canvas.width, currentCtx.canvas.height];
       bounds = _util.Util.intersect(bounds, canvasBounds) || [0, 0, 0, 0];
       var offsetX = Math.floor(bounds[0]);
       var offsetY = Math.floor(bounds[1]);
       var drawnWidth = Math.max(Math.ceil(bounds[2]) - offsetX, 1);
       var drawnHeight = Math.max(Math.ceil(bounds[3]) - offsetY, 1);
       var scaleX = 1,
@@ -10067,18 +10077,18 @@ exports.TilingPattern = TilingPattern;
 
 /***/ }),
 /* 13 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.557';
-var pdfjsBuild = '4a74cc41';
+var pdfjsVersion = '1.8.564';
+var pdfjsBuild = 'e7cddcce';
 var pdfjsSharedUtil = __w_pdfjs_require__(0);
 var pdfjsDisplayGlobal = __w_pdfjs_require__(8);
 var pdfjsDisplayAPI = __w_pdfjs_require__(3);
 var pdfjsDisplayTextLayer = __w_pdfjs_require__(5);
 var pdfjsDisplayAnnotationLayer = __w_pdfjs_require__(2);
 var pdfjsDisplayDOMUtils = __w_pdfjs_require__(1);
 var pdfjsDisplaySVG = __w_pdfjs_require__(4);
 exports.PDFJS = pdfjsDisplayGlobal.PDFJS;
--- a/browser/extensions/pdfjs/content/build/pdf.worker.js
+++ b/browser/extensions/pdfjs/content/build/pdf.worker.js
@@ -13910,17 +13910,19 @@ var CFFParser = function CFFParserClosur
           rawBytes = bytes.subarray(start, pos);
           if (invalidFirstGID) {
             rawBytes[3] = rawBytes[4] = 0;
           }
           break;
         default:
           throw new _util.FormatError(`parseFDSelect: Unknown format "${format}".`);
       }
-      (0, _util.assert)(fdSelect.length === length, 'parseFDSelect: Invalid font data.');
+      if (fdSelect.length !== length) {
+        throw new _util.FormatError('parseFDSelect: Invalid font data.');
+      }
       return new CFFFDSelect(fdSelect, rawBytes);
     }
   };
   return CFFParser;
 }();
 var CFF = function CFFClosure() {
   function CFF() {
     this.header = null;
@@ -14339,18 +14341,20 @@ var CFFCompiler = function CFFCompilerCl
       return {
         trackers: fontDictTrackers,
         output: fdArrayIndex
       };
     },
     compilePrivateDicts: function CFFCompiler_compilePrivateDicts(dicts, trackers, output) {
       for (var i = 0, ii = dicts.length; i < ii; ++i) {
         var fontDict = dicts[i];
-        (0, _util.assert)(fontDict.privateDict && fontDict.hasName('Private'), 'There must be an private dictionary.');
         var privateDict = fontDict.privateDict;
+        if (!privateDict || !fontDict.hasName('Private')) {
+          throw new _util.FormatError('There must be a private dictionary.');
+        }
         var privateDictTracker = new CFFOffsetTracker();
         var privateDictData = this.compileDict(privateDict, privateDictTracker);
         var outputLength = output.length;
         privateDictTracker.offset(outputLength);
         if (!privateDictData.length) {
           outputLength = 0;
         }
         trackers[i].setEntryLocation('Private', [privateDictData.length, outputLength], output);
@@ -14545,19 +14549,23 @@ var ChunkedStream = function ChunkedStre
     getBaseStreams: function ChunkedStream_getBaseStreams() {
       return [this];
     },
     allChunksLoaded: function ChunkedStream_allChunksLoaded() {
       return this.numChunksLoaded === this.numChunks;
     },
     onReceiveData: function ChunkedStream_onReceiveData(begin, chunk) {
       var end = begin + chunk.byteLength;
-      (0, _util.assert)(begin % this.chunkSize === 0, 'Bad begin offset: ' + begin);
+      if (begin % this.chunkSize !== 0) {
+        throw new Error(`Bad begin offset: ${begin}`);
+      }
       var length = this.bytes.length;
-      (0, _util.assert)(end % this.chunkSize === 0 || end === length, 'Bad end offset: ' + end);
+      if (end % this.chunkSize !== 0 && end !== length) {
+        throw new Error(`Bad end offset: ${end}`);
+      }
       this.bytes.set(new Uint8Array(chunk), begin);
       var chunkSize = this.chunkSize;
       var beginChunk = Math.floor(begin / chunkSize);
       var endChunk = Math.floor((end - 1) / chunkSize) + 1;
       var curChunk;
       for (curChunk = beginChunk; curChunk < endChunk; ++curChunk) {
         if (!this.loadedChunks[curChunk]) {
           this.loadedChunks[curChunk] = true;
@@ -16501,17 +16509,19 @@ var CipherTransformFactory = function Ci
       key[i++] = 0x41;
       key[i++] = 0x6C;
       key[i++] = 0x54;
     }
     var hash = calculateMD5(key, 0, i);
     return hash.subarray(0, Math.min(encryptionKey.length + 5, 16));
   }
   function buildCipherConstructor(cf, name, num, gen, key) {
-    (0, _util.assert)((0, _primitives.isName)(name), 'Invalid crypt filter name.');
+    if (!(0, _primitives.isName)(name)) {
+      throw new _util.FormatError('Invalid crypt filter name.');
+    }
     var cryptFilter = cf.get(name.name);
     var cfm;
     if (cryptFilter !== null && cryptFilter !== undefined) {
       cfm = cryptFilter.get('CFM');
     }
     if (!cfm || cfm.name === 'None') {
       return function cipherTransformFactoryBuildCipherConstructorNone() {
         return new NullCipher();
@@ -17083,17 +17093,19 @@ var PartialEvaluator = function PartialE
     },
     loadFont: function PartialEvaluator_loadFont(fontName, font, resources) {
       function errorFont() {
         return Promise.resolve(new TranslatedFont('g_font_error', new _fonts.ErrorFont('Font ' + fontName + ' is not available'), font));
       }
       var fontRef,
           xref = this.xref;
       if (font) {
-        (0, _util.assert)((0, _primitives.isRef)(font));
+        if (!(0, _primitives.isRef)(font)) {
+          throw new Error('The "font" object should be a reference.');
+        }
         fontRef = font;
       } else {
         var fontRes = resources.get('Font');
         if (fontRes) {
           fontRef = fontRes.getRaw(fontName);
         } else {
           (0, _util.warn)('fontRes not available');
           return errorFont();
@@ -17210,17 +17222,19 @@ var PartialEvaluator = function PartialE
         return Promise.reject(new Error('Unknown PatternType: ' + typeNum));
       }
       operatorList.addOp(fn, args);
       return Promise.resolve();
     },
     getOperatorList({ stream, task, resources, operatorList, initialState = null }) {
       resources = resources || _primitives.Dict.empty;
       initialState = initialState || new EvalState();
-      (0, _util.assert)(operatorList, 'getOperatorList: missing "operatorList" parameter');
+      if (!operatorList) {
+        throw new Error('getOperatorList: missing "operatorList" parameter');
+      }
       var self = this;
       var xref = this.xref;
       var imageCache = Object.create(null);
       var xobjs = resources.get('XObject') || _primitives.Dict.empty;
       var patterns = resources.get('Pattern') || _primitives.Dict.empty;
       var stateManager = new StateManager(initialState);
       var preprocessor = new EvaluatorPreprocessor(stream, xref, stateManager);
       var timeSlotManager = new TimeSlotManager();
@@ -17265,19 +17279,23 @@ var PartialEvaluator = function PartialE
               }
               if (imageCache[name] !== undefined) {
                 operatorList.addOp(imageCache[name].fn, imageCache[name].args);
                 args = null;
                 continue;
               }
               var xobj = xobjs.get(name);
               if (xobj) {
-                (0, _util.assert)((0, _primitives.isStream)(xobj), 'XObject should be a stream');
+                if (!(0, _primitives.isStream)(xobj)) {
+                  throw new _util.FormatError('XObject should be a stream');
+                }
                 var type = xobj.dict.get('Subtype');
-                (0, _util.assert)((0, _primitives.isName)(type), 'XObject should have a Name subtype');
+                if (!(0, _primitives.isName)(type)) {
+                  throw new _util.FormatError('XObject should have a Name subtype');
+                }
                 if (type.name === 'Form') {
                   stateManager.save();
                   next(self.buildFormXObject(resources, xobj, null, operatorList, task, stateManager.state.clone()).then(function () {
                     stateManager.restore();
                   }));
                   return;
                 } else if (type.name === 'Image') {
                   self.buildPaintImageXObject(resources, xobj, false, operatorList, name, imageCache);
@@ -17404,19 +17422,23 @@ var PartialEvaluator = function PartialE
                 next(self.handleColorN(operatorList, _util.OPS.setStrokeColorN, args, cs, patterns, resources, task));
                 return;
               }
               args = cs.getRgb(args, 0);
               fn = _util.OPS.setStrokeRGBColor;
               break;
             case _util.OPS.shadingFill:
               var shadingRes = resources.get('Shading');
-              (0, _util.assert)(shadingRes, 'No shading resource found');
+              if (!shadingRes) {
+                throw new _util.FormatError('No shading resource found');
+              }
               var shading = shadingRes.get(args[0].name);
-              (0, _util.assert)(shading, 'No shading object found');
+              if (!shading) {
+                throw new _util.FormatError('No shading object found');
+              }
               var shadingFill = _pattern.Pattern.parseShading(shading, null, xref, resources, self.handler);
               var patternIR = shadingFill.getIR();
               args = [patternIR];
               fn = _util.OPS.shadingFill;
               break;
             case _util.OPS.setGState:
               var dictName = args[0];
               var extGState = resources.get('ExtGState');
@@ -17840,19 +17862,23 @@ var PartialEvaluator = function PartialE
               var name = args[0].name;
               if (name in skipEmptyXObjs) {
                 break;
               }
               var xobj = xobjs.get(name);
               if (!xobj) {
                 break;
               }
-              (0, _util.assert)((0, _primitives.isStream)(xobj), 'XObject should be a stream');
+              if (!(0, _primitives.isStream)(xobj)) {
+                throw new _util.FormatError('XObject should be a stream');
+              }
               var type = xobj.dict.get('Subtype');
-              (0, _util.assert)((0, _primitives.isName)(type), 'XObject should have a Name subtype');
+              if (!(0, _primitives.isName)(type)) {
+                throw new _util.FormatError('XObject should have a Name subtype');
+              }
               if (type.name !== 'Form') {
                 skipEmptyXObjs[name] = true;
                 break;
               }
               var currentState = stateManager.state.clone();
               var xObjStateManager = new StateManager(currentState);
               var matrix = xobj.dict.getArray('Matrix');
               if ((0, _util.isArray)(matrix) && matrix.length === 6) {
@@ -18082,17 +18108,19 @@ var PartialEvaluator = function PartialE
         return _cmap.CMapFactory.create({
           encoding: ucs2CMapName,
           fetchBuiltInCMap: this.fetchBuiltInCMap,
           useCMap: null
         }).then(function (ucs2CMap) {
           var cMap = properties.cMap;
           toUnicode = [];
           cMap.forEach(function (charcode, cid) {
-            (0, _util.assert)(cid <= 0xffff, 'Max size of CID is 65,535');
+            if (cid > 0xffff) {
+              throw new _util.FormatError('Max size of CID is 65,535');
+            }
             var ucs2 = ucs2CMap.lookup(cid);
             if (ucs2) {
               toUnicode[charcode] = String.fromCharCode((ucs2.charCodeAt(0) << 8) + ucs2.charCodeAt(1));
             }
           });
           return new _fonts.ToUnicodeMap(toUnicode);
         });
       }
@@ -18287,25 +18315,31 @@ var PartialEvaluator = function PartialE
           continue;
         }
       }
       return widths;
     },
     preEvaluateFont: function PartialEvaluator_preEvaluateFont(dict) {
       var baseDict = dict;
       var type = dict.get('Subtype');
-      (0, _util.assert)((0, _primitives.isName)(type), 'invalid font Subtype');
+      if (!(0, _primitives.isName)(type)) {
+        throw new _util.FormatError('invalid font Subtype');
+      }
       var composite = false;
       var uint8array;
       if (type.name === 'Type0') {
         var df = dict.get('DescendantFonts');
-        (0, _util.assert)(df, 'Descendant fonts are not specified');
+        if (!df) {
+          throw new _util.FormatError('Descendant fonts are not specified');
+        }
         dict = (0, _util.isArray)(df) ? this.xref.fetchIfRef(df[0]) : df;
         type = dict.get('Subtype');
-        (0, _util.assert)((0, _primitives.isName)(type), 'invalid font Subtype');
+        if (!(0, _primitives.isName)(type)) {
+          throw new _util.FormatError('invalid font Subtype');
+        }
         composite = true;
       }
       var descriptor = dict.get('FontDescriptor');
       if (descriptor) {
         var hash = new _murmurhash.MurmurHash3_64();
         var encoding = baseDict.getRaw('Encoding');
         if ((0, _primitives.isName)(encoding)) {
           hash.update(encoding.name);
@@ -18367,17 +18401,19 @@ var PartialEvaluator = function PartialE
       var properties;
       if (!descriptor) {
         if (type === 'Type3') {
           descriptor = new _primitives.Dict(null);
           descriptor.set('FontName', _primitives.Name.get(type));
           descriptor.set('FontBBox', dict.getArray('FontBBox'));
         } else {
           var baseFontName = dict.get('BaseFont');
-          (0, _util.assert)((0, _primitives.isName)(baseFontName), 'Base font is not specified');
+          if (!(0, _primitives.isName)(baseFontName)) {
+            throw new _util.FormatError('Base font is not specified');
+          }
           baseFontName = baseFontName.name.replace(/[,_]/g, '-');
           var metrics = this.getBaseFontMetrics(baseFontName);
           var fontNameWoStyle = baseFontName.split('-')[0];
           var flags = (this.isSerifFont(fontNameWoStyle) ? _fonts.FontFlags.Serif : 0) | (metrics.monospace ? _fonts.FontFlags.FixedPitch : 0) | ((0, _standard_fonts.getSymbolsFonts)()[fontNameWoStyle] ? _fonts.FontFlags.Symbolic : _fonts.FontFlags.Nonsymbolic);
           properties = {
             type,
             name: baseFontName,
             widths: metrics.widths,
@@ -18408,17 +18444,19 @@ var PartialEvaluator = function PartialE
         if (fontNameStr !== baseFontStr) {
           (0, _util.info)('The FontDescriptor\'s FontName is "' + fontNameStr + '" but should be the same as the Font\'s BaseFont "' + baseFontStr + '"');
           if (fontNameStr && baseFontStr && baseFontStr.indexOf(fontNameStr) === 0) {
             fontName = baseFont;
           }
         }
       }
       fontName = fontName || baseFont;
-      (0, _util.assert)((0, _primitives.isName)(fontName), 'invalid font name');
+      if (!(0, _primitives.isName)(fontName)) {
+        throw new _util.FormatError('invalid font name');
+      }
       var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
       if (fontFile) {
         if (fontFile.dict) {
           var subtype = fontFile.dict.get('Subtype');
           if (subtype) {
             subtype = subtype.name;
           }
           var length1 = fontFile.dict.get('Length1');
@@ -18493,17 +18531,19 @@ var TranslatedFont = function Translated
       if (this.sent) {
         return;
       }
       var fontData = this.font.exportData();
       handler.send('commonobj', [this.loadedName, 'Font', fontData]);
       this.sent = true;
     },
     loadType3Data(evaluator, resources, parentOperatorList, task) {
-      (0, _util.assert)(this.font.isType3Font);
+      if (!this.font.isType3Font) {
+        throw new Error('Must be a Type3 font.');
+      }
       if (this.type3Loaded) {
         return this.type3Loaded;
       }
       var type3Options = Object.create(evaluator.options);
       type3Options.ignoreErrors = false;
       var type3Evaluator = evaluator.clone(type3Options);
       var translatedFont = this.font;
       var loadCharProcsPromise = Promise.resolve();
@@ -19201,17 +19241,19 @@ var EvaluatorPreprocessor = function Eva
         if ((0, _primitives.isEOF)(obj)) {
           return false;
         }
         if (obj !== null) {
           if (args === null) {
             args = [];
           }
           args.push(obj);
-          (0, _util.assert)(args.length <= 33, 'Too many arguments');
+          if (args.length > 33) {
+            throw new _util.FormatError('Too many arguments');
+          }
         }
       }
     },
     preprocessCommand: function EvaluatorPreprocessor_preprocessCommand(fn, args) {
       switch (fn | 0) {
         case _util.OPS.save:
           this.stateManager.save();
           break;
@@ -21494,17 +21536,19 @@ var _crypto = __w_pdfjs_require__(12);
 
 var _colorspace = __w_pdfjs_require__(3);
 
 var Catalog = function CatalogClosure() {
   function Catalog(pdfManager, xref, pageFactory) {
     this.pdfManager = pdfManager;
     this.xref = xref;
     this.catDict = xref.getCatalogObj();
-    (0, _util.assert)((0, _primitives.isDict)(this.catDict), 'catalog object is not a dictionary');
+    if (!(0, _primitives.isDict)(this.catDict)) {
+      throw new _util.FormatError('catalog object is not a dictionary');
+    }
     this.fontCache = new _primitives.RefSetCache();
     this.builtInCMapCache = Object.create(null);
     this.pageKidsCountCache = new _primitives.RefSetCache();
     this.pageFactory = pageFactory;
     this.pagePromises = [];
   }
   Catalog.prototype = {
     get metadata() {
@@ -21528,17 +21572,19 @@ var Catalog = function CatalogClosure() 
             (0, _util.info)('Skipping invalid metadata.');
           }
         }
       }
       return (0, _util.shadow)(this, 'metadata', metadata);
     },
     get toplevelPagesDict() {
       var pagesObj = this.catDict.get('Pages');
-      (0, _util.assert)((0, _primitives.isDict)(pagesObj), 'invalid top-level pages dictionary');
+      if (!(0, _primitives.isDict)(pagesObj)) {
+        throw new _util.FormatError('invalid top-level pages dictionary');
+      }
       return (0, _util.shadow)(this, 'toplevelPagesDict', pagesObj);
     },
     get documentOutline() {
       var obj = null;
       try {
         obj = this.readDocumentOutline();
       } catch (ex) {
         if (ex instanceof _util.MissingDataException) {
@@ -21567,17 +21613,19 @@ var Catalog = function CatalogClosure() 
       var xref = this.xref,
           blackColor = new Uint8Array(3);
       while (queue.length > 0) {
         var i = queue.shift();
         var outlineDict = xref.fetchIfRef(i.obj);
         if (outlineDict === null) {
           continue;
         }
-        (0, _util.assert)(outlineDict.has('Title'), 'Invalid outline item');
+        if (!outlineDict.has('Title')) {
+          throw new _util.FormatError('Invalid outline item');
+        }
         var data = {
           url: null,
           dest: null
         };
         Catalog.parseDestDictionary({
           destDict: outlineDict,
           resultObj: data,
           docBaseUrl: this.pdfManager.docBaseUrl
@@ -21618,18 +21666,20 @@ var Catalog = function CatalogClosure() 
           });
           processed.put(obj);
         }
       }
       return root.items.length > 0 ? root.items : null;
     },
     get numPages() {
       var obj = this.toplevelPagesDict.get('Count');
-      (0, _util.assert)((0, _util.isInt)(obj), 'page count in top level pages object is not an integer');
-      return (0, _util.shadow)(this, 'num', obj);
+      if (!(0, _util.isInt)(obj)) {
+        throw new _util.FormatError('page count in top level pages object is not an integer');
+      }
+      return (0, _util.shadow)(this, 'numPages', obj);
     },
     get destinations() {
       function fetchDestination(dest) {
         return (0, _primitives.isDict)(dest) ? dest.get('D') : dest;
       }
       var xref = this.xref;
       var dests = {},
           nameTreeRef,
@@ -21706,27 +21756,37 @@ var Catalog = function CatalogClosure() 
       var prefix = '';
       var numberTree = new NumberTree(obj, this.xref);
       var nums = numberTree.getAll();
       var currentLabel = '',
           currentIndex = 1;
       for (var i = 0, ii = this.numPages; i < ii; i++) {
         if (i in nums) {
           var labelDict = nums[i];
-          (0, _util.assert)((0, _primitives.isDict)(labelDict), 'The PageLabel is not a dictionary.');
+          if (!(0, _primitives.isDict)(labelDict)) {
+            throw new _util.FormatError('The PageLabel is not a dictionary.');
+          }
           var type = labelDict.get('Type');
-          (0, _util.assert)(!type || (0, _primitives.isName)(type, 'PageLabel'), 'Invalid type in PageLabel dictionary.');
+          if (type && !(0, _primitives.isName)(type, 'PageLabel')) {
+            throw new _util.FormatError('Invalid type in PageLabel dictionary.');
+          }
           var s = labelDict.get('S');
-          (0, _util.assert)(!s || (0, _primitives.isName)(s), 'Invalid style in PageLabel dictionary.');
+          if (s && !(0, _primitives.isName)(s)) {
+            throw new _util.FormatError('Invalid style in PageLabel dictionary.');
+          }
           style = s ? s.name : null;
           var p = labelDict.get('P');
-          (0, _util.assert)(!p || (0, _util.isString)(p), 'Invalid prefix in PageLabel dictionary.');
+          if (p && !(0, _util.isString)(p)) {
+            throw new _util.FormatError('Invalid prefix in PageLabel dictionary.');
+          }
           prefix = p ? (0, _util.stringToPDFString)(p) : '';
           var st = labelDict.get('St');
-          (0, _util.assert)(!st || (0, _util.isInt)(st) && st >= 1, 'Invalid start in PageLabel dictionary.');
+          if (st && !((0, _util.isInt)(st) && st >= 1)) {
+            throw new _util.FormatError('Invalid start in PageLabel dictionary.');
+          }
           currentIndex = st || 1;
         }
         switch (style) {
           case 'D':
             currentLabel = currentIndex;
             break;
           case 'R':
           case 'r':
@@ -21742,17 +21802,19 @@ var Catalog = function CatalogClosure() 
             var character = String.fromCharCode(baseCharCode + letterIndex % LIMIT);
             var charBuf = [];
             for (var j = 0, jj = letterIndex / LIMIT | 0; j <= jj; j++) {
               charBuf.push(character);
             }
             currentLabel = charBuf.join('');
             break;
           default:
-            (0, _util.assert)(!style, 'Invalid style "' + style + '" in PageLabel dictionary.');
+            if (style) {
+              throw new _util.FormatError(`Invalid style "${style}" in PageLabel dictionary.`);
+            }
         }
         pageLabels[i] = prefix + currentLabel;
         currentLabel = '';
         currentIndex++;
       }
       return pageLabels;
     },
     get attachments() {
@@ -21869,67 +21931,79 @@ var Catalog = function CatalogClosure() 
                 }
                 return;
               }
               nodesToVisit.push(obj);
               next();
             }, capability.reject);
             return;
           }
-          (0, _util.assert)((0, _primitives.isDict)(currentNode), 'page dictionary kid reference points to wrong type of object');
+          if (!(0, _primitives.isDict)(currentNode)) {
+            capability.reject(new _util.FormatError('page dictionary kid reference points to wrong type of object'));
+            return;
+          }
           count = currentNode.get('Count');
           var objId = currentNode.objId;
           if (objId && !pageKidsCountCache.has(objId)) {
             pageKidsCountCache.put(objId, count);
           }
           if (currentPageIndex + count <= pageIndex) {
             currentPageIndex += count;
             continue;
           }
           var kids = currentNode.get('Kids');
-          (0, _util.assert)((0, _util.isArray)(kids), 'page dictionary kids object is not an array');
+          if (!(0, _util.isArray)(kids)) {
+            capability.reject(new _util.FormatError('page dictionary kids object is not an array'));
+            return;
+          }
           for (var last = kids.length - 1; last >= 0; last--) {
             nodesToVisit.push(kids[last]);
           }
         }
-        capability.reject('Page index ' + pageIndex + ' not found.');
+        capability.reject(new Error('Page index ' + pageIndex + ' not found.'));
       }
       next();
       return capability.promise;
     },
     getPageIndex: function Catalog_getPageIndex(pageRef) {
       var xref = this.xref;
       function pagesBeforeRef(kidRef) {
         var total = 0;
         var parentRef;
         return xref.fetchAsync(kidRef).then(function (node) {
           if ((0, _primitives.isRefsEqual)(kidRef, pageRef) && !(0, _primitives.isDict)(node, 'Page') && !((0, _primitives.isDict)(node) && !node.has('Type') && node.has('Contents'))) {
-            throw new Error('The reference does not point to a /Page Dict.');
+            throw new _util.FormatError('The reference does not point to a /Page Dict.');
           }
           if (!node) {
             return null;
           }
-          (0, _util.assert)((0, _primitives.isDict)(node), 'node must be a Dict.');
+          if (!(0, _primitives.isDict)(node)) {
+            throw new _util.FormatError('node must be a Dict.');
+          }
           parentRef = node.getRaw('Parent');
           return node.getAsync('Parent');
         }).then(function (parent) {
           if (!parent) {
             return null;
           }
-          (0, _util.assert)((0, _primitives.isDict)(parent), 'parent must be a Dict.');
+          if (!(0, _primitives.isDict)(parent)) {
+            throw new _util.FormatError('parent must be a Dict.');
+          }
           return parent.getAsync('Kids');
         }).then(function (kids) {
           if (!kids) {
             return null;
           }
           var kidPromises = [];
           var found = false;
           for (var i = 0; i < kids.length; i++) {
             var kid = kids[i];
-            (0, _util.assert)((0, _primitives.isRef)(kid), 'kid must be a Ref.');
+            if (!(0, _primitives.isRef)(kid)) {
+              throw new _util.FormatError('kid must be a Ref.');
+            }
             if (kid.num === kidRef.num) {
               found = true;
               break;
             }
             kidPromises.push(xref.fetchAsync(kid).then(function (kid) {
               if (kid.has('Count')) {
                 var count = kid.get('Count');
                 total += count;
@@ -22462,17 +22536,19 @@ var XRef = function XRefClosure() {
     },
     fetchIfRef: function XRef_fetchIfRef(obj, suppressEncryption) {
       if (!(0, _primitives.isRef)(obj)) {
         return obj;
       }
       return this.fetch(obj, suppressEncryption);
     },
     fetch: function XRef_fetch(ref, suppressEncryption) {
-      (0, _util.assert)((0, _primitives.isRef)(ref), 'ref object is not a reference');
+      if (!(0, _primitives.isRef)(ref)) {
+        throw new Error('ref object is not a reference');
+      }
       var num = ref.num;
       if (num in this.cache) {
         var cacheEntry = this.cache[num];
         if (cacheEntry instanceof _primitives.Dict && !cacheEntry.objId) {
           cacheEntry.objId = ref.toString();
         }
         return cacheEntry;
       }
@@ -22618,17 +22694,19 @@ var NameOrNumberTree = function NameOrNu
         var obj = xref.fetchIfRef(queue.shift());
         if (!(0, _primitives.isDict)(obj)) {
           continue;
         }
         if (obj.has('Kids')) {
           var kids = obj.get('Kids');
           for (i = 0, n = kids.length; i < n; i++) {
             var kid = kids[i];
-            (0, _util.assert)(!processed.has(kid), 'Duplicate entry in "' + this._type + '" tree.');
+            if (processed.has(kid)) {
+              throw new _util.FormatError(`Duplicate entry in "${this._type}" tree.`);
+            }
             queue.push(kid);
             processed.put(kid);
           }
           continue;
         }
         var entries = obj.get(this._type);
         if ((0, _util.isArray)(entries)) {
           for (i = 0, n = entries.length; i < n; i += 2) {
@@ -23901,17 +23979,19 @@ var WorkerMessageHandler = {
         }
         return pdfManagerCapability.promise;
       }
       var pdfStream;
       try {
         if (source.chunkedViewerLoading) {
           pdfStream = new PDFWorkerStream(source, handler);
         } else {
-          (0, _util.assert)(PDFNetworkStream, './network module is not loaded');
+          if (!PDFNetworkStream) {
+            throw new Error('./network module is not loaded');
+          }
           pdfStream = new PDFNetworkStream(data);
         }
       } catch (ex) {
         pdfManagerCapability.reject(ex);
         return pdfManagerCapability.promise;
       }
       var fullRequest = pdfStream.getFullReader();
       fullRequest.headersReady.then(function () {
@@ -28483,17 +28563,19 @@ var BinaryCMapReader = function BinaryCM
             case 1:
               useCMap = stream.readString();
               break;
           }
           continue;
         }
         var sequence = !!(b & 0x10);
         var dataSize = b & 15;
-        (0, _util.assert)(dataSize + 1 <= MAX_NUM_SIZE);
+        if (dataSize + 1 > MAX_NUM_SIZE) {
+          throw new Error('processBinaryCMap: Invalid dataSize.');
+        }
         var ucs2DataSize = 1;
         var subitemsCount = stream.readNumber();
         var i;
         switch (type) {
           case 0:
             stream.readHex(start, dataSize);
             stream.readHexNumber(end, dataSize);
             addHex(end, start, dataSize);
@@ -28824,29 +28906,33 @@ var CMapFactory = function CMapFactoryCl
     if (name === 'Identity-H') {
       return Promise.resolve(new IdentityCMap(false, 2));
     } else if (name === 'Identity-V') {
       return Promise.resolve(new IdentityCMap(true, 2));
     }
     if (BUILT_IN_CMAPS.indexOf(name) === -1) {
       return Promise.reject(new Error('Unknown CMap name: ' + name));
     }
-    (0, _util.assert)(fetchBuiltInCMap, 'Built-in CMap parameters are not provided.');
+    if (!fetchBuiltInCMap) {
+      return Promise.reject(new Error('Built-in CMap parameters are not provided.'));
+    }
     return fetchBuiltInCMap(name).then(function (data) {
       var cMapData = data.cMapData,
           compressionType = data.compressionType;
       var cMap = new CMap(true);
       if (compressionType === _util.CMapCompressionType.BINARY) {
         return new BinaryCMapReader().process(cMapData, cMap, function (useCMap) {
           return extendCMap(cMap, fetchBuiltInCMap, useCMap);
         });
       }
-      (0, _util.assert)(compressionType === _util.CMapCompressionType.NONE, 'TODO: Only BINARY/NONE CMap compression is currently supported.');
-      var lexer = new _parser.Lexer(new _stream.Stream(cMapData));
-      return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
+      if (compressionType === _util.CMapCompressionType.NONE) {
+        var lexer = new _parser.Lexer(new _stream.Stream(cMapData));
+        return parseCMap(cMap, lexer, fetchBuiltInCMap, null);
+      }
+      return Promise.reject(new Error('TODO: Only BINARY/NONE CMap compression is currently supported.'));
     });
   }
   return {
     create(params) {
       var encoding = params.encoding;
       var fetchBuiltInCMap = params.fetchBuiltInCMap;
       var useCMap = params.useCMap;
       if ((0, _primitives.isName)(encoding)) {
@@ -28876,22 +28962,22 @@ exports.CMapFactory = CMapFactory;
 "use strict";
 
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.PDFDocument = exports.Page = undefined;
 
-var _util = __w_pdfjs_require__(0);
-
 var _obj = __w_pdfjs_require__(15);
 
 var _primitives = __w_pdfjs_require__(1);
 
+var _util = __w_pdfjs_require__(0);
+
 var _stream = __w_pdfjs_require__(2);
 
 var _evaluator = __w_pdfjs_require__(13);
 
 var _annotation = __w_pdfjs_require__(19);
 
 var _crypto = __w_pdfjs_require__(12);
 
@@ -29141,17 +29227,19 @@ var PDFDocument = function PDFDocumentCl
     var stream;
     if ((0, _primitives.isStream)(arg)) {
       stream = arg;
     } else if ((0, _util.isArrayBuffer)(arg)) {
       stream = new _stream.Stream(arg);
     } else {
       throw new Error('PDFDocument: Unknown argument type');
     }
-    (0, _util.assert)(stream.length > 0, 'stream must have data');
+    if (stream.length <= 0) {
+      throw new Error('PDFDocument: stream must have data');
+    }
     this.pdfManager = pdfManager;
     this.stream = stream;
     this.xref = new _obj.XRef(stream, pdfManager);
   }
   function find(stream, needle, limit, backwards) {
     var pos = stream.pos;
     var end = stream.end;
     var strBuf = [];
@@ -31785,17 +31873,19 @@ var Font = function FontClosure() {
           charCode;
       function hasGlyph(glyphId) {
         return !missingGlyphs[glyphId];
       }
       if (properties.composite) {
         var cidToGidMap = properties.cidToGidMap || [];
         var isCidToGidMapEmpty = cidToGidMap.length === 0;
         properties.cMap.forEach(function (charCode, cid) {
-          (0, _util.assert)(cid <= 0xffff, 'Max size of CID is 65,535');
+          if (cid > 0xffff) {
+            throw new _util.FormatError('Max size of CID is 65,535');
+          }
           var glyphId = -1;
           if (isCidToGidMapEmpty) {
             glyphId = cid;
           } else if (cidToGidMap[cid] !== undefined) {
             glyphId = cidToGidMap[cid];
           }
           if (glyphId >= 0 && glyphId < numGlyphs && hasGlyph(glyphId)) {
             charCodeToGlyphId[charCode] = glyphId;
@@ -34892,26 +34982,27 @@ var JpegImage = function JpegImageClosur
         for (i = 0; i < dataLength;) {
           for (j = 0, k = 0; j < numComponents; j++, i++, k += 2) {
             data[i] = (data[i] * transform[k] >> 8) + transform[k + 1];
           }
         }
       }
       return data;
     },
-    _isColorConversionNeeded: function isColorConversionNeeded() {
-      if (this.adobe && this.adobe.transformCode) {
-        return true;
-      } else if (this.numComponents === 3) {
-        if (!this.adobe && this.colorTransform === 0) {
+    _isColorConversionNeeded() {
+      if (this.adobe) {
+        return !!this.adobe.transformCode;
+      }
+      if (this.numComponents === 3) {
+        if (this.colorTransform === 0) {
           return false;
         }
         return true;
       }
-      if (!this.adobe && this.colorTransform === 1) {
+      if (this.colorTransform === 1) {
         return true;
       }
       return false;
     },
     _convertYccToRgb: function convertYccToRgb(data) {
       var Y, Cb, Cr;
       for (var i = 0, length = data.length; i < length; i += 3) {
         Y = data[i];
@@ -38336,17 +38427,19 @@ Shadings.Mesh = function MeshClosure() {
     var operators = [];
     var ps = [];
     var verticesLeft = 0;
     while (reader.hasData) {
       var f = reader.readFlag();
       var coord = reader.readCoordinate();
       var color = reader.readComponents();
       if (verticesLeft === 0) {
-        (0, _util.assert)(0 <= f && f <= 2, 'Unknown type4 flag');
+        if (!(0 <= f && f <= 2)) {
+          throw new _util.FormatError('Unknown type4 flag');
+        }
         switch (f) {
           case 0:
             verticesLeft = 3;
             break;
           case 1:
             ps.push(ps[ps.length - 2], ps[ps.length - 1]);
             verticesLeft = 1;
             break;
@@ -38483,17 +38576,19 @@ Shadings.Mesh = function MeshClosure() {
   }
   function decodeType6Shading(mesh, reader) {
     var coords = mesh.coords;
     var colors = mesh.colors;
     var ps = new Int32Array(16);
     var cs = new Int32Array(4);
     while (reader.hasData) {
       var f = reader.readFlag();
-      (0, _util.assert)(0 <= f && f <= 3, 'Unknown type6 flag');
+      if (!(0 <= f && f <= 3)) {
+        throw new _util.FormatError('Unknown type6 flag');
+      }
       var i, ii;
       var pi = coords.length;
       for (i = 0, ii = f !== 0 ? 8 : 12; i < ii; i++) {
         coords.push(reader.readCoordinate());
       }
       var ci = colors.length;
       for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
         colors.push(reader.readComponents());
@@ -38599,17 +38694,19 @@ Shadings.Mesh = function MeshClosure() {
   }
   function decodeType7Shading(mesh, reader) {
     var coords = mesh.coords;
     var colors = mesh.colors;
     var ps = new Int32Array(16);
     var cs = new Int32Array(4);
     while (reader.hasData) {
       var f = reader.readFlag();
-      (0, _util.assert)(0 <= f && f <= 3, 'Unknown type7 flag');
+      if (!(0 <= f && f <= 3)) {
+        throw new _util.FormatError('Unknown type7 flag');
+      }
       var i, ii;
       var pi = coords.length;
       for (i = 0, ii = f !== 0 ? 12 : 16; i < ii; i++) {
         coords.push(reader.readCoordinate());
       }
       var ci = colors.length;
       for (i = 0, ii = f !== 0 ? 2 : 4; i < ii; i++) {
         colors.push(reader.readComponents());
@@ -38762,17 +38859,19 @@ Shadings.Mesh = function MeshClosure() {
           cs = figure.colors;
       for (j = 0, jj = ps.length; j < jj; j++) {
         ps[j] *= 2;
         cs[j] *= 3;
       }
     }
   }
   function Mesh(stream, matrix, xref, res) {
-    (0, _util.assert)((0, _primitives.isStream)(stream), 'Mesh data is not a stream');
+    if (!(0, _primitives.isStream)(stream)) {
+      throw new _util.FormatError('Mesh data is not a stream');
+    }
     var dict = stream.dict;
     this.matrix = matrix;
     this.shadingType = dict.get('ShadingType');
     this.type = 'Pattern';
     this.bbox = dict.getArray('BBox');
     var cs = dict.get('ColorSpace', 'CS');
     cs = _colorspace.ColorSpace.parse(cs, xref, res);
     this.cs = cs;
@@ -38794,17 +38893,19 @@ Shadings.Mesh = function MeshClosure() {
     var reader = new MeshStreamReader(stream, decodeContext);
     var patchMesh = false;
     switch (this.shadingType) {
       case ShadingType.FREE_FORM_MESH:
         decodeType4Shading(this, reader);
         break;
       case ShadingType.LATTICE_FORM_MESH:
         var verticesPerRow = dict.get('VerticesPerRow') | 0;
-        (0, _util.assert)(verticesPerRow >= 2, 'Invalid VerticesPerRow');
+        if (verticesPerRow < 2) {
+          throw new _util.FormatError('Invalid VerticesPerRow');
+        }
         decodeType5Shading(this, reader, verticesPerRow);
         break;
       case ShadingType.COONS_PATCH_MESH:
         decodeType6Shading(this, reader);
         patchMesh = true;
         break;
       case ShadingType.TENSOR_PATCH_MESH:
         decodeType7Shading(this, reader);
@@ -39799,18 +39900,18 @@ exports.Type1Parser = Type1Parser;
 
 /***/ }),
 /* 35 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
 "use strict";
 
 
-var pdfjsVersion = '1.8.557';
-var pdfjsBuild = '4a74cc41';
+var pdfjsVersion = '1.8.564';
+var pdfjsBuild = 'e7cddcce';
 var pdfjsCoreWorker = __w_pdfjs_require__(17);
 ;
 exports.WorkerMessageHandler = pdfjsCoreWorker.WorkerMessageHandler;
 
 /***/ }),
 /* 36 */
 /***/ (function(module, exports, __w_pdfjs_require__) {
 
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -236,103 +236,124 @@ def unique_list(l):
 # Windows.
 # The `pattern` argument is a string starting with HKEY_ and giving the full
 # "path" of the registry key to get the value for, with backslash separators.
 # The string can contains wildcards ('*').
 # The result of this functions is an enumerator yielding tuples for each
 # match. Each of these tuples contains the key name matching wildcards
 # followed by the value.
 #
+# The `get_32_and_64_bit` argument is a boolean, if True then it will return the
+# values from the 32-bit and 64-bit registry views. This defaults to False,
+# which will return the view depending on the bitness of python.
+#
 # Examples:
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\Installed Roots\KitsRoot*')
 #   yields e.g.:
 #     ('KitsRoot81', r'C:\Program Files (x86)\Windows Kits\8.1\')
 #     ('KitsRoot10', r'C:\Program Files (x86)\Windows Kits\10\')
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\Installed Roots\KitsRoot8.1')
 #   yields e.g.:
 #     (r'C:\Program Files (x86)\Windows Kits\8.1\',)
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
+#                       r'Windows Kits\Installed Roots\KitsRoot8.1',
+#                       get_32_and_64_bit=True)
+#   yields e.g.:
+#     (r'C:\Program Files (x86)\Windows Kits\8.1\',)
+#     (r'C:\Program Files\Windows Kits\8.1\',)
+#
+#   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'Windows Kits\*\KitsRoot*')
 #   yields e.g.:
 #     ('Installed Roots', 'KitsRoot81',
 #      r'C:\Program Files (x86)\Windows Kits\8.1\')
 #     ('Installed Roots', 'KitsRoot10',
 #      r'C:\Program Files (x86)\Windows Kits\10\')
 #
 #   get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\'
 #                       r'VisualStudio\VC\*\x86\*\Compiler')
 #   yields e.g.:
 #     ('19.0', 'arm', r'C:\...\amd64_arm\cl.exe')
 #     ('19.0', 'x64', r'C:\...\amd64\cl.exe')
 #     ('19.0', 'x86', r'C:\...\amd64_x86\cl.exe')
 @imports(_import='_winreg', _as='winreg')
 @imports(_from='__builtin__', _import='WindowsError')
 @imports(_from='fnmatch', _import='fnmatch')
-def get_registry_values(pattern):
+def get_registry_values(pattern, get_32_and_64_bit=False):
     def enum_helper(func, key):
         i = 0
         while True:
             try:
                 yield func(key, i)
             except WindowsError:
                 break
             i += 1
 
-    def get_keys(key, pattern):
+    def get_keys(key, pattern, access_mask):
         try:
-            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]), 0, access_mask)
         except WindowsError:
             return
         for k in enum_helper(winreg.EnumKey, s):
             if fnmatch(k, pattern[-1]):
                 try:
-                    yield k, winreg.OpenKey(s, k)
+                    yield k, winreg.OpenKey(s, k, 0, access_mask)
                 except WindowsError:
                     pass
 
-    def get_values(key, pattern):
+    def get_values(key, pattern, access_mask):
         try:
-            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]))
+            s = winreg.OpenKey(key, '\\'.join(pattern[:-1]), 0, access_mask)
         except WindowsError:
             return
         for k, v, t in enum_helper(winreg.EnumValue, s):
             if fnmatch(k, pattern[-1]):
                 yield k, v
 
     def split_pattern(pattern):
         subpattern = []
         for p in pattern:
             subpattern.append(p)
             if '*' in p:
                 yield subpattern
                 subpattern = []
         if subpattern:
             yield subpattern
 
+    def get_all_values(keys, pattern, access_mask):
+        for i, p in enumerate(pattern):
+            next_keys = []
+            for base_key in keys:
+                matches = base_key[:-1]
+                base_key = base_key[-1]
+                if i == len(pattern) - 1:
+                    want_name = '*' in p[-1]
+                    for name, value in get_values(base_key, p, access_mask):
+                        yield matches + ((name, value) if want_name else (value,))
+                else:
+                    for name, k in get_keys(base_key, p, access_mask):
+                        next_keys.append(matches + (name, k))
+            keys = next_keys
+
     pattern = pattern.split('\\')
     assert pattern[0].startswith('HKEY_')
     keys = [(getattr(winreg, pattern[0]),)]
     pattern = list(split_pattern(pattern[1:]))
-    for i, p in enumerate(pattern):
-        next_keys = []
-        for base_key in keys:
-            matches = base_key[:-1]
-            base_key = base_key[-1]
-            if i == len(pattern) - 1:
-                want_name = '*' in p[-1]
-                for name, value in get_values(base_key, p):
-                    yield matches + ((name, value) if want_name else (value,))
-            else:
-                for name, k in get_keys(base_key, p):
-                    next_keys.append(matches + (name, k))
-        keys = next_keys
+    if get_32_and_64_bit:
+        for match in get_all_values(keys, pattern, winreg.KEY_READ | winreg.KEY_WOW64_32KEY):
+            yield match
+        for match in get_all_values(keys, pattern, winreg.KEY_READ | winreg.KEY_WOW64_64KEY):
+            yield match
+    else:
+        for match in get_all_values(keys, pattern, winreg.KEY_READ):
+            yield match
 
 
 @imports(_from='mozbuild.configure.util', _import='Version', _as='_Version')
 def Version(v):
     'A version number that can be compared usefully.'
     return _Version(v)
 
 # Denotes a deprecated option. Combines option() and @depends:
--- a/build/moz.configure/windows.configure
+++ b/build/moz.configure/windows.configure
@@ -28,19 +28,19 @@ option(env='WINDOWSSDKDIR', nargs=1,
 
 @depends('WINDOWSSDKDIR', host)
 def windows_sdk_dir(value, host):
     if value:
         return value
     if host.kernel != 'WINNT':
         return ()
 
-    return tuple(x[1] for x in get_registry_values(
+    return set(x[1] for x in get_registry_values(
         r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots'
-        r'\KitsRoot*'))
+        r'\KitsRoot*', get_32_and_64_bit=True))
 
 # The Windows SDK 8.1 and 10 have different layouts. The former has
 # $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir.
 # The vcvars* scripts don't actually care about the version, they just take
 # the last alphanumerically.
 # The $SDK/lib directories always have version subdirectories, but while the
 # versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK
 # 8.1.
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -753,20 +753,16 @@ a.learn-more-link.webconsole-learn-more-
 .devtools-toolbar.webconsole-filterbar-secondary {
   height: initial;
 }
 
 .webconsole-filterbar-primary .devtools-plaininput {
   flex: 1 1 100%;
 }
 
-.webconsole-output-wrapper .message {
-  width: 100vw;
-}
-
 .webconsole-output-wrapper .message-flex-body > .message-body {
   display: flex;
   flex-wrap: wrap;
   max-width: 100%;
 }
 
 .webconsole-output-wrapper .message-body > * {
   flex-shrink: 0;
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_init.js
@@ -10,26 +10,27 @@ const TEST_URI = "http://example.com/bro
 add_task(function* () {
   let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
   let hud = toolbox.getCurrentPanel().hud;
   let {ui} = hud;
 
   ok(ui.jsterm, "jsterm exists");
   ok(ui.newConsoleOutput, "newConsoleOutput exists");
 
-  // @TODO: fix proptype errors
   let receievedMessages = waitForMessages({
     hud,
-    messages: [{
-      text: "0",
-    }, {
-      text: "1",
-    }, {
-      text: "2",
-    }],
+    messages: [
+      { text: "19" },
+    ]
   });
 
   yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function () {
-    content.wrappedJSObject.doLogs(3);
+    content.wrappedJSObject.doLogs(20);
   });
 
   yield receievedMessages;
+
+  const outputContainer = ui.outputNode.querySelector(".webconsole-output");
+  is(outputContainer.querySelectorAll(".message.console-api").length, 20,
+    "Correct number of messages appear");
+  is(outputContainer.scrollWidth, outputContainer.clientWidth,
+    "No horizontal overflow");
 });
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -709,16 +709,18 @@ ConvertLoadTypeToNavigationType(uint32_t
     case LOAD_REPLACE_BYPASS_CACHE:
       result = nsDOMNavigationTiming::TYPE_NAVIGATE;
       break;
     case LOAD_HISTORY:
       result = nsDOMNavigationTiming::TYPE_BACK_FORWARD;
       break;
     case LOAD_RELOAD_NORMAL:
     case LOAD_RELOAD_CHARSET_CHANGE:
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
     case LOAD_RELOAD_BYPASS_CACHE:
     case LOAD_RELOAD_BYPASS_PROXY:
     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
     case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
       result = nsDOMNavigationTiming::TYPE_RELOAD;
       break;
     case LOAD_STOP_CONTENT_AND_REPLACE:
     case LOAD_REFRESH:
@@ -1136,16 +1138,22 @@ nsDocShell::ConvertDocShellLoadInfoToLoa
       loadType = LOAD_NORMAL_ALLOW_MIXED_CONTENT;
       break;
     case nsIDocShellLoadInfo::loadReloadNormal:
       loadType = LOAD_RELOAD_NORMAL;
       break;
     case nsIDocShellLoadInfo::loadReloadCharsetChange:
       loadType = LOAD_RELOAD_CHARSET_CHANGE;
       break;
+    case nsIDocShellLoadInfo::loadReloadCharsetChangeBypassCache:
+      loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE;
+      break;
+    case nsIDocShellLoadInfo::loadReloadCharsetChangeBypassProxyAndCache:
+      break;
+      loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE;
     case nsIDocShellLoadInfo::loadReloadBypassCache:
       loadType = LOAD_RELOAD_BYPASS_CACHE;
       break;
     case nsIDocShellLoadInfo::loadReloadBypassProxy:
       loadType = LOAD_RELOAD_BYPASS_PROXY;
       break;
     case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
       loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
@@ -1211,16 +1219,22 @@ nsDocShell::ConvertLoadTypeToDocShellLoa
       docShellLoadType = nsIDocShellLoadInfo::loadHistory;
       break;
     case LOAD_RELOAD_NORMAL:
       docShellLoadType = nsIDocShellLoadInfo::loadReloadNormal;
       break;
     case LOAD_RELOAD_CHARSET_CHANGE:
       docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
       break;
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
+      docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChangeBypassCache;
+      break;
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
+      docShellLoadType = nsIDocShellLoadInfo::loadReloadCharsetChangeBypassProxyAndCache;
+      break;
     case LOAD_RELOAD_BYPASS_CACHE:
       docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassCache;
       break;
     case LOAD_RELOAD_BYPASS_PROXY:
       docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
       break;
     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
       docShellLoadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
@@ -1392,17 +1406,19 @@ nsDocShell::LoadURI(nsIURI* aURI,
         } else if (parentLoadType == LOAD_REFRESH) {
           // Clear shEntry. For refresh loads, we have to load
           // what comes thro' the pipe, not what's in history.
           shEntry = nullptr;
         } else if ((parentLoadType == LOAD_BYPASS_HISTORY) ||
                    (shEntry &&
                     ((parentLoadType & LOAD_CMD_HISTORY) ||
                      (parentLoadType == LOAD_RELOAD_NORMAL) ||
-                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE)))) {
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE) ||
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE) ||
+                     (parentLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)))) {
           // If the parent url, bypassed history or was loaded from
           // history, pass on the parent's loadType to the new child
           // frame too, so that the child frame will also
           // avoid getting into history.
           loadType = parentLoadType;
         } else if (parentLoadType == LOAD_ERROR_PAGE) {
           // If the parent document is an error page, we don't
           // want to update global/session history. However,
@@ -7320,17 +7336,19 @@ nsDocShell::Embed(nsIContentViewer* aCon
 
   // If we are loading a wyciwyg url from history, change the base URI for
   // the document to the original http url that created the document.write().
   // This makes sure that all relative urls in a document.written page loaded
   // via history work properly.
   if (mCurrentURI &&
       (mLoadType & LOAD_CMD_HISTORY ||
        mLoadType == LOAD_RELOAD_NORMAL ||
-       mLoadType == LOAD_RELOAD_CHARSET_CHANGE)) {
+       mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
+       mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
+       mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE)) {
     bool isWyciwyg = false;
     // Check if the url is wyciwyg
     rv = mCurrentURI->SchemeIs("wyciwyg", &isWyciwyg);
     if (isWyciwyg && NS_SUCCEEDED(rv)) {
       SetBaseUrlForWyciwyg(aContentViewer);
     }
   }
   // XXX What if SetupNewViewer fails?
@@ -11315,17 +11333,19 @@ nsDocShell::DoURILoad(nsIURI* aURI,
     /* If there is no postdata, set the cache key on the channel, and
      * do not set the LOAD_ONLY_FROM_CACHE flag, so that the channel
      * will be free to get it from net if it is not found in cache.
      * New cache may use it creatively on CGI pages with GET
      * method and even on those that say "no-cache"
      */
     if (mLoadType == LOAD_HISTORY ||
         mLoadType == LOAD_RELOAD_NORMAL ||
-        mLoadType == LOAD_RELOAD_CHARSET_CHANGE) {
+        mLoadType == LOAD_RELOAD_CHARSET_CHANGE ||
+        mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE ||
+        mLoadType == LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE) {
       if (cacheChannel && cacheKey) {
         cacheChannel->SetCacheKey(cacheKey);
       }
     }
   }
 
   if (httpChannel) {
     if (aHeadersData) {
@@ -11494,16 +11514,22 @@ nsDocShell::DoChannelLoad(nsIChannel* aC
       }
 
       if (!uriModified) {
         loadFlags |= nsIRequest::VALIDATE_NEVER;
       }
       break;
     }
 
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
+      loadFlags |= nsIRequest::LOAD_BYPASS_CACHE |
+        nsIRequest::LOAD_FRESH_CONNECTION;
+      MOZ_FALLTHROUGH;
+
     case LOAD_RELOAD_CHARSET_CHANGE: {
       // Use SetAllowStaleCacheContent (not LOAD_FROM_CACHE flag) since we only want
       // to force cache load for this channel, not the whole loadGroup.
       nsCOMPtr<nsICacheInfoChannel> cachingChannel = do_QueryInterface(aChannel);
       if (cachingChannel) {
         cachingChannel->SetAllowStaleCacheContent(true);
       }
       break;
@@ -14402,17 +14428,27 @@ nsDocShell::ReloadDocument(const char* a
     int32_t hint;
     cv->GetHintCharacterSetSource(&hint);
     if (aSource > hint) {
       nsCString charset(aCharset);
       cv->SetHintCharacterSet(charset);
       cv->SetHintCharacterSetSource(aSource);
       if (eCharsetReloadRequested != mCharsetReloadState) {
         mCharsetReloadState = eCharsetReloadRequested;
-        return Reload(LOAD_FLAGS_CHARSET_CHANGE);
+        switch (mLoadType) {
+        case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+          return Reload(LOAD_FLAGS_CHARSET_CHANGE |
+                        LOAD_FLAGS_BYPASS_CACHE |
+                        LOAD_FLAGS_BYPASS_PROXY);
+        case LOAD_RELOAD_BYPASS_CACHE:
+          return Reload(LOAD_FLAGS_CHARSET_CHANGE |
+                        LOAD_FLAGS_BYPASS_CACHE);
+        default:
+          return Reload(LOAD_FLAGS_CHARSET_CHANGE);
+        }
       }
     }
   }
   // return failure if this request is not accepted due to mCharsetReloadState
   return NS_ERROR_DOCSHELL_REQUEST_REJECTED;
 }
 
 NS_IMETHODIMP
--- a/docshell/base/nsDocShellLoadTypes.h
+++ b/docshell/base/nsDocShellLoadTypes.h
@@ -50,16 +50,18 @@ enum LoadType
   LOAD_RELOAD_NORMAL = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_NONE),
   LOAD_RELOAD_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
   LOAD_RELOAD_BYPASS_PROXY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
   LOAD_RELOAD_ALLOW_MIXED_CONTENT = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
   LOAD_RELOAD_BYPASS_PROXY_AND_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
   LOAD_LINK = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_IS_LINK),
   LOAD_REFRESH = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH),
   LOAD_RELOAD_CHARSET_CHANGE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE),
+  LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE | nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY),
+  LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_RELOAD, nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
   LOAD_BYPASS_HISTORY = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_BYPASS_HISTORY),
   LOAD_STOP_CONTENT = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_STOP_CONTENT),
   LOAD_STOP_CONTENT_AND_REPLACE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_STOP_CONTENT | nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY),
   LOAD_PUSHSTATE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_PUSHSTATE, nsIWebNavigation::LOAD_FLAGS_NONE),
   LOAD_REPLACE_BYPASS_CACHE = MAKE_LOAD_TYPE(nsIDocShell::LOAD_CMD_NORMAL, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY | nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE),
   /**
    * Load type for an error page. These loads are never triggered by users of
    * Docshell. Instead, Docshell triggers the load itself when a
@@ -85,16 +87,18 @@ IsValidLoadType(uint32_t aLoadType)
     case LOAD_RELOAD_NORMAL:
     case LOAD_RELOAD_BYPASS_CACHE:
     case LOAD_RELOAD_BYPASS_PROXY:
     case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
     case LOAD_RELOAD_ALLOW_MIXED_CONTENT:
     case LOAD_LINK:
     case LOAD_REFRESH:
     case LOAD_RELOAD_CHARSET_CHANGE:
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE:
+    case LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE:
     case LOAD_BYPASS_HISTORY:
     case LOAD_STOP_CONTENT:
     case LOAD_STOP_CONTENT_AND_REPLACE:
     case LOAD_PUSHSTATE:
     case LOAD_REPLACE_BYPASS_CACHE:
     case LOAD_ERROR_PAGE:
       return true;
   }
--- a/docshell/base/nsIDocShellLoadInfo.idl
+++ b/docshell/base/nsIDocShellLoadInfo.idl
@@ -82,16 +82,18 @@ interface nsIDocShellLoadInfo : nsISuppo
     const long loadNormalExternal = 13;
     const long loadNormalBypassCache = 14;
     const long loadNormalBypassProxy = 15;
     const long loadNormalBypassProxyAndCache = 16;
     const long loadPushState = 17;                 // history.pushState or replaceState
     const long loadReplaceBypassCache = 18;
     const long loadReloadMixedContent = 19;
     const long loadNormalAllowMixedContent = 20;
+    const long loadReloadCharsetChangeBypassCache = 21;
+    const long loadReloadCharsetChangeBypassProxyAndCache = 22;
 
     /** Contains a load type as specified by the load* constants */
     attribute nsDocShellInfoLoadType loadType;
 
     /** SHEntry for this page */
     attribute nsISHEntry SHEntry;
 
     /** Target for load, like _content, _blank etc. */
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -488,17 +488,17 @@ Element::GetBindingURL(nsIDocument *aDoc
   // If we have a frame the frame has already loaded the binding.  And
   // otherwise, don't do anything else here unless we're dealing with
   // XUL or an HTML element that may have a plugin-related overlay
   // (i.e. object, embed, or applet).
   bool isXULorPluginElement = (IsXULElement() ||
                                IsHTMLElement(nsGkAtoms::object) ||
                                IsHTMLElement(nsGkAtoms::embed) ||
                                IsHTMLElement(nsGkAtoms::applet));
-  nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
+  nsIPresShell* shell = aDocument->GetShell();
   if (!shell || GetPrimaryFrame() || !isXULorPluginElement) {
     *aResult = nullptr;
     return true;
   }
 
   // Get the computed -moz-binding directly from the style context
   RefPtr<nsStyleContext> sc =
     nsComputedDOMStyle::GetStyleContextNoFlush(this, nullptr, shell);
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -988,17 +988,17 @@ APZCTreeManager::ReceiveInputEvent(Input
       if (startsDrag) {
         // If this is the start of a drag we need to unambiguously know if it's
         // going to land on a scrollbar or not. We can't apply an untransform
         // here without knowing that, so we need to ensure the untransform is
         // a no-op.
         FlushRepaintsToClearScreenToGeckoTransform();
       }
 
-      HitTestingTreeNode* hitScrollbarNode = nullptr;
+      RefPtr<HitTestingTreeNode> hitScrollbarNode = nullptr;
       RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(mouseInput.mOrigin,
             &hitResult, &hitScrollbarNode);
       bool hitScrollbar = hitScrollbarNode;
 
       // When the mouse is outside the window we still want to handle dragging
       // but we won't find an APZC. Fallback to root APZC then.
       { // scope lock
         MutexAutoLock lock(mTreeLock);
@@ -2028,28 +2028,32 @@ APZCTreeManager::GetTargetNode(const Scr
       }
   );
   return target.forget();
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint,
                                HitTestResult* aOutHitResult,
-                               HitTestingTreeNode** aOutHitScrollbar)
+                               RefPtr<HitTestingTreeNode>* aOutScrollbarNode)
 {
   MutexAutoLock lock(mTreeLock);
   HitTestResult hitResult = HitNothing;
+  HitTestingTreeNode* scrollbarNode = nullptr;
   ParentLayerPoint point = ViewAs<ParentLayerPixel>(aPoint,
     PixelCastJustification::ScreenIsParentLayerForRoot);
   RefPtr<AsyncPanZoomController> target = GetAPZCAtPoint(mRootNode, point,
-      &hitResult, aOutHitScrollbar);
+      &hitResult, &scrollbarNode);
 
   if (aOutHitResult) {
     *aOutHitResult = hitResult;
   }
+  if (aOutScrollbarNode) {
+    *aOutScrollbarNode = scrollbarNode;
+  }
   return target.forget();
 }
 
 RefPtr<const OverscrollHandoffChain>
 APZCTreeManager::BuildOverscrollHandoffChain(const RefPtr<AsyncPanZoomController>& aInitialTarget)
 {
   // Scroll grabbing is a mechanism that allows content to specify that
   // the initial target of a pan should be not the innermost scrollable
@@ -2218,19 +2222,17 @@ APZCTreeManager::GetAPZCAtPoint(HitTesti
         return TraversalFlag::Continue;
       }
   );
 
   if (*aOutHitResult != HitNothing) {
       MOZ_ASSERT(resultNode);
       for (HitTestingTreeNode* n = resultNode; n; n = n->GetParent()) {
         if (n->IsScrollbarNode()) {
-          if (aOutScrollbarNode) {
-            *aOutScrollbarNode = n;
-          }
+          *aOutScrollbarNode = n;
           // If we hit a scrollbar, target the APZC for the content scrolled
           // by the scrollbar. (The scrollbar itself doesn't scroll with the
           // scrolled content, so it doesn't carry the scrolled content's
           // scroll metadata).
           ScrollableLayerGuid guid(n->GetLayersId(), 0, n->GetScrollTargetId());
           if (RefPtr<HitTestingTreeNode> scrollTarget = GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
             MOZ_ASSERT(scrollTarget->GetApzc());
             return scrollTarget->GetApzc();
@@ -2542,16 +2544,17 @@ APZCTreeManager::CommonAncestor(AsyncPan
     aApzc2 = aApzc2->GetParent();
   }
   return ancestor.forget();
 }
 
 LayerToParentLayerMatrix4x4
 APZCTreeManager::ComputeTransformForNode(const HitTestingTreeNode* aNode) const
 {
+  mTreeLock.AssertCurrentThreadOwns();
   if (AsyncPanZoomController* apzc = aNode->GetApzc()) {
     // If the node represents scrollable content, apply the async transform
     // from its APZC.
     return aNode->GetTransform() *
         CompleteAsyncTransform(
           apzc->GetCurrentAsyncTransformWithOverscroll(AsyncPanZoomController::eForHitTesting));
   } else if (aNode->IsScrollThumbNode()) {
     // If the node represents a scrollbar thumb, compute and apply the
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -475,17 +475,17 @@ public:
      lock the tree of APZCs while they find the right one, and then return an addref'd
      pointer to it. This allows caller code to just use the target APZC without worrying
      about it going away. These are public for testing code and generally should not be
      used by other production code.
   */
   RefPtr<HitTestingTreeNode> GetRootNode() const;
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint,
                                                          HitTestResult* aOutHitResult,
-                                                         HitTestingTreeNode** aOutScrollbarNode = nullptr);
+                                                         RefPtr<HitTestingTreeNode>* aOutScrollbarNode = nullptr);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const uint64_t& aLayersId,
                                                          const FrameMetrics::ViewID& aScrollId);
   ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(const AsyncPanZoomController *aApzc) const;
   ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(const AsyncPanZoomController *aApzc) const;
 
   /**
    * Process touch velocity.
    * Sometimes the touch move event will have a velocity even though no scrolling
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -400,16 +400,24 @@ Decoder::PostSize(int32_t aWidth,
 {
   // Validate.
   MOZ_ASSERT(aWidth >= 0, "Width can't be negative!");
   MOZ_ASSERT(aHeight >= 0, "Height can't be negative!");
 
   // Set our intrinsic size.
   mImageMetadata.SetSize(aWidth, aHeight, aOrientation);
 
+  // Verify it is the expected size, if given. Note that this is only used by
+  // the ICO decoder for embedded image types, so only its subdecoders are
+  // required to handle failures in PostSize.
+  if (!IsExpectedSize()) {
+    PostError();
+    return;
+  }
+
   // Set our output size if it's not already set.
   if (!mOutputSize) {
     mOutputSize = Some(IntSize(aWidth, aHeight));
   }
 
   MOZ_ASSERT(mOutputSize->width <= aWidth && mOutputSize->height <= aHeight,
              "Output size will result in upscaling");
 
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -211,16 +211,33 @@ public:
 
   /**
    * @return either the size passed to SetOutputSize() or Nothing(), indicating
    * that SetOutputSize() was not explicitly called.
    */
   Maybe<gfx::IntSize> ExplicitOutputSize() const;
 
   /**
+   * Sets the expected image size of this decoder. Decoding will fail if this
+   * does not match.
+   */
+  void SetExpectedSize(const gfx::IntSize& aSize)
+  {
+    mExpectedSize.emplace(aSize);
+  }
+
+  /**
+   * Is the image size what was expected, if specified?
+   */
+  bool IsExpectedSize() const
+  {
+    return mExpectedSize.isNothing() || *mExpectedSize == Size();
+  }
+
+  /**
    * Set an iterator to the SourceBuffer which will feed data to this decoder.
    * This must always be called before calling Init(). (And only before Init().)
    *
    * XXX(seth): We should eliminate this method and pass a SourceBufferIterator
    * to the various decoder constructors instead.
    */
   void SetIterator(SourceBufferIterator&& aIterator)
   {
@@ -271,16 +288,22 @@ public:
   {
     return mReachedTerminalState || mDecodeDone ||
            (mMetadataDecode && HasSize()) || HasError();
   }
 
   /// Are we in the middle of a frame right now? Used for assertions only.
   bool InFrame() const { return mInFrame; }
 
+  /// Is the image valid if embedded inside an ICO.
+  virtual bool IsValidICOResource() const
+  {
+    return false;
+  }
+
   enum DecodeStyle {
       PROGRESSIVE, // produce intermediate frames representing the partial
                    // state of the image
       SEQUENTIAL   // decode to final image immediately
   };
 
   /**
    * Get or set the DecoderFlags that influence the behavior of this decoder.
@@ -522,16 +545,17 @@ protected:
 
 private:
   RefPtr<RasterImage> mImage;
   Maybe<SourceBufferIterator> mIterator;
   RawAccessFrameRef mCurrentFrame;
   ImageMetadata mImageMetadata;
   gfx::IntRect mInvalidRect; // Tracks an invalidation region in the current frame.
   Maybe<gfx::IntSize> mOutputSize;  // The size of our output surface.
+  Maybe<gfx::IntSize> mExpectedSize; // The expected size of the image.
   Progress mProgress;
 
   uint32_t mFrameCount; // Number of frames, including anything in-progress
   FrameTimeout mLoopLength;  // Length of a single loop of this image.
   gfx::IntRect mFirstFrameRefreshArea;  // The area of the image that needs to
                                         // be invalidated when the animation loops.
 
   // Telemetry data for this decoder.
--- a/image/DecoderFactory.cpp
+++ b/image/DecoderFactory.cpp
@@ -229,18 +229,20 @@ DecoderFactory::CreateMetadataDecoder(De
   }
 
   RefPtr<IDecodingTask> task = new MetadataDecodingTask(WrapNotNull(decoder));
   return task.forget();
 }
 
 /* static */ already_AddRefed<Decoder>
 DecoderFactory::CreateDecoderForICOResource(DecoderType aType,
-                                            NotNull<SourceBuffer*> aSourceBuffer,
+                                            SourceBufferIterator&& aIterator,
                                             NotNull<nsICODecoder*> aICODecoder,
+                                            bool aIsMetadataDecode,
+                                            const Maybe<IntSize>& aExpectedSize,
                                             const Maybe<uint32_t>& aDataOffset
                                               /* = Nothing() */)
 {
   // Create the decoder.
   RefPtr<Decoder> decoder;
   switch (aType) {
     case DecoderType::BMP:
       MOZ_ASSERT(aDataOffset);
@@ -255,20 +257,24 @@ DecoderFactory::CreateDecoderForICOResou
     default:
       MOZ_ASSERT_UNREACHABLE("Invalid ICO resource decoder type");
       return nullptr;
   }
 
   MOZ_ASSERT(decoder);
 
   // Initialize the decoder, copying settings from @aICODecoder.
-  MOZ_ASSERT(!aICODecoder->IsMetadataDecode());
-  decoder->SetMetadataDecode(aICODecoder->IsMetadataDecode());
-  decoder->SetIterator(aSourceBuffer->Iterator());
-  decoder->SetOutputSize(aICODecoder->OutputSize());
+  decoder->SetMetadataDecode(aIsMetadataDecode);
+  decoder->SetIterator(Forward<SourceBufferIterator>(aIterator));
+  if (!aIsMetadataDecode) {
+    decoder->SetOutputSize(aICODecoder->OutputSize());
+  }
+  if (aExpectedSize) {
+    decoder->SetExpectedSize(*aExpectedSize);
+  }
   decoder->SetDecoderFlags(aICODecoder->GetDecoderFlags());
   decoder->SetSurfaceFlags(aICODecoder->GetSurfaceFlags());
   decoder->SetFinalizeFrames(false);
 
   if (NS_FAILED(decoder->Init())) {
     return nullptr;
   }
 
--- a/image/DecoderFactory.h
+++ b/image/DecoderFactory.h
@@ -18,16 +18,17 @@
 namespace mozilla {
 namespace image {
 
 class Decoder;
 class IDecodingTask;
 class nsICODecoder;
 class RasterImage;
 class SourceBuffer;
+class SourceBufferIterator;
 
 /**
  * The type of decoder; this is usually determined from a MIME type using
  * DecoderFactory::GetDecoderType().
  */
 enum class DecoderType
 {
   PNG,
@@ -114,31 +115,37 @@ public:
                         NotNull<SourceBuffer*> aSourceBuffer);
 
   /**
    * Creates and initializes a decoder for an ICO resource, which may be either
    * a BMP or PNG image.
    *
    * @param aType Which type of decoder to create. This must be either BMP or
    *              PNG.
-   * @param aSourceBuffer The SourceBuffer which the decoder will read its data
-   *                      from.
+   * @param aIterator The SourceBufferIterator which the decoder will read its
+   *                  data from.
    * @param aICODecoder The ICO decoder which is controlling this resource
    *                    decoder. @aICODecoder's settings will be copied to the
    *                    resource decoder, so the two decoders will have the
    *                    same decoder flags, surface flags, target size, and
    *                    other parameters.
+   * @param aIsMetadataDecode Indicates whether or not this decoder is for
+   *                          metadata or not. Independent of the state of the
+   *                          parent decoder.
+   * @param aExpectedSize The expected size of the resource from the ICO header.
    * @param aDataOffset If @aType is BMP, specifies the offset at which data
    *                    begins in the BMP resource. Must be Some() if and only
    *                    if @aType is BMP.
    */
   static already_AddRefed<Decoder>
   CreateDecoderForICOResource(DecoderType aType,
-                              NotNull<SourceBuffer*> aSourceBuffer,
+                              SourceBufferIterator&& aIterator,
                               NotNull<nsICODecoder*> aICODecoder,
+                              bool aIsMetadataDecode,
+                              const Maybe<gfx::IntSize>& aExpectedSize,
                               const Maybe<uint32_t>& aDataOffset = Nothing());
 
   /**
    * Creates and initializes an anonymous decoder (one which isn't associated
    * with an Image object). Only the first frame of the image will be decoded.
    *
    * @param aType Which type of decoder to create - JPEG, PNG, etc.
    * @param aSourceBuffer The SourceBuffer which the decoder will read its data
--- a/image/SourceBuffer.cpp
+++ b/image/SourceBuffer.cpp
@@ -37,16 +37,17 @@ SourceBufferIterator::operator=(SourceBu
     mOwner->OnIteratorRelease();
   }
 
   mOwner = Move(aOther.mOwner);
   mState = aOther.mState;
   mData = aOther.mData;
   mChunkCount = aOther.mChunkCount;
   mByteCount = aOther.mByteCount;
+  mRemainderToRead = aOther.mRemainderToRead;
 
   return *this;
 }
 
 SourceBufferIterator::State
 SourceBufferIterator::AdvanceOrScheduleResume(size_t aRequestedBytes,
                                               IResumable* aConsumer)
 {
@@ -58,16 +59,35 @@ SourceBufferIterator::AdvanceOrScheduleR
   }
 
   // The range of data [mOffset, mOffset + mNextReadLength) has just been read
   // by the caller (or at least they don't have any interest in it), so consume
   // that data.
   MOZ_ASSERT(mData.mIterating.mNextReadLength <= mData.mIterating.mAvailableLength);
   mData.mIterating.mOffset += mData.mIterating.mNextReadLength;
   mData.mIterating.mAvailableLength -= mData.mIterating.mNextReadLength;
+
+  // An iterator can have a limit imposed on it to read only a subset of a
+  // source buffer. If it is present, we need to mimic the same behaviour as
+  // the owning SourceBuffer.
+  if (MOZ_UNLIKELY(mRemainderToRead != SIZE_MAX)) {
+    MOZ_ASSERT(mData.mIterating.mNextReadLength <= mRemainderToRead);
+    mRemainderToRead -= mData.mIterating.mNextReadLength;
+
+    if (MOZ_UNLIKELY(mRemainderToRead == 0)) {
+      mData.mIterating.mNextReadLength = 0;
+      SetComplete(NS_OK);
+      return COMPLETE;
+    }
+
+    if (MOZ_UNLIKELY(aRequestedBytes > mRemainderToRead)) {
+      aRequestedBytes = mRemainderToRead;
+    }
+  }
+
   mData.mIterating.mNextReadLength = 0;
 
   if (MOZ_LIKELY(mState == READY)) {
     // If the caller wants zero bytes of data, that's easy enough; we just
     // configured ourselves for a zero-byte read above!  In theory we could do
     // this even in the START state, but it's not important for performance and
     // breaking the ability of callers to assert that the pointer returned by
     // Data() is non-null doesn't seem worth it.
@@ -513,24 +533,24 @@ SourceBuffer::SizeOfIncludingThisWithCom
 
     n += chunkSize;
   }
 
   return n;
 }
 
 SourceBufferIterator
-SourceBuffer::Iterator()
+SourceBuffer::Iterator(size_t aReadLength)
 {
   {
     MutexAutoLock lock(mMutex);
     mConsumerCount++;
   }
 
-  return SourceBufferIterator(this);
+  return SourceBufferIterator(this, aReadLength);
 }
 
 void
 SourceBuffer::OnIteratorRelease()
 {
   MutexAutoLock lock(mMutex);
 
   MOZ_ASSERT(mConsumerCount > 0, "Consumer count doesn't add up");
--- a/image/SourceBuffer.h
+++ b/image/SourceBuffer.h
@@ -72,36 +72,38 @@ class SourceBufferIterator final
 public:
   enum State {
     START,    // The iterator is at the beginning of the buffer.
     READY,    // The iterator is pointing to new data.
     WAITING,  // The iterator is blocked and the caller must yield.
     COMPLETE  // The iterator is pointing to the end of the buffer.
   };
 
-  explicit SourceBufferIterator(SourceBuffer* aOwner)
+  explicit SourceBufferIterator(SourceBuffer* aOwner, size_t aReadLimit)
     : mOwner(aOwner)
     , mState(START)
     , mChunkCount(0)
     , mByteCount(0)
+    , mRemainderToRead(aReadLimit)
   {
     MOZ_ASSERT(aOwner);
     mData.mIterating.mChunk = 0;
     mData.mIterating.mData = nullptr;
     mData.mIterating.mOffset = 0;
     mData.mIterating.mAvailableLength = 0;
     mData.mIterating.mNextReadLength = 0;
   }
 
   SourceBufferIterator(SourceBufferIterator&& aOther)
     : mOwner(Move(aOther.mOwner))
     , mState(aOther.mState)
     , mData(aOther.mData)
     , mChunkCount(aOther.mChunkCount)
     , mByteCount(aOther.mByteCount)
+    , mRemainderToRead(aOther.mRemainderToRead)
   { }
 
   ~SourceBufferIterator();
 
   SourceBufferIterator& operator=(SourceBufferIterator&& aOther);
 
   /**
    * Returns true if there are no more than @aBytes remaining in the
@@ -174,16 +176,29 @@ public:
   }
 
   /// @return a count of the chunks we've advanced through.
   uint32_t ChunkCount() const { return mChunkCount; }
 
   /// @return a count of the bytes in all chunks we've advanced through.
   size_t ByteCount() const { return mByteCount; }
 
+  /// @return the source buffer which owns the iterator.
+  SourceBuffer* Owner() const
+  {
+    MOZ_ASSERT(mOwner);
+    return mOwner;
+  }
+
+  /// @return the current offset from the beginning of the buffer.
+  size_t Position() const
+  {
+    return mByteCount - mData.mIterating.mAvailableLength;
+  }
+
 private:
   friend class SourceBuffer;
 
   SourceBufferIterator(const SourceBufferIterator&) = delete;
   SourceBufferIterator& operator=(const SourceBufferIterator&) = delete;
 
   bool HasMore() const { return mState != COMPLETE; }
 
@@ -203,16 +218,21 @@ private:
 
   State SetReady(uint32_t aChunk, const char* aData,
                  size_t aOffset, size_t aAvailableLength,
                  size_t aRequestedBytes)
   {
     MOZ_ASSERT(mState != COMPLETE);
     mState = READY;
 
+    // Prevent the iterator from reporting more data than it is allowed to read.
+    if (aAvailableLength > mRemainderToRead) {
+      aAvailableLength = mRemainderToRead;
+    }
+
     // Update state.
     mData.mIterating.mChunk = aChunk;
     mData.mIterating.mData = aData;
     mData.mIterating.mOffset = aOffset;
     mData.mIterating.mAvailableLength = aAvailableLength;
 
     // Update metrics.
     mChunkCount++;
@@ -241,29 +261,37 @@ private:
 
   /**
    * This union contains our iteration state if we're still iterating (for
    * states START, READY, and WAITING) and the status the SourceBuffer was
    * completed with if we're in state COMPLETE.
    */
   union {
     struct {
-      uint32_t mChunk;
-      const char* mData;
-      size_t mOffset;
-      size_t mAvailableLength;
-      size_t mNextReadLength;
-    } mIterating;
+      uint32_t mChunk;   // Index of the chunk in SourceBuffer.
+      const char* mData; // Pointer to the start of the chunk.
+      size_t mOffset;    // Current read position of the iterator relative to
+                         // mData.
+      size_t mAvailableLength; // How many bytes remain unread in the chunk,
+                               // relative to mOffset.
+      size_t mNextReadLength; // How many bytes the last iterator advance
+                              // requested to be read, so that we know much
+                              // to increase mOffset and reduce mAvailableLength
+                              // by when the next advance is requested.
+    } mIterating;        // Cached info of the chunk currently iterating over.
     struct {
-      nsresult mStatus;
-    } mAtEnd;
+      nsresult mStatus;  // Status code indicating if we read all the data.
+    } mAtEnd;            // State info after iterator is complete.
   } mData;
 
-  uint32_t mChunkCount;  // Count of chunks we've advanced through.
-  size_t mByteCount;     // Count of bytes in all chunks we've advanced through.
+  uint32_t mChunkCount;  // Count of chunks observed, including current chunk.
+  size_t mByteCount;     // Count of readable bytes observed, including unread
+                         // bytes from the current chunk.
+  size_t mRemainderToRead; // Count of bytes left to read if there is a maximum
+                           // imposed by the caller. SIZE_MAX if unlimited.
 };
 
 /**
  * SourceBuffer is a parallel data structure used for storing image source
  * (compressed) data.
  *
  * SourceBuffer is a single producer, multiple consumer data structure. The
  * single producer calls Append() to append data to the buffer. In parallel,
@@ -314,18 +342,21 @@ public:
   /// Memory reporting.
   size_t SizeOfIncludingThisWithComputedFallback(MallocSizeOf) const;
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Consumer methods.
   //////////////////////////////////////////////////////////////////////////////
 
-  /// Returns an iterator to this SourceBuffer.
-  SourceBufferIterator Iterator();
+  /**
+   * Returns an iterator to this SourceBuffer, which cannot read more than the
+   * given length.
+   */
+  SourceBufferIterator Iterator(size_t aReadLength = SIZE_MAX);
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Consumer methods.
   //////////////////////////////////////////////////////////////////////////////
 
   /**
    * The minimum chunk capacity we'll allocate, if we don't know the correct
--- a/image/StreamingLexer.h
+++ b/image/StreamingLexer.h
@@ -388,16 +388,62 @@ public:
       // zero bytes.
       MOZ_ASSERT_UNREACHABLE("Truncated state makes no sense");
       return;
     }
 
     SetTransition(aStartState);
   }
 
+  /**
+   * From the given SourceBufferIterator, aIterator, create a new iterator at
+   * the same position, with the given read limit, aReadLimit. The read limit
+   * applies after adjusting for the position. If the given iterator has been
+   * advanced, but required buffering inside StreamingLexer, the position
+   * of the cloned iterator will be at the beginning of buffered data; this
+   * should match the perspective of the caller.
+   */
+  SourceBufferIterator Clone(SourceBufferIterator& aIterator,
+                             size_t aReadLimit) const
+  {
+    // In order to advance to the current position of the iterator from the
+    // perspective of the caller, we need to take into account if we are
+    // buffering data.
+    size_t pos = aIterator.Position();
+    if (!mBuffer.empty()) {
+      pos += aIterator.Length();
+      MOZ_ASSERT(pos > mBuffer.length());
+      pos -= mBuffer.length();
+    }
+
+    size_t readLimit = aReadLimit;
+    if (aReadLimit != SIZE_MAX) {
+      readLimit += pos;
+    }
+
+    SourceBufferIterator other = aIterator.Owner()->Iterator(readLimit);
+
+    // Since the current iterator has already advanced to this point, we
+    // know that the state can only be READY or COMPLETE. That does not mean
+    // everything is stored in a single chunk, and may require multiple Advance
+    // calls to get where we want to be.
+    DebugOnly<SourceBufferIterator::State> state;
+    do {
+      state = other.Advance(pos);
+      MOZ_ASSERT(state != SourceBufferIterator::WAITING);
+      MOZ_ASSERT(pos >= other.Length());
+      pos -= other.Length();
+    } while (pos > 0);
+
+    // Force the data pointer to be where we expect it to be.
+    state = other.Advance(0);
+    MOZ_ASSERT(state != SourceBufferIterator::WAITING);
+    return other;
+  }
+
   template <typename Func>
   LexerResult Lex(SourceBufferIterator& aIterator,
                   IResumable* aOnResume,
                   Func aFunc)
   {
     if (mTransition.NextStateIsTerminal()) {
       // We've already reached a terminal state. We never deliver any more data
       // in this case; just return the terminal state again immediately.
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -540,16 +540,24 @@ nsBMPDecoder::ReadInfoHeaderRest(const c
     mH.mNumColors   = aLength >= 32 ? LittleEndian::readUint32(aData + 28) : 0;
     // We ignore the important_colors (aData + 36) field.
 
     // For WinBMPv4, WinBMPv5 and (possibly) OS2-BMPv2 there are additional
     // fields in the info header which we ignore, with the possible exception
     // of the color bitfields (see below).
   }
 
+  // The height for BMPs embedded inside an ICO includes spaces for the AND
+  // mask even if it is not present, thus we need to adjust for that here.
+  if (mIsWithinICO) {
+    // XXX(seth): Should we really be writing the absolute value from
+    // the BIH below? Seems like this could be problematic for inverted BMPs.
+    mH.mHeight = abs(mH.mHeight) / 2;
+  }
+
   // Run with MOZ_LOG=BMPDecoder:5 set to see this output.
   MOZ_LOG(sBMPLog, LogLevel::Debug,
           ("BMP: bihsize=%u, %d x %d, bpp=%u, compression=%u, colors=%u\n",
           mH.mBIHSize, mH.mWidth, mH.mHeight, uint32_t(mH.mBpp),
           mH.mCompression, mH.mNumColors));
 
   // BMPs with negative width are invalid. Also, reject extremely wide images
   // to keep the math sane. And reject INT_MIN as a height because you can't
@@ -645,16 +653,19 @@ nsBMPDecoder::ReadBitfields(const char* 
     (mH.mCompression == Compression::BITFIELDS &&
      mBitFields.mAlpha.IsPresent());
   if (mMayHaveTransparency) {
     PostHasTransparency();
   }
 
   // Post our size to the superclass.
   PostSize(mH.mWidth, AbsoluteHeight());
+  if (HasError()) {
+    return Transition::TerminateFailure();
+  }
 
   // We've now read all the headers. If we're doing a metadata decode, we're
   // done.
   if (IsMetadataDecode()) {
     return Transition::TerminateSuccess();
   }
 
   // Set up the color table, if present; it'll be filled in by ReadColorTable().
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -120,16 +120,19 @@ class RasterImage;
 
 /// Decoder for BMP-Files, as used by Windows and OS/2.
 
 class nsBMPDecoder : public Decoder
 {
 public:
   ~nsBMPDecoder();
 
+  /// @return true if this BMP is a valid ICO resource.
+  bool IsValidICOResource() const override { return true; }
+
   /// Obtains the internal output image buffer.
   uint32_t* GetImageData() { return reinterpret_cast<uint32_t*>(mImageData); }
 
   /// Obtains the length of the internal output image buffer.
   size_t GetImageDataLength() const { return mImageDataLength; }
 
   /// Obtains the size of the compressed image resource.
   int32_t GetCompressedImageSize() const;
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -50,19 +50,17 @@ nsICODecoder::GetNumColors()
   }
   return numColors;
 }
 
 nsICODecoder::nsICODecoder(RasterImage* aImage)
   : Decoder(aImage)
   , mLexer(Transition::To(ICOState::HEADER, ICOHEADERSIZE),
            Transition::TerminateSuccess())
-  , mBiggestResourceColorDepth(0)
-  , mBestResourceDelta(INT_MIN)
-  , mBestResourceColorDepth(0)
+  , mDirEntry(nullptr)
   , mNumIcons(0)
   , mCurrIcon(0)
   , mBPP(0)
   , mMaskRowSize(0)
   , mCurrMaskLine(0)
   , mIsCursor(false)
   , mHasMaskAlpha(false)
 { }
@@ -86,94 +84,34 @@ nsICODecoder::FinishWithErrorInternal()
 
 nsresult
 nsICODecoder::GetFinalStateFromContainedDecoder()
 {
   if (!mContainedDecoder) {
     return NS_OK;
   }
 
-  MOZ_ASSERT(mContainedSourceBuffer,
-             "Should have a SourceBuffer if we have a decoder");
-
   // Let the contained decoder finish up if necessary.
-  if (!mContainedSourceBuffer->IsComplete()) {
-    mContainedSourceBuffer->Complete(NS_OK);
-    mContainedDecoder->Decode();
-  }
+  FlushContainedDecoder();
 
   // Make our state the same as the state of the contained decoder.
   mDecodeDone = mContainedDecoder->GetDecodeDone();
   mProgress |= mContainedDecoder->TakeProgress();
   mInvalidRect.UnionRect(mInvalidRect, mContainedDecoder->TakeInvalidRect());
   mCurrentFrame = mContainedDecoder->GetCurrentFrameRef();
 
   // Propagate errors.
   nsresult rv = HasError() || mContainedDecoder->HasError()
               ? NS_ERROR_FAILURE
               : NS_OK;
 
   MOZ_ASSERT(NS_FAILED(rv) || !mCurrentFrame || mCurrentFrame->IsFinished());
   return rv;
 }
 
-bool
-nsICODecoder::CheckAndFixBitmapSize(int8_t* aBIH)
-{
-  // Get the width from the BMP file information header. This is
-  // (unintuitively) a signed integer; see the documentation at:
-  //
-  //   https://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
-  //
-  // However, we reject negative widths since they aren't meaningful.
-  const int32_t width = LittleEndian::readInt32(aBIH + 4);
-  if (width <= 0 || width > 256) {
-    return false;
-  }
-
-  // Verify that the BMP width matches the width we got from the ICO directory
-  // entry. If not, decoding fails, because if we were to allow it to continue
-  // the intrinsic size of the image wouldn't match the size of the decoded
-  // surface.
-  if (width != int32_t(GetRealWidth())) {
-    return false;
-  }
-
-  // Get the height from the BMP file information header. This is also signed,
-  // but in this case negative values are meaningful; see below.
-  int32_t height = LittleEndian::readInt32(aBIH + 8);
-  if (height == 0) {
-    return false;
-  }
-
-  // BMPs can be stored inverted by having a negative height.
-  // XXX(seth): Should we really be writing the absolute value into the BIH
-  // below? Seems like this could be problematic for inverted BMPs.
-  height = abs(height);
-
-  // The height field is double the actual height of the image to account for
-  // the AND mask. This is true even if the AND mask is not present.
-  height /= 2;
-  if (height > 256) {
-    return false;
-  }
-
-  // Verify that the BMP height matches the height we got from the ICO directory
-  // entry. If not, again, decoding fails.
-  if (height != int32_t(GetRealHeight())) {
-    return false;
-  }
-
-  // Fix the BMP height in the BIH so that the BMP decoder, which does not know
-  // about the AND mask that may follow the actual bitmap, can work properly.
-  LittleEndian::writeInt32(aBIH + 8, GetRealHeight());
-
-  return true;
-}
-
 LexerTransition<ICOState>
 nsICODecoder::ReadHeader(const char* aData)
 {
   // If the third byte is 1, this is an icon. If 2, a cursor.
   if ((aData[2] != 1) && (aData[2] != 2)) {
     return Transition::TerminateFailure();
   }
   mIsCursor = (aData[2] == 2);
@@ -206,334 +144,415 @@ nsICODecoder::FirstResourceOffset() cons
   return ICOHEADERSIZE + mNumIcons * ICODIRENTRYSIZE;
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadDirEntry(const char* aData)
 {
   mCurrIcon++;
 
-  // Read the directory entry.
-  IconDirEntry e;
-  e.mWidth       = aData[0];
-  e.mHeight      = aData[1];
-  e.mColorCount  = aData[2];
-  e.mReserved    = aData[3];
-  e.mPlanes      = LittleEndian::readUint16(aData + 4);
-  e.mBitCount    = LittleEndian::readUint16(aData + 6);
-  e.mBytesInRes  = LittleEndian::readUint32(aData + 8);
-  e.mImageOffset = LittleEndian::readUint32(aData + 12);
-
-  // If an explicit output size was specified, we'll try to select the resource
-  // that matches it best below.
-  const Maybe<IntSize> desiredSize = ExplicitOutputSize();
-
-  // Determine if this is the biggest resource we've seen so far. We always use
-  // the biggest resource for the intrinsic size, and if we don't have a
-  // specific desired size, we select it as the best resource as well.
-  IntSize entrySize(GetRealWidth(e), GetRealHeight(e));
-  if (e.mBitCount >= mBiggestResourceColorDepth &&
-      entrySize.width * entrySize.height >=
-        mBiggestResourceSize.width * mBiggestResourceSize.height) {
-    mBiggestResourceSize = entrySize;
-    mBiggestResourceColorDepth = e.mBitCount;
-    mBiggestResourceHotSpot = IntSize(e.mXHotspot, e.mYHotspot);
-
-    if (!desiredSize) {
-      mDirEntry = e;
-    }
-  }
-
-  mImageMetadata.AddNativeSize(entrySize);
-
-  if (desiredSize) {
-    // Calculate the delta between this resource's size and the desired size, so
-    // we can see if it is better than our current-best option.  In the case of
-    // several equally-good resources, we use the last one. "Better" in this
-    // case is determined by |delta|, a measure of the difference in size
-    // between the entry we've found and the desired size. We will choose the
-    // smallest resource that is greater than or equal to the desired size (i.e.
-    // we assume it's better to downscale a larger icon than to upscale a
-    // smaller one).
-    int32_t delta = std::min(entrySize.width - desiredSize->width,
-                             entrySize.height - desiredSize->height);
-    if (e.mBitCount >= mBestResourceColorDepth &&
-        ((mBestResourceDelta < 0 && delta >= mBestResourceDelta) ||
-         (delta >= 0 && delta <= mBestResourceDelta))) {
-      mBestResourceDelta = delta;
-      mBestResourceColorDepth = e.mBitCount;
-      mDirEntry = e;
+  // Ensure the resource has an offset past the ICO headers.
+  uint32_t offset = LittleEndian::readUint32(aData + 12);
+  if (offset >= FirstResourceOffset()) {
+    // Read the directory entry.
+    IconDirEntryEx e;
+    e.mWidth       = aData[0];
+    e.mHeight      = aData[1];
+    e.mColorCount  = aData[2];
+    e.mReserved    = aData[3];
+    e.mPlanes      = LittleEndian::readUint16(aData + 4);
+    e.mBitCount    = LittleEndian::readUint16(aData + 6);
+    e.mBytesInRes  = LittleEndian::readUint32(aData + 8);
+    e.mImageOffset = offset;
+    e.mSize        = IntSize(e.mWidth, e.mHeight);
+    if (e.mWidth == 0 || e.mHeight == 0) {
+      mUnsizedDirEntries.AppendElement(e);
+    } else {
+      mDirEntries.AppendElement(e);
     }
   }
 
   if (mCurrIcon == mNumIcons) {
-    // Ensure the resource we selected has an offset past the ICO headers.
-    if (mDirEntry.mImageOffset < FirstResourceOffset()) {
-      return Transition::TerminateFailure();
-    }
-
-    // If this is a cursor, set the hotspot. We use the hotspot from the biggest
-    // resource since we also use that resource for the intrinsic size.
-    if (mIsCursor) {
-      mImageMetadata.SetHotspot(mBiggestResourceHotSpot.width,
-                                mBiggestResourceHotSpot.height);
+    if (mUnsizedDirEntries.IsEmpty()) {
+      return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
     }
-
-    // We always report the biggest resource's size as the intrinsic size; this
-    // is necessary for downscale-during-decode to work since we won't even
-    // attempt to *upscale* while decoding.
-    PostSize(mBiggestResourceSize.width, mBiggestResourceSize.height);
-    if (IsMetadataDecode()) {
-      return Transition::TerminateSuccess();
-    }
-
-    // If the resource we selected matches the output size perfectly, we don't
-    // need to do any downscaling.
-    if (GetRealSize() == OutputSize()) {
-      MOZ_ASSERT_IF(desiredSize, GetRealSize() == *desiredSize);
-      MOZ_ASSERT_IF(!desiredSize, GetRealSize() == Size());
-      mDownscaler.reset();
-    }
-
-    size_t offsetToResource = mDirEntry.mImageOffset - FirstResourceOffset();
-    return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
-                                    ICOState::SKIP_TO_RESOURCE,
-                                    offsetToResource);
+    return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
   }
 
   return Transition::To(ICOState::DIR_ENTRY, ICODIRENTRYSIZE);
 }
 
 LexerTransition<ICOState>
+nsICODecoder::IterateUnsizedDirEntry()
+{
+  MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
+
+  if (!mDirEntry) {
+    // The first time we are here, there is no entry selected. We must prepare a
+    // new iterator for the contained decoder to advance as it wills. Cloning at
+    // this point ensures it will begin at the end of the dir entries.
+    mReturnIterator.emplace(mLexer.Clone(*mIterator, SIZE_MAX));
+  } else {
+    // We have already selected an entry which means a metadata decoder has
+    // finished. Verify the size is valid and if so, add to the discovered
+    // resources.
+    if (mDirEntry->mSize.width > 0 && mDirEntry->mSize.height > 0) {
+      mDirEntries.AppendElement(*mDirEntry);
+    }
+
+    // Remove the entry from the unsized list either way.
+    mDirEntry = nullptr;
+    mUnsizedDirEntries.RemoveElementAt(0);
+
+    // Our iterator is at an unknown point, so reset it to the point that we
+    // saved.
+    mIterator.reset();
+    mIterator.emplace(mLexer.Clone(*mReturnIterator, SIZE_MAX));
+  }
+
+  // There are no more unsized entries, so we can finally decide which entry to
+  // select for decoding.
+  if (mUnsizedDirEntries.IsEmpty()) {
+    mReturnIterator.reset();
+    return Transition::To(ICOState::FINISHED_DIR_ENTRY, 0);
+  }
+
+  // Move to the resource data to start metadata decoding.
+  mDirEntry = &mUnsizedDirEntries[0];
+  size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
+  return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
+                                  ICOState::SKIP_TO_RESOURCE,
+                                  offsetToResource);
+}
+
+LexerTransition<ICOState>
+nsICODecoder::FinishDirEntry()
+{
+  MOZ_ASSERT(!mDirEntry);
+
+  if (mDirEntries.IsEmpty()) {
+    return Transition::TerminateFailure();
+  }
+
+  // If an explicit output size was specified, we'll try to select the resource
+  // that matches it best below.
+  const Maybe<IntSize> desiredSize = ExplicitOutputSize();
+
+  // Determine the biggest resource. We always use the biggest resource for the
+  // intrinsic size, and if we don't have a specific desired size, we select it
+  // as the best resource as well.
+  int32_t bestDelta = INT32_MIN;
+  IconDirEntryEx* biggestEntry = nullptr;
+
+  for (size_t i = 0; i < mDirEntries.Length(); ++i) {
+    IconDirEntryEx& e = mDirEntries[i];
+    mImageMetadata.AddNativeSize(e.mSize);
+
+    if (!biggestEntry ||
+        (e.mBitCount >= biggestEntry->mBitCount &&
+         e.mSize.width * e.mSize.height >=
+           biggestEntry->mSize.width * biggestEntry->mSize.height)) {
+      biggestEntry = &e;
+
+      if (!desiredSize) {
+        mDirEntry = &e;
+      }
+    }
+
+    if (desiredSize) {
+      // Calculate the delta between this resource's size and the desired size, so
+      // we can see if it is better than our current-best option.  In the case of
+      // several equally-good resources, we use the last one. "Better" in this
+      // case is determined by |delta|, a measure of the difference in size
+      // between the entry we've found and the desired size. We will choose the
+      // smallest resource that is greater than or equal to the desired size (i.e.
+      // we assume it's better to downscale a larger icon than to upscale a
+      // smaller one).
+      int32_t delta = std::min(e.mSize.width - desiredSize->width,
+                               e.mSize.height - desiredSize->height);
+      if (!mDirEntry ||
+          (e.mBitCount >= mDirEntry->mBitCount &&
+           ((bestDelta < 0 && delta >= bestDelta) ||
+            (delta >= 0 && delta <= bestDelta)))) {
+        mDirEntry = &e;
+        bestDelta = delta;
+      }
+    }
+  }
+
+  MOZ_ASSERT(mDirEntry);
+  MOZ_ASSERT(biggestEntry);
+
+  // If this is a cursor, set the hotspot. We use the hotspot from the biggest
+  // resource since we also use that resource for the intrinsic size.
+  if (mIsCursor) {
+    mImageMetadata.SetHotspot(biggestEntry->mXHotspot,
+                              biggestEntry->mYHotspot);
+  }
+
+  // We always report the biggest resource's size as the intrinsic size; this
+  // is necessary for downscale-during-decode to work since we won't even
+  // attempt to *upscale* while decoding.
+  PostSize(biggestEntry->mSize.width, biggestEntry->mSize.height);
+  if (HasError()) {
+    return Transition::TerminateFailure();
+  }
+
+  if (IsMetadataDecode()) {
+    return Transition::TerminateSuccess();
+  }
+
+  // If the resource we selected matches the output size perfectly, we don't
+  // need to do any downscaling.
+  if (mDirEntry->mSize == OutputSize()) {
+    MOZ_ASSERT_IF(desiredSize, mDirEntry->mSize == *desiredSize);
+    MOZ_ASSERT_IF(!desiredSize, mDirEntry->mSize == Size());
+    mDownscaler.reset();
+  }
+
+  size_t offsetToResource = mDirEntry->mImageOffset - FirstResourceOffset();
+  return Transition::ToUnbuffered(ICOState::FOUND_RESOURCE,
+                                  ICOState::SKIP_TO_RESOURCE,
+                                  offsetToResource);
+}
+
+LexerTransition<ICOState>
 nsICODecoder::SniffResource(const char* aData)
 {
+  MOZ_ASSERT(mDirEntry);
+
+  // We have BITMAPINFOSIZE bytes buffered at this point. We know an embedded
+  // BMP will have at least that many bytes by definition. We can also infer
+  // that any valid embedded PNG will contain that many bytes as well because:
+  //    BITMAPINFOSIZE
+  //      <
+  //    signature (8 bytes) +
+  //    IHDR (12 bytes header + 13 bytes data)
+  //    IDAT (12 bytes header)
+
   // We use the first PNGSIGNATURESIZE bytes to determine whether this resource
   // is a PNG or a BMP.
   bool isPNG = !memcmp(aData, nsPNGDecoder::pngSignatureBytes,
                        PNGSIGNATURESIZE);
   if (isPNG) {
-    // Create a PNG decoder which will do the rest of the work for us.
-    mContainedSourceBuffer = new SourceBuffer();
-    mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
-    mContainedDecoder =
-      DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
-                                                  WrapNotNull(mContainedSourceBuffer),
-                                                  WrapNotNull(this));
-
-    if (!WriteToContainedDecoder(aData, PNGSIGNATURESIZE)) {
+    if (mDirEntry->mBytesInRes <= BITMAPINFOSIZE) {
       return Transition::TerminateFailure();
     }
 
-    if (mDirEntry.mBytesInRes <= PNGSIGNATURESIZE) {
-      return Transition::TerminateFailure();
-    }
+    // Prepare a new iterator for the contained decoder to advance as it wills.
+    // Cloning at the point ensures it will begin at the resource offset.
+    SourceBufferIterator containedIterator
+      = mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
+
+    // Create a PNG decoder which will do the rest of the work for us.
+    bool metadataDecode = mReturnIterator.isSome();
+    Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
+                                                 : Some(mDirEntry->mSize);
+    mContainedDecoder =
+      DecoderFactory::CreateDecoderForICOResource(DecoderType::PNG,
+                                                  Move(containedIterator),
+                                                  WrapNotNull(this),
+                                                  metadataDecode,
+                                                  expectedSize);
 
     // Read in the rest of the PNG unbuffered.
-    size_t toRead = mDirEntry.mBytesInRes - PNGSIGNATURESIZE;
+    size_t toRead = mDirEntry->mBytesInRes - BITMAPINFOSIZE;
     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
-                                    ICOState::READ_PNG,
+                                    ICOState::READ_RESOURCE,
                                     toRead);
   } else {
     // Make sure we have a sane size for the bitmap information header.
     int32_t bihSize = LittleEndian::readUint32(aData);
     if (bihSize != static_cast<int32_t>(BITMAPINFOSIZE)) {
       return Transition::TerminateFailure();
     }
 
-    // Buffer the first part of the bitmap information header.
-    memcpy(mBIHraw, aData, PNGSIGNATURESIZE);
-
     // Read in the rest of the bitmap information header.
-    return Transition::To(ICOState::READ_BIH,
-                          BITMAPINFOSIZE - PNGSIGNATURESIZE);
+    return ReadBIH(aData);
   }
 }
 
 LexerTransition<ICOState>
-nsICODecoder::ReadPNG(const char* aData, uint32_t aLen)
+nsICODecoder::ReadResource()
 {
-  if (!WriteToContainedDecoder(aData, aLen)) {
+  if (!FlushContainedDecoder()) {
     return Transition::TerminateFailure();
   }
 
-  // Raymond Chen says that 32bpp only are valid PNG ICOs
-  // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
-  if (!static_cast<nsPNGDecoder*>(mContainedDecoder.get())->IsValidICO()) {
-    return Transition::TerminateFailure();
-  }
-
-  return Transition::ContinueUnbuffered(ICOState::READ_PNG);
+  return Transition::ContinueUnbuffered(ICOState::READ_RESOURCE);
 }
 
 LexerTransition<ICOState>
 nsICODecoder::ReadBIH(const char* aData)
 {
-  // Buffer the rest of the bitmap information header.
-  memcpy(mBIHraw + PNGSIGNATURESIZE, aData, BITMAPINFOSIZE - PNGSIGNATURESIZE);
+  MOZ_ASSERT(mDirEntry);
 
   // Extract the BPP from the BIH header; it should be trusted over the one
   // we have from the ICO header which is usually set to 0.
-  mBPP = LittleEndian::readUint16(mBIHraw + 14);
-
-  // The ICO format when containing a BMP does not include the 14 byte
-  // bitmap file header. So we create the BMP decoder via the constructor that
-  // tells it to skip this, and pass in the required data (dataOffset) that
-  // would have been present in the header.
-  uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE;
-  if (mBPP <= 8) {
-    // The color table is present only if BPP is <= 8.
-    uint16_t numColors = GetNumColors();
-    if (numColors == (uint16_t)-1) {
-      return Transition::TerminateFailure();
-    }
-    dataOffset += 4 * numColors;
-  }
-
-  // Create a BMP decoder which will do most of the work for us; the exception
-  // is the AND mask, which isn't present in standalone BMPs.
-  mContainedSourceBuffer = new SourceBuffer();
-  mContainedSourceBuffer->ExpectLength(mDirEntry.mBytesInRes);
-  mContainedDecoder =
-    DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
-                                                WrapNotNull(mContainedSourceBuffer),
-                                                WrapNotNull(this),
-                                                Some(dataOffset));
-  RefPtr<nsBMPDecoder> bmpDecoder =
-    static_cast<nsBMPDecoder*>(mContainedDecoder.get());
-
-  // Verify that the BIH width and height values match the ICO directory entry,
-  // and fix the BIH height value to compensate for the fact that the underlying
-  // BMP decoder doesn't know about AND masks.
-  if (!CheckAndFixBitmapSize(reinterpret_cast<int8_t*>(mBIHraw))) {
-    return Transition::TerminateFailure();
-  }
-
-  // Write out the BMP's bitmap info header.
-  if (!WriteToContainedDecoder(mBIHraw, sizeof(mBIHraw))) {
-    return Transition::TerminateFailure();
-  }
+  mBPP = LittleEndian::readUint16(aData + 14);
 
   // Check to make sure we have valid color settings.
   uint16_t numColors = GetNumColors();
   if (numColors == uint16_t(-1)) {
     return Transition::TerminateFailure();
   }
 
+  // The color table is present only if BPP is <= 8.
+  MOZ_ASSERT_IF(mBPP > 8, numColors == 0);
+
+  // The ICO format when containing a BMP does not include the 14 byte
+  // bitmap file header. So we create the BMP decoder via the constructor that
+  // tells it to skip this, and pass in the required data (dataOffset) that
+  // would have been present in the header.
+  uint32_t dataOffset = bmp::FILE_HEADER_LENGTH + BITMAPINFOSIZE + 4 * numColors;
+
+  // Prepare a new iterator for the contained decoder to advance as it wills.
+  // Cloning at the point ensures it will begin at the resource offset.
+  SourceBufferIterator containedIterator
+    = mLexer.Clone(*mIterator, mDirEntry->mBytesInRes);
+
+  // Create a BMP decoder which will do most of the work for us; the exception
+  // is the AND mask, which isn't present in standalone BMPs.
+  bool metadataDecode = mReturnIterator.isSome();
+  Maybe<IntSize> expectedSize = metadataDecode ? Nothing()
+                                               : Some(mDirEntry->mSize);
+  mContainedDecoder =
+    DecoderFactory::CreateDecoderForICOResource(DecoderType::BMP,
+                                                Move(containedIterator),
+                                                WrapNotNull(this),
+                                                metadataDecode,
+                                                expectedSize,
+                                                Some(dataOffset));
+
+  RefPtr<nsBMPDecoder> bmpDecoder =
+    static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+
+  // Ensure the decoder has parsed at least the BMP's bitmap info header.
+  if (!FlushContainedDecoder()) {
+    return Transition::TerminateFailure();
+  }
+
+  // If this is a metadata decode, FinishResource will any necessary checks.
+  if (mContainedDecoder->IsMetadataDecode()) {
+    return Transition::To(ICOState::FINISHED_RESOURCE, 0);
+  }
+
   // Do we have an AND mask on this BMP? If so, we need to read it after we read
   // the BMP data itself.
   uint32_t bmpDataLength = bmpDecoder->GetCompressedImageSize() + 4 * numColors;
-  bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry.mBytesInRes;
+  bool hasANDMask = (BITMAPINFOSIZE + bmpDataLength) < mDirEntry->mBytesInRes;
   ICOState afterBMPState = hasANDMask ? ICOState::PREPARE_FOR_MASK
                                       : ICOState::FINISHED_RESOURCE;
 
   // Read in the rest of the BMP unbuffered.
   return Transition::ToUnbuffered(afterBMPState,
-                                  ICOState::READ_BMP,
+                                  ICOState::READ_RESOURCE,
                                   bmpDataLength);
 }
 
 LexerTransition<ICOState>
-nsICODecoder::ReadBMP(const char* aData, uint32_t aLen)
-{
-  if (!WriteToContainedDecoder(aData, aLen)) {
-    return Transition::TerminateFailure();
-  }
-
-  return Transition::ContinueUnbuffered(ICOState::READ_BMP);
-}
-
-LexerTransition<ICOState>
 nsICODecoder::PrepareForMask()
 {
+  MOZ_ASSERT(mDirEntry);
+  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
+
+  // We have received all of the data required by the BMP decoder so flushing
+  // here guarantees the decode has finished.
+  if (!FlushContainedDecoder()) {
+    return Transition::TerminateFailure();
+  }
+
+  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
+
   RefPtr<nsBMPDecoder> bmpDecoder =
     static_cast<nsBMPDecoder*>(mContainedDecoder.get());
 
   uint16_t numColors = GetNumColors();
   MOZ_ASSERT(numColors != uint16_t(-1));
 
   // Determine the length of the AND mask.
   uint32_t bmpLengthWithHeader =
     BITMAPINFOSIZE + bmpDecoder->GetCompressedImageSize() + 4 * numColors;
-  MOZ_ASSERT(bmpLengthWithHeader < mDirEntry.mBytesInRes);
-  uint32_t maskLength = mDirEntry.mBytesInRes - bmpLengthWithHeader;
+  MOZ_ASSERT(bmpLengthWithHeader < mDirEntry->mBytesInRes);
+  uint32_t maskLength = mDirEntry->mBytesInRes - bmpLengthWithHeader;
 
-  // If the BMP provides its own transparency, we ignore the AND mask. We can
-  // also obviously ignore it if the image has zero width or zero height.
-  if (bmpDecoder->HasTransparency() ||
-      GetRealWidth() == 0 || GetRealHeight() == 0) {
+  // If the BMP provides its own transparency, we ignore the AND mask.
+  if (bmpDecoder->HasTransparency()) {
     return Transition::ToUnbuffered(ICOState::FINISHED_RESOURCE,
                                     ICOState::SKIP_MASK,
                                     maskLength);
   }
 
   // Compute the row size for the mask.
-  mMaskRowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
+  mMaskRowSize = ((mDirEntry->mSize.width + 31) / 32) * 4; // + 31 to round up
 
   // If the expected size of the AND mask is larger than its actual size, then
   // we must have a truncated (and therefore corrupt) AND mask.
-  uint32_t expectedLength = mMaskRowSize * GetRealHeight();
+  uint32_t expectedLength = mMaskRowSize * mDirEntry->mSize.height;
   if (maskLength < expectedLength) {
     return Transition::TerminateFailure();
   }
 
   // If we're downscaling, the mask is the wrong size for the surface we've
   // produced, so we need to downscale the mask into a temporary buffer and then
   // combine the mask's alpha values with the color values from the image.
   if (mDownscaler) {
     MOZ_ASSERT(bmpDecoder->GetImageDataLength() ==
                  mDownscaler->TargetSize().width *
                  mDownscaler->TargetSize().height *
                  sizeof(uint32_t));
     mMaskBuffer = MakeUnique<uint8_t[]>(bmpDecoder->GetImageDataLength());
-    nsresult rv = mDownscaler->BeginFrame(GetRealSize(), Nothing(),
+    nsresult rv = mDownscaler->BeginFrame(mDirEntry->mSize, Nothing(),
                                           mMaskBuffer.get(),
                                           /* aHasAlpha = */ true,
                                           /* aFlipVertically = */ true);
     if (NS_FAILED(rv)) {
       return Transition::TerminateFailure();
     }
   }
 
-  mCurrMaskLine = GetRealHeight();
+  mCurrMaskLine = mDirEntry->mSize.height;
   return Transition::To(ICOState::READ_MASK_ROW, mMaskRowSize);
 }
 
 
 LexerTransition<ICOState>
 nsICODecoder::ReadMaskRow(const char* aData)
 {
+  MOZ_ASSERT(mDirEntry);
+
   mCurrMaskLine--;
 
   uint8_t sawTransparency = 0;
 
   // Get the mask row we're reading.
   const uint8_t* mask = reinterpret_cast<const uint8_t*>(aData);
   const uint8_t* maskRowEnd = mask + mMaskRowSize;
 
   // Get the corresponding row of the mask buffer (if we're downscaling) or the
   // decoded image data (if we're not).
   uint32_t* decoded = nullptr;
   if (mDownscaler) {
     // Initialize the row to all white and fully opaque.
-    memset(mDownscaler->RowBuffer(), 0xFF, GetRealWidth() * sizeof(uint32_t));
+    memset(mDownscaler->RowBuffer(), 0xFF, mDirEntry->mSize.width * sizeof(uint32_t));
 
     decoded = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
   } else {
     RefPtr<nsBMPDecoder> bmpDecoder =
       static_cast<nsBMPDecoder*>(mContainedDecoder.get());
     uint32_t* imageData = bmpDecoder->GetImageData();
     if (!imageData) {
       return Transition::TerminateFailure();
     }
 
-    decoded = imageData + mCurrMaskLine * GetRealWidth();
+    decoded = imageData + mCurrMaskLine * mDirEntry->mSize.width;
   }
 
   MOZ_ASSERT(decoded);
-  uint32_t* decodedRowEnd = decoded + GetRealWidth();
+  uint32_t* decodedRowEnd = decoded + mDirEntry->mSize.width;
 
   // Iterate simultaneously through the AND mask and the image data.
   while (mask < maskRowEnd) {
     uint8_t idx = *mask++;
     sawTransparency |= idx;
     for (uint8_t bit = 0x80; bit && decoded < decodedRowEnd; bit >>= 1) {
       // Clear pixel completely for transparency.
       if (idx & bit) {
@@ -583,23 +602,45 @@ nsICODecoder::FinishMask()
   }
 
   return Transition::To(ICOState::FINISHED_RESOURCE, 0);
 }
 
 LexerTransition<ICOState>
 nsICODecoder::FinishResource()
 {
-  // Make sure the actual size of the resource matches the size in the directory
-  // entry. If not, we consider the image corrupt.
-  if (mContainedDecoder->HasSize() &&
-      mContainedDecoder->Size() != GetRealSize()) {
+  MOZ_ASSERT(mDirEntry);
+
+  // We have received all of the data required by the PNG/BMP decoder so
+  // flushing here guarantees the decode has finished.
+  if (!FlushContainedDecoder()) {
     return Transition::TerminateFailure();
   }
 
+  MOZ_ASSERT(mContainedDecoder->GetDecodeDone());
+
+  // If it is a metadata decode, all we were trying to get was the size
+  // information missing from the dir entry.
+  if (mContainedDecoder->IsMetadataDecode()) {
+    if (mContainedDecoder->HasSize()) {
+      mDirEntry->mSize = mContainedDecoder->Size();
+    }
+    return Transition::To(ICOState::ITERATE_UNSIZED_DIR_ENTRY, 0);
+  }
+
+  // Raymond Chen says that 32bpp only are valid PNG ICOs
+  // http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
+  if (!mContainedDecoder->IsValidICOResource()) {
+    return Transition::TerminateFailure();
+  }
+
+  // This size from the resource should match that from the dir entry.
+  MOZ_ASSERT_IF(mContainedDecoder->HasSize(),
+                mContainedDecoder->Size() == mDirEntry->mSize);
+
   // Finalize the frame which we deferred to ensure we could modify the final
   // result (e.g. to apply the BMP mask).
   MOZ_ASSERT(!mContainedDecoder->GetFinalizeFrames());
   if (mCurrentFrame) {
     mCurrentFrame->FinalizeSurface();
   }
 
   return Transition::TerminateSuccess();
@@ -612,28 +653,28 @@ nsICODecoder::DoDecode(SourceBufferItera
 
   return mLexer.Lex(aIterator, aOnResume,
                     [=](ICOState aState, const char* aData, size_t aLength) {
     switch (aState) {
       case ICOState::HEADER:
         return ReadHeader(aData);
       case ICOState::DIR_ENTRY:
         return ReadDirEntry(aData);
+      case ICOState::FINISHED_DIR_ENTRY:
+        return FinishDirEntry();
+      case ICOState::ITERATE_UNSIZED_DIR_ENTRY:
+        return IterateUnsizedDirEntry();
       case ICOState::SKIP_TO_RESOURCE:
         return Transition::ContinueUnbuffered(ICOState::SKIP_TO_RESOURCE);
       case ICOState::FOUND_RESOURCE:
-        return Transition::To(ICOState::SNIFF_RESOURCE, PNGSIGNATURESIZE);
+        return Transition::To(ICOState::SNIFF_RESOURCE, BITMAPINFOSIZE);
       case ICOState::SNIFF_RESOURCE:
         return SniffResource(aData);
-      case ICOState::READ_PNG:
-        return ReadPNG(aData, aLength);
-      case ICOState::READ_BIH:
-        return ReadBIH(aData);
-      case ICOState::READ_BMP:
-        return ReadBMP(aData, aLength);
+      case ICOState::READ_RESOURCE:
+        return ReadResource();
       case ICOState::PREPARE_FOR_MASK:
         return PrepareForMask();
       case ICOState::READ_MASK_ROW:
         return ReadMaskRow(aData);
       case ICOState::FINISH_MASK:
         return FinishMask();
       case ICOState::SKIP_MASK:
         return Transition::ContinueUnbuffered(ICOState::SKIP_MASK);
@@ -641,31 +682,26 @@ nsICODecoder::DoDecode(SourceBufferItera
         return FinishResource();
       default:
         MOZ_CRASH("Unknown ICOState");
     }
   });
 }
 
 bool
-nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
+nsICODecoder::FlushContainedDecoder()
 {
   MOZ_ASSERT(mContainedDecoder);
-  MOZ_ASSERT(mContainedSourceBuffer);
-
-  // Append the provided data to the SourceBuffer that the contained decoder is
-  // reading from.
-  mContainedSourceBuffer->Append(aBuffer, aCount);
 
   bool succeeded = true;
 
-  // Write to the contained decoder. If we run out of data, the ICO decoder will
-  // get resumed when there's more data available, as usual, so we don't need
-  // the contained decoder to get resumed too. To avoid that, we provide an
-  // IResumable which just does nothing.
+  // If we run out of data, the ICO decoder will get resumed when there's more
+  // data available, as usual, so we don't need the contained decoder to get
+  // resumed too. To avoid that, we provide an IResumable which just does
+  // nothing. All the caller needs to do is flush when there is new data.
   LexerResult result = mContainedDecoder->Decode();
   if (result == LexerResult(TerminalState::FAILURE)) {
     succeeded = false;
   }
 
   MOZ_ASSERT(result != LexerResult(Yield::OUTPUT_AVAILABLE),
              "Unexpected yield");
 
--- a/image/decoders/nsICODecoder.h
+++ b/image/decoders/nsICODecoder.h
@@ -19,117 +19,84 @@ namespace mozilla {
 namespace image {
 
 class RasterImage;
 
 enum class ICOState
 {
   HEADER,
   DIR_ENTRY,
+  FINISHED_DIR_ENTRY,
+  ITERATE_UNSIZED_DIR_ENTRY,
   SKIP_TO_RESOURCE,
   FOUND_RESOURCE,
   SNIFF_RESOURCE,
-  READ_PNG,
-  READ_BIH,
-  READ_BMP,
+  READ_RESOURCE,
   PREPARE_FOR_MASK,
   READ_MASK_ROW,
   FINISH_MASK,
   SKIP_MASK,
   FINISHED_RESOURCE
 };
 
 class nsICODecoder : public Decoder
 {
 public:
   virtual ~nsICODecoder() { }
 
-  /// @return the width of the icon directory entry @aEntry.
-  static uint32_t GetRealWidth(const IconDirEntry& aEntry)
-  {
-    return aEntry.mWidth == 0 ? 256 : aEntry.mWidth;
-  }
-
-  /// @return the width of the selected directory entry (mDirEntry).
-  uint32_t GetRealWidth() const { return GetRealWidth(mDirEntry); }
-
-  /// @return the height of the icon directory entry @aEntry.
-  static uint32_t GetRealHeight(const IconDirEntry& aEntry)
-  {
-    return aEntry.mHeight == 0 ? 256 : aEntry.mHeight;
-  }
-
-  /// @return the height of the selected directory entry (mDirEntry).
-  uint32_t GetRealHeight() const { return GetRealHeight(mDirEntry); }
-
-  /// @return the size of the selected directory entry (mDirEntry).
-  gfx::IntSize GetRealSize() const
-  {
-    return gfx::IntSize(GetRealWidth(), GetRealHeight());
-  }
-
   /// @return The offset from the beginning of the ICO to the first resource.
   size_t FirstResourceOffset() const;
 
   LexerResult DoDecode(SourceBufferIterator& aIterator,
                        IResumable* aOnResume) override;
   nsresult FinishInternal() override;
   nsresult FinishWithErrorInternal() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsICODecoder(RasterImage* aImage);
 
-  // Writes to the contained decoder and sets the appropriate errors
-  // Returns true if there are no errors.
-  bool WriteToContainedDecoder(const char* aBuffer, uint32_t aCount);
+  // Flushes the contained decoder to read all available data and sets the
+  // appropriate errors. Returns true if there are no errors.
+  bool FlushContainedDecoder();
 
   // Gets decoder state from the contained decoder so it's visible externally.
   nsresult GetFinalStateFromContainedDecoder();
 
-  /**
-   * Verifies that the width and height values in @aBIH are valid and match the
-   * values we read from the ICO directory entry. If everything looks OK, the
-   * height value in @aBIH is updated to compensate for the AND mask, which the
-   * underlying BMP decoder doesn't know about.
-   *
-   * @return true if the width and height values in @aBIH are valid and correct.
-   */
-  bool CheckAndFixBitmapSize(int8_t* aBIH);
-
   // Obtains the number of colors from the BPP, mBPP must be filled in
   uint16_t GetNumColors();
 
   LexerTransition<ICOState> ReadHeader(const char* aData);
   LexerTransition<ICOState> ReadDirEntry(const char* aData);
+  LexerTransition<ICOState> IterateUnsizedDirEntry();
+  LexerTransition<ICOState> FinishDirEntry();
   LexerTransition<ICOState> SniffResource(const char* aData);
-  LexerTransition<ICOState> ReadPNG(const char* aData, uint32_t aLen);
+  LexerTransition<ICOState> ReadResource();
   LexerTransition<ICOState> ReadBIH(const char* aData);
-  LexerTransition<ICOState> ReadBMP(const char* aData, uint32_t aLen);
   LexerTransition<ICOState> PrepareForMask();
   LexerTransition<ICOState> ReadMaskRow(const char* aData);
   LexerTransition<ICOState> FinishMask();
   LexerTransition<ICOState> FinishResource();
 
+  struct IconDirEntryEx : public IconDirEntry {
+    gfx::IntSize mSize;
+  };
+
   StreamingLexer<ICOState, 32> mLexer; // The lexer.
   RefPtr<Decoder> mContainedDecoder; // Either a BMP or PNG decoder.
-  RefPtr<SourceBuffer> mContainedSourceBuffer;  // SourceBuffer for mContainedDecoder.
-  UniquePtr<uint8_t[]> mMaskBuffer;    // A temporary buffer for the alpha mask.
-  char mBIHraw[bmp::InfoHeaderLength::WIN_ICO]; // The bitmap information header.
-  IconDirEntry mDirEntry;              // The dir entry for the selected resource.
-  gfx::IntSize mBiggestResourceSize;   // Used to select the intrinsic size.
-  gfx::IntSize mBiggestResourceHotSpot; // Used to select the intrinsic size.
-  uint16_t mBiggestResourceColorDepth; // Used to select the intrinsic size.
-  int32_t mBestResourceDelta;          // Used to select the best resource.
-  uint16_t mBestResourceColorDepth;    // Used to select the best resource.
-  uint16_t mNumIcons; // Stores the number of icons in the ICO file.
-  uint16_t mCurrIcon; // Stores the current dir entry index we are processing.
-  uint16_t mBPP;      // The BPP of the resource we're decoding.
+  Maybe<SourceBufferIterator> mReturnIterator; // Iterator to save return point.
+  UniquePtr<uint8_t[]> mMaskBuffer; // A temporary buffer for the alpha mask.
+  nsTArray<IconDirEntryEx> mDirEntries; // Valid dir entries with a size.
+  nsTArray<IconDirEntryEx> mUnsizedDirEntries; // Dir entries without a size.
+  IconDirEntryEx* mDirEntry; // The dir entry for the selected resource.
+  uint16_t mNumIcons;     // Stores the number of icons in the ICO file.
+  uint16_t mCurrIcon;     // Stores the current dir entry index we are processing.
+  uint16_t mBPP;          // The BPP of the resource we're decoding.
   uint32_t mMaskRowSize;  // The size in bytes of each row in the BMP alpha mask.
   uint32_t mCurrMaskLine; // The line of the BMP alpha mask we're processing.
   bool mIsCursor;         // Is this ICO a cursor?
   bool mHasMaskAlpha;     // Did the BMP alpha mask have any transparency?
 };
 
 } // namespace image
 } // namespace mozilla
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -1085,17 +1085,17 @@ nsPNGDecoder::warning_callback(png_struc
 
 Maybe<Telemetry::HistogramID>
 nsPNGDecoder::SpeedHistogram() const
 {
   return Some(Telemetry::IMAGE_DECODE_SPEED_PNG);
 }
 
 bool
-nsPNGDecoder::IsValidICO() const
+nsPNGDecoder::IsValidICOResource() const
 {
   // Only 32-bit RGBA PNGs are valid ICO resources; see here:
   //   http://blogs.msdn.com/b/oldnewthing/archive/2010/10/22/10079192.aspx
 
   // If there are errors in the call to png_get_IHDR, the error_callback in
   // nsPNGDecoder.cpp is called.  In this error callback we do a longjmp, so
   // we need to save the jump buffer here. Otherwise we'll end up without a
   // proper callstack.
--- a/image/decoders/nsPNGDecoder.h
+++ b/image/decoders/nsPNGDecoder.h
@@ -18,17 +18,17 @@ namespace image {
 class RasterImage;
 
 class nsPNGDecoder : public Decoder
 {
 public:
   virtual ~nsPNGDecoder();
 
   /// @return true if this PNG is a valid ICO resource.
-  bool IsValidICO() const;
+  bool IsValidICOResource() const override;
 
 protected:
   nsresult InitInternal() override;
   nsresult FinishInternal() override;
   LexerResult DoDecode(SourceBufferIterator& aIterator,
                        IResumable* aOnResume) override;
 
   Maybe<Telemetry::HistogramID> SpeedHistogram() const override;
--- a/image/test/gtest/Common.cpp
+++ b/image/test/gtest/Common.cpp
@@ -21,16 +21,51 @@
 namespace mozilla {
 namespace image {
 
 using namespace gfx;
 
 using std::abs;
 using std::vector;
 
+static bool sImageLibInitialized = false;
+
+AutoInitializeImageLib::AutoInitializeImageLib()
+{
+  if (MOZ_LIKELY(sImageLibInitialized)) {
+    return;
+  }
+
+  EXPECT_TRUE(NS_IsMainThread());
+  sImageLibInitialized = true;
+
+  // Force sRGB to be consistent with reftests.
+  Preferences::SetBool("gfx.color_management.force_srgb", true);
+
+  // Ensure that ImageLib services are initialized.
+  nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1");
+  EXPECT_TRUE(imgTools != nullptr);
+
+  // Ensure gfxPlatform is initialized.
+  gfxPlatform::GetPlatform();
+
+  // Depending on initialization order, it is possible that our pref changes
+  // have not taken effect yet because there are pending gfx-related events on
+  // the main thread.
+  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+  EXPECT_TRUE(mainThread != nullptr);
+
+  bool processed;
+  do {
+    processed = false;
+    nsresult rv = mainThread->ProcessNextEvent(false, &processed);
+    EXPECT_TRUE(NS_SUCCEEDED(rv));
+  } while (processed);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // General Helpers
 ///////////////////////////////////////////////////////////////////////////////
 
 // These macros work like gtest's ASSERT_* macros, except that they can be used
 // in functions that return values.
 #define ASSERT_TRUE_OR_RETURN(e, rv) \
   EXPECT_TRUE(e);                    \
@@ -674,16 +709,28 @@ ImageTestCase DownscaledTransparentICOWi
                        TEST_CASE_IS_TRANSPARENT | TEST_CASE_IGNORE_OUTPUT);
 }
 
 ImageTestCase TruncatedSmallGIFTestCase()
 {
   return ImageTestCase("green-1x1-truncated.gif", "image/gif", IntSize(1, 1));
 }
 
+ImageTestCase LargeICOWithBMPTestCase()
+{
+  return ImageTestCase("green-large-bmp.ico", "image/x-icon", IntSize(256, 256),
+                       TEST_CASE_IS_TRANSPARENT);
+}
+
+ImageTestCase LargeICOWithPNGTestCase()
+{
+  return ImageTestCase("green-large-png.ico", "image/x-icon", IntSize(512, 512),
+                       TEST_CASE_IS_TRANSPARENT);
+}
+
 ImageTestCase GreenMultipleSizesICOTestCase()
 {
   return ImageTestCase("green-multiple-sizes.ico", "image/x-icon",
                        IntSize(256, 256));
 }
 
 } // namespace image
 } // namespace mozilla
--- a/image/test/gtest/Common.h
+++ b/image/test/gtest/Common.h
@@ -102,24 +102,20 @@ struct BGRAColor
 
 /**
  * A RAII class that ensure that ImageLib services are available. Any tests that
  * require ImageLib to be initialized (for example, any test that uses the
  * SurfaceCache; see image::EnsureModuleInitialized() for the full list) can
  * use this class to ensure that ImageLib services are available. Failure to do
  * so can result in strange, non-deterministic failures.
  */
-struct AutoInitializeImageLib
+class AutoInitializeImageLib
 {
-  AutoInitializeImageLib()
-  {
-    // Ensure that ImageLib services are initialized.
-    nsCOMPtr<imgITools> imgTools = do_CreateInstance("@mozilla.org/image/tools;1");
-    EXPECT_TRUE(imgTools != nullptr);
-  }
+public:
+  AutoInitializeImageLib();
 };
 
 /// Loads a file from the current directory. @return an nsIInputStream for it.
 already_AddRefed<nsIInputStream> LoadFile(const char* aRelativePath);
 
 /**
  * @returns true if every pixel of @aSurface is @aColor.
  *
@@ -409,14 +405,16 @@ ImageTestCase DownscaledGIFTestCase();
 ImageTestCase DownscaledJPGTestCase();
 ImageTestCase DownscaledBMPTestCase();
 ImageTestCase DownscaledICOTestCase();
 ImageTestCase DownscaledIconTestCase();
 ImageTestCase DownscaledTransparentICOWithANDMaskTestCase();
 
 ImageTestCase TruncatedSmallGIFTestCase();
 
+ImageTestCase LargeICOWithBMPTestCase();
+ImageTestCase LargeICOWithPNGTestCase();
 ImageTestCase GreenMultipleSizesICOTestCase();
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_test_gtest_Common_h
--- a/image/test/gtest/TestDecoders.cpp
+++ b/image/test/gtest/TestDecoders.cpp
@@ -668,16 +668,36 @@ TEST_F(ImageDecoders, AnimatedGIFWithExt
   EXPECT_TRUE(bool(result.Surface()));
 }
 
 TEST_F(ImageDecoders, TruncatedSmallGIFSingleChunk)
 {
   CheckDecoderSingleChunk(TruncatedSmallGIFTestCase());
 }
 
+TEST_F(ImageDecoders, LargeICOWithBMPSingleChunk)
+{
+  CheckDecoderSingleChunk(LargeICOWithBMPTestCase());
+}
+
+TEST_F(ImageDecoders, LargeICOWithBMPMultiChunk)
+{
+  CheckDecoderMultiChunk(LargeICOWithBMPTestCase());
+}
+
+TEST_F(ImageDecoders, LargeICOWithPNGSingleChunk)
+{
+  CheckDecoderSingleChunk(LargeICOWithPNGTestCase());
+}
+
+TEST_F(ImageDecoders, LargeICOWithPNGMultiChunk)
+{
+  CheckDecoderMultiChunk(LargeICOWithPNGTestCase());
+}
+
 TEST_F(ImageDecoders, MultipleSizesICOSingleChunk)
 {
   ImageTestCase testCase = GreenMultipleSizesICOTestCase();
 
   // Create an image.
   RefPtr<Image> image =
     ImageFactory::CreateAnonymousImage(nsDependentCString(testCase.mMimeType));
   ASSERT_TRUE(!image->HasError());
--- a/image/test/gtest/TestMetadata.cpp
+++ b/image/test/gtest/TestMetadata.cpp
@@ -85,17 +85,22 @@ CheckMetadata(const ImageTestCase& aTest
 
   EXPECT_TRUE(decoder->GetDecodeDone() && !decoder->HasError());
 
   // Check that we got the expected metadata.
   EXPECT_TRUE(metadataProgress & FLAG_SIZE_AVAILABLE);
 
   IntSize metadataSize = decoder->Size();
   EXPECT_EQ(aTestCase.mSize.width, metadataSize.width);
-  EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
+  if (aBMPWithinICO == BMPWithinICO::YES) {
+    // Half the data is considered to be part of the AND mask if embedded
+    EXPECT_EQ(aTestCase.mSize.height / 2, metadataSize.height);
+  } else {
+    EXPECT_EQ(aTestCase.mSize.height, metadataSize.height);
+  }
 
   bool expectTransparency = aBMPWithinICO == BMPWithinICO::YES
                           ? true
                           : bool(aTestCase.mFlags & TEST_CASE_IS_TRANSPARENT);
   EXPECT_EQ(expectTransparency, bool(metadataProgress & FLAG_HAS_TRANSPARENCY));
 
   EXPECT_EQ(bool(aTestCase.mFlags & TEST_CASE_IS_ANIMATED),
             bool(metadataProgress & FLAG_IS_ANIMATED));
--- a/image/test/gtest/TestSourceBuffer.cpp
+++ b/image/test/gtest/TestSourceBuffer.cpp
@@ -803,8 +803,60 @@ TEST_F(ImageSourceBuffer, ExpectLengthDo
 
   // Check that we can't advance.
   CheckIteratorMustWait(iterator, mExpectNoResume);
 
   // Call ExpectLength(). If this triggers a resume, |mExpectNoResume| will
   // ensure that the test fails.
   mSourceBuffer->ExpectLength(1000);
 }
+
+TEST_F(ImageSourceBuffer, CompleteSuccessWithSameReadLength)
+{
+  SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
+
+  // Write a single byte to the buffer and complete the buffer. (We have to
+  // write at least one byte because completing a zero length buffer always
+  // fails; see the ZeroLengthBufferAlwaysFails test.)
+  CheckedAppendToBuffer(mData, 1);
+  CheckedCompleteBuffer(iterator, 1);
+
+  // We should be able to advance once (to read the single byte) and then should
+  // reach the COMPLETE state with a successful status.
+  CheckedAdvanceIterator(iterator, 1);
+  CheckIteratorIsComplete(iterator, 1);
+}
+
+TEST_F(ImageSourceBuffer, CompleteSuccessWithSmallerReadLength)
+{
+  // Create an iterator limited to one byte.
+  SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
+
+  // Write two bytes to the buffer and complete the buffer. (We have to
+  // write at least one byte because completing a zero length buffer always
+  // fails; see the ZeroLengthBufferAlwaysFails test.)
+  CheckedAppendToBuffer(mData, 2);
+  CheckedCompleteBuffer(iterator, 2);
+
+  // We should be able to advance once (to read the single byte) and then should
+  // reach the COMPLETE state with a successful status, because our iterator is
+  // limited to a single byte, rather than the full length.
+  CheckedAdvanceIterator(iterator, 1);
+  CheckIteratorIsComplete(iterator, 1);
+}
+
+TEST_F(ImageSourceBuffer, CompleteSuccessWithGreaterReadLength)
+{
+  // Create an iterator limited to one byte.
+  SourceBufferIterator iterator = mSourceBuffer->Iterator(2);
+
+  // Write a single byte to the buffer and complete the buffer. (We have to
+  // write at least one byte because completing a zero length buffer always
+  // fails; see the ZeroLengthBufferAlwaysFails test.)
+  CheckedAppendToBuffer(mData, 1);
+  CheckedCompleteBuffer(iterator, 1);
+
+  // We should be able to advance once (to read the single byte) and then should
+  // reach the COMPLETE state with a successful status. Our iterator lets us
+  // read more but the underlying buffer has been completed.
+  CheckedAdvanceIterator(iterator, 1);
+  CheckIteratorIsComplete(iterator, 1);
+}
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3962cea29de37892ddda2b7dae390bd2865f784b
GIT binary patch
literal 270398
zc%1FXu?@f=6hlE1F#|Ov`!AS)ewR!T>E1P#leV2&X4dQ(d#^f@tNyCz(+vOs00000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000000000000
z0000000000000000000000000000000000000000000000002+B1w`YNs=T<k|arz
Ge%}KR#vZr;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..27b9f43cdd6f48e3f581455d8df10f3dbf0f0d30
GIT binary patch
literal 341
zc${NkU<5)CU}R8Wum>{5fb7lyKX+a(DJ~$B*VDr#h=GBL0fZTufz-KeZaaY#OS+@4
zBLl<6e(pbstU$g(vPY0F14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a><Y92
zO0Wg^gt#*NXZZjBfBxQxK#=W8-tI1p|Cw~}0Xgg?p1!W^57|Y<%{iRswVVVBnR&W6
zhE&{odyo<2+9rkn>+?JuAP&b3YNVThCMTB$ECF&<OI#yLQW8s2t&)pUffR$0fuXss
zfw``cVTh5jm8q$ffrYMtiIsst-x0Z6C>nC}Q!>*kaclTLb6Fb5K2KLamvv4FO#s$r
BOe+8Y
--- a/image/test/gtest/moz.build
+++ b/image/test/gtest/moz.build
@@ -42,16 +42,18 @@ TEST_HARNESS_FILES.gtest += [
     'downscaled.ico',
     'downscaled.icon',
     'downscaled.jpg',
     'downscaled.png',
     'first-frame-green.gif',
     'first-frame-green.png',
     'first-frame-padding.gif',
     'green-1x1-truncated.gif',
+    'green-large-bmp.ico',
+    'green-large-png.ico',
     'green-multiple-sizes.ico',
     'green.bmp',
     'green.gif',
     'green.ico',
     'green.icon',
     'green.jpg',
     'green.png',
     'invalid-truncated-metadata.bmp',
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -251,16 +251,18 @@ BaselineCompiler::compile()
         BaselineICEntry* entryAddr = &(baselineScript->icEntry(icEntry));
         Assembler::PatchDataWithValueCheck(CodeLocationLabel(code, label),
                                            ImmPtr(entryAddr),
                                            ImmPtr((void*)-1));
     }
 
     if (modifiesArguments_)
         baselineScript->setModifiesArguments();
+    if (analysis_.usesEnvironmentChain())
+        baselineScript->setUsesEnvironmentChain();
 
 #ifdef JS_TRACE_LOGGING
     // Initialize the tracelogger instrumentation.
     baselineScript->initTraceLogger(cx->runtime(), script, traceLoggerToggleOffsets_);
 #endif
 
     uint32_t* bytecodeMap = baselineScript->bytecodeTypeMap();
     FillBytecodeTypeMap(script, bytecodeMap);
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -276,19 +276,24 @@ BaselineInspector::maybeInfoForPropertyO
 }
 
 ICStub*
 BaselineInspector::monomorphicStub(jsbytecode* pc)
 {
     if (!hasBaselineScript())
         return nullptr;
 
-    const ICEntry& entry = icEntryFromPC(pc);
+    // IonBuilder::analyzeNewLoopTypes may call this (via expectedResultType
+    // below) on code that's unreachable, according to BytecodeAnalysis. Use
+    // maybeICEntryFromPC to handle this.
+    const ICEntry* entry = maybeICEntryFromPC(pc);
+    if (!entry)
+        return nullptr;
 
-    ICStub* stub = entry.firstStub();
+    ICStub* stub = entry->firstStub();
     ICStub* next = stub->next();
 
     if (!next || !next->isFallback())
         return nullptr;
 
     return stub;
 }
 
@@ -311,17 +316,19 @@ BaselineInspector::dimorphicStub(jsbytec
     *psecond = next;
     return true;
 }
 
 MIRType
 BaselineInspector::expectedResultType(jsbytecode* pc)
 {
     // Look at the IC entries for this op to guess what type it will produce,
-    // returning MIRType::None otherwise.
+    // returning MIRType::None otherwise. Note that IonBuilder may call this
+    // for bytecode ops that are unreachable and don't have a Baseline IC, see
+    // comment in monomorphicStub.
 
     ICStub* stub = monomorphicStub(pc);
     if (!stub)
         return MIRType::None;
 
     switch (stub->kind()) {
       case ICStub::BinaryArith_Int32:
         if (stub->toBinaryArith_Int32()->allowDouble())
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -72,16 +72,28 @@ class BaselineInspector
         MOZ_ASSERT(isValidPC(pc));
         BaselineICEntry& ent =
             baselineScript()->icEntryFromPCOffset(script->pcToOffset(pc), prevLookedUpEntry);
         MOZ_ASSERT(ent.isForOp());
         prevLookedUpEntry = &ent;
         return ent;
     }
 
+    BaselineICEntry* maybeICEntryFromPC(jsbytecode* pc) {
+        MOZ_ASSERT(hasBaselineScript());
+        MOZ_ASSERT(isValidPC(pc));
+        BaselineICEntry* ent =
+            baselineScript()->maybeICEntryFromPCOffset(script->pcToOffset(pc), prevLookedUpEntry);
+        if (!ent)
+            return nullptr;
+        MOZ_ASSERT(ent->isForOp());
+        prevLookedUpEntry = ent;
+        return ent;
+    }
+
     template <typename ICInspectorType>
     ICInspectorType makeICInspector(jsbytecode* pc, ICStub::Kind expectedFallbackKind) {
         BaselineICEntry* ent = nullptr;
         if (hasBaselineScript()) {
             ent = &icEntryFromPC(pc);
             MOZ_ASSERT(ent->fallbackStub()->kind() == expectedFallbackKind);
         }
         return ICInspectorType(this, pc, ent);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -640,89 +640,105 @@ BaselineScript::icEntryFromReturnOffset(
                        &loc);
 
     MOZ_ASSERT(found);
     MOZ_ASSERT(loc < numICEntries());
     MOZ_ASSERT(icEntry(loc).returnOffset().offset() == returnOffset.offset());
     return icEntry(loc);
 }
 
-static inline size_t
-ComputeBinarySearchMid(BaselineScript* baseline, uint32_t pcOffset)
+static inline bool
+ComputeBinarySearchMid(BaselineScript* baseline, uint32_t pcOffset, size_t* loc)
 {
-    size_t loc;
-    BinarySearchIf(ICEntries(baseline), 0, baseline->numICEntries(),
-                   [pcOffset](BaselineICEntry& entry) {
-                       uint32_t entryOffset = entry.pcOffset();
-                       if (pcOffset < entryOffset)
-                           return -1;
-                       if (entryOffset < pcOffset)
-                           return 1;
-                       return 0;
-                   },
-                   &loc);
-    return loc;
+    return BinarySearchIf(ICEntries(baseline), 0, baseline->numICEntries(),
+                          [pcOffset](BaselineICEntry& entry) {
+                              uint32_t entryOffset = entry.pcOffset();
+                              if (pcOffset < entryOffset)
+                                  return -1;
+                              if (entryOffset < pcOffset)
+                                  return 1;
+                              return 0;
+                          },
+                          loc);
 }
 
 uint8_t*
 BaselineScript::returnAddressForIC(const BaselineICEntry& ent)
 {
     return method()->raw() + ent.returnOffset().offset();
 }
 
-BaselineICEntry&
-BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+BaselineICEntry*
+BaselineScript::maybeICEntryFromPCOffset(uint32_t pcOffset)
 {
     // Multiple IC entries can have the same PC offset, but this method only looks for
     // those which have isForOp() set.
-    size_t mid = ComputeBinarySearchMid(this, pcOffset);
+    size_t mid;
+    if (!ComputeBinarySearchMid(this, pcOffset, &mid))
+        return nullptr;
 
     // Found an IC entry with a matching PC offset.  Search backward, and then
     // forward from this IC entry, looking for one with the same PC offset which
     // has isForOp() set.
     for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) {
         if (icEntry(i).isForOp())
-            return icEntry(i);
+            return &icEntry(i);
     }
     for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
         if (icEntry(i).isForOp())
-            return icEntry(i);
+            return &icEntry(i);
     }
-    MOZ_CRASH("Invalid PC offset for IC entry.");
+    return nullptr;
 }
 
 BaselineICEntry&
-BaselineScript::icEntryFromPCOffset(uint32_t pcOffset, BaselineICEntry* prevLookedUpEntry)
+BaselineScript::icEntryFromPCOffset(uint32_t pcOffset)
+{
+    BaselineICEntry* entry = maybeICEntryFromPCOffset(pcOffset);
+    MOZ_RELEASE_ASSERT(entry);
+    return *entry;
+}
+
+BaselineICEntry*
+BaselineScript::maybeICEntryFromPCOffset(uint32_t pcOffset, BaselineICEntry* prevLookedUpEntry)
 {
     // Do a linear forward search from the last queried PC offset, or fallback to a
     // binary search if the last offset is too far away.
     if (prevLookedUpEntry && pcOffset >= prevLookedUpEntry->pcOffset() &&
         (pcOffset - prevLookedUpEntry->pcOffset()) <= 10)
     {
         BaselineICEntry* firstEntry = &icEntry(0);
         BaselineICEntry* lastEntry = &icEntry(numICEntries() - 1);
         BaselineICEntry* curEntry = prevLookedUpEntry;
         while (curEntry >= firstEntry && curEntry <= lastEntry) {
             if (curEntry->pcOffset() == pcOffset && curEntry->isForOp())
-                break;
+                return curEntry;
             curEntry++;
         }
-        MOZ_ASSERT(curEntry->pcOffset() == pcOffset && curEntry->isForOp());
-        return *curEntry;
+        return nullptr;
     }
 
-    return icEntryFromPCOffset(pcOffset);
+    return maybeICEntryFromPCOffset(pcOffset);
+}
+
+BaselineICEntry&
+BaselineScript::icEntryFromPCOffset(uint32_t pcOffset, BaselineICEntry* prevLookedUpEntry)
+{
+    BaselineICEntry* entry = maybeICEntryFromPCOffset(pcOffset, prevLookedUpEntry);
+    MOZ_RELEASE_ASSERT(entry);
+    return *entry;
 }
 
 BaselineICEntry&
 BaselineScript::callVMEntryFromPCOffset(uint32_t pcOffset)
 {
     // Like icEntryFromPCOffset, but only looks for the fake ICEntries
     // inserted by VM calls.
-    size_t mid = ComputeBinarySearchMid(this, pcOffset);
+    size_t mid;
+    MOZ_ALWAYS_TRUE(ComputeBinarySearchMid(this, pcOffset, &mid));
 
     for (size_t i = mid; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i--) {
         if (icEntry(i).kind() == ICEntry::Kind_CallVM)
             return icEntry(i);
     }
     for (size_t i = mid+1; i < numICEntries() && icEntry(i).pcOffset() == pcOffset; i++) {
         if (icEntry(i).kind() == ICEntry::Kind_CallVM)
             return icEntry(i);
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -189,17 +189,22 @@ struct BaselineScript
         HAS_DEBUG_INSTRUMENTATION = 1 << 3,
 
         // Flag set if this script has ever been Ion compiled, either directly
         // or inlined into another script. This is cleared when the script's
         // type information or caches are cleared.
         ION_COMPILED_OR_INLINED = 1 << 4,
 
         // Flag is set if this script has profiling instrumentation turned on.
-        PROFILER_INSTRUMENTATION_ON = 1 << 5
+        PROFILER_INSTRUMENTATION_ON = 1 << 5,
+
+        // Whether this script uses its environment chain. This is currently
+        // determined by the BytecodeAnalysis and cached on the BaselineScript
+        // for IonBuilder.
+        USES_ENVIRONMENT_CHAIN = 1 << 6,
     };
 
   private:
     uint32_t flags_;
 
   private:
     void trace(JSTracer* trc);
 
@@ -317,16 +322,23 @@ struct BaselineScript
     }
     void clearIonCompiledOrInlined() {
         flags_ &= ~ION_COMPILED_OR_INLINED;
     }
     bool ionCompiledOrInlined() const {
         return flags_ & ION_COMPILED_OR_INLINED;
     }
 
+    void setUsesEnvironmentChain() {
+        flags_ |= USES_ENVIRONMENT_CHAIN;
+    }
+    bool usesEnvironmentChain() const {
+        return flags_ & USES_ENVIRONMENT_CHAIN;
+    }
+
     uint32_t prologueOffset() const {
         return prologueOffset_;
     }
     uint8_t* prologueEntryAddr() const {
         return method_->raw() + prologueOffset_;
     }
 
     uint32_t epilogueOffset() const {
@@ -374,16 +386,20 @@ struct BaselineScript
         MOZ_ASSERT(!templateEnv_);
         templateEnv_ = templateEnv;
     }
 
     bool containsCodeAddress(uint8_t* addr) const {
         return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize();
     }
 
+    BaselineICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset);
+    BaselineICEntry* maybeICEntryFromPCOffset(uint32_t pcOffset,
+                                              BaselineICEntry* prevLookedUpEntry);
+
     BaselineICEntry& icEntry(size_t index);
     BaselineICEntry& icEntryFromReturnOffset(CodeOffset returnOffset);
     BaselineICEntry& icEntryFromPCOffset(uint32_t pcOffset);
     BaselineICEntry& icEntryFromPCOffset(uint32_t pcOffset, BaselineICEntry* prevLookedUpEntry);
     BaselineICEntry& callVMEntryFromPCOffset(uint32_t pcOffset);
     BaselineICEntry& stackCheckICEntry(bool earlyCheck);
     BaselineICEntry& warmupCountICEntry();
     BaselineICEntry& icEntryFromReturnAddress(uint8_t* returnAddr);
--- a/js/src/jit/BytecodeAnalysis.cpp
+++ b/js/src/jit/BytecodeAnalysis.cpp
@@ -12,19 +12,17 @@
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
 BytecodeAnalysis::BytecodeAnalysis(TempAllocator& alloc, JSScript* script)
   : script_(script),
     infos_(alloc),
-    usesEnvironmentChain_(false),
-    hasTryFinally_(false),
-    hasSetArg_(false)
+    usesEnvironmentChain_(false)
 {
 }
 
 // Bytecode range containing only catch or finally code.
 struct CatchFinallyRange
 {
     uint32_t start; // Inclusive.
     uint32_t end;   // Exclusive.
@@ -134,16 +132,22 @@ BytecodeAnalysis::init(TempAllocator& al
             MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY);
 
             jsbytecode* endOfTry = pc + GetSrcNoteOffset(sn, 0);
             MOZ_ASSERT(JSOp(*endOfTry) == JSOP_GOTO);
 
             jsbytecode* afterTry = endOfTry + GET_JUMP_OFFSET(endOfTry);
             MOZ_ASSERT(afterTry > endOfTry);
 
+            // Ensure the code following the try-block is always marked as
+            // reachable, to simplify Ion's ControlFlowGenerator.
+            uint32_t afterTryOffset = script_->pcToOffset(afterTry);
+            infos_[afterTryOffset].init(stackDepth);
+            infos_[afterTryOffset].jumpTarget = true;
+
             // Pop CatchFinallyRanges that are no longer needed.
             while (!catchFinallyRanges.empty() && catchFinallyRanges.back().end <= offset)
                 catchFinallyRanges.popBack();
 
             CatchFinallyRange range(script_->pcToOffset(endOfTry), script_->pcToOffset(afterTry));
             if (!catchFinallyRanges.append(range))
                 return false;
             break;
@@ -175,24 +179,16 @@ BytecodeAnalysis::init(TempAllocator& al
 
           case JSOP_GETGNAME:
           case JSOP_SETGNAME:
           case JSOP_STRICTSETGNAME:
             if (script_->hasNonSyntacticScope())
                 usesEnvironmentChain_ = true;
             break;
 
-          case JSOP_FINALLY:
-            hasTryFinally_ = true;
-            break;
-
-          case JSOP_SETARG:
-            hasSetArg_ = true;
-            break;
-
           default:
             break;
         }
 
         bool jump = IsJumpOpcode(op);
         if (jump) {
             // Case instructions do not push the lvalue back when branching.
             unsigned newStackDepth = stackDepth;
--- a/js/src/jit/BytecodeAnalysis.h
+++ b/js/src/jit/BytecodeAnalysis.h
@@ -35,17 +35,16 @@ struct BytecodeInfo
 
 class BytecodeAnalysis
 {
     JSScript* script_;
     Vector<BytecodeInfo, 0, JitAllocPolicy> infos_;
 
     bool usesEnvironmentChain_;
     bool hasTryFinally_;
-    bool hasSetArg_;
 
   public:
     explicit BytecodeAnalysis(TempAllocator& alloc, JSScript* script);
 
     MOZ_MUST_USE bool init(TempAllocator& alloc, GSNCache& gsn);
 
     BytecodeInfo& info(jsbytecode* pc) {
         MOZ_ASSERT(infos_[script_->pcToOffset(pc)].initialized);
@@ -60,19 +59,15 @@ class BytecodeAnalysis
 
     bool usesEnvironmentChain() const {
         return usesEnvironmentChain_;
     }
 
     bool hasTryFinally() const {
         return hasTryFinally_;
     }
-
-    bool hasSetArg() const {
-        return hasSetArg_;
-    }
 };
 
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BytecodeAnalysis_h */
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -125,17 +125,16 @@ IonBuilder::IonBuilder(JSContext* analys
     backgroundCodegen_(nullptr),
     actionableAbortScript_(nullptr),
     actionableAbortPc_(nullptr),
     actionableAbortMessage_(nullptr),
     rootList_(nullptr),
     analysisContext(analysisContext),
     baselineFrame_(baselineFrame),
     constraints_(constraints),
-    analysis_(*temp, info->script()),
     thisTypes(nullptr),
     argTypes(nullptr),
     typeArray(nullptr),
     typeArrayHint(0),
     bytecodeTypeMap(nullptr),
     loopDepth_(loopDepth),
     blockWorklist(*temp),
     cfgCurrent(nullptr),
@@ -181,17 +180,16 @@ IonBuilder::clearForBackEnd()
 {
     MOZ_ASSERT(!analysisContext);
     baselineFrame_ = nullptr;
 
     // The caches below allocate data from the malloc heap. Release this before
     // later phases of compilation to avoid leaks, as the top level IonBuilder
     // is not explicitly destroyed. Note that builders for inner scripts are
     // constructed on the stack and will release this memory on destruction.
-    gsn.purge();
     envCoordinateNameCache.purge();
 }
 
 mozilla::GenericErrorResult<AbortReason>
 IonBuilder::abort(AbortReason r)
 {
     auto res = this->MIRGenerator::abort(r);
 # ifdef DEBUG
@@ -578,18 +576,16 @@ IonBuilder::analyzeNewLoopTypes(const CF
         if (*pc == JSOP_SETLOCAL)
             slot = info().localSlot(GET_LOCALNO(pc));
         else if (*pc == JSOP_SETARG)
             slot = info().argSlotUnchecked(GET_ARGNO(pc));
         else
             continue;
         if (slot >= info().firstStackSlot())
             continue;
-        if (!analysis().maybeInfo(pc))
-            continue;
         if (!last)
             continue;
 
         MPhi* phi = entry->getSlot(slot)->toPhi();
 
         if (*last == JSOP_POS)
             last = earlier;
 
@@ -703,19 +699,16 @@ IonBuilder::init()
     if (inlineCallInfo_) {
         // If we're inlining, the actual this/argument types are not necessarily
         // a subset of the script's observed types. |argTypes| is never accessed
         // for inlined scripts, so we just null it.
         thisTypes = inlineCallInfo_->thisArg()->resultTypeSet();
         argTypes = nullptr;
     }
 
-    if (!analysis().init(alloc(), gsn))
-        return abort(AbortReason::Alloc);
-
     // The baseline script normally has the bytecode type map, but compute
     // it ourselves if we do not have a baseline script.
     if (script()->hasBaselineScript()) {
         bytecodeTypeMap = script()->baselineScript()->bytecodeTypeMap();
     } else {
         bytecodeTypeMap = alloc_->lifoAlloc()->newArrayUninitialized<uint32_t>(script()->nTypeSets());
         if (!bytecodeTypeMap)
             return abort(AbortReason::Alloc);
@@ -729,17 +722,17 @@ AbortReasonOr<Ok>
 IonBuilder::build()
 {
     MOZ_TRY(init());
 
     if (script()->hasBaselineScript())
         script()->baselineScript()->resetMaxInliningDepth();
 
     MBasicBlock* entry;
-    MOZ_TRY_VAR(entry, newBlock(pc));
+    MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
     MOZ_TRY(setCurrentAndSpecializePhis(entry));
 
 #ifdef JS_JITSPEW
     if (info().isAnalysis()) {
         JitSpew(JitSpew_IonScripts, "Analyzing script %s:%" PRIuSIZE " (%p) %s",
                 script()->filename(), script()->lineno(), (void*)script(),
                 AnalysisModeString(info().analysisMode()));
     } else {
@@ -925,17 +918,17 @@ IonBuilder::buildInline(IonBuilder* call
 
     if (callerBuilder->failedLexicalCheck_)
         failedLexicalCheck_ = true;
 
     safeForMinorGC_ = callerBuilder->safeForMinorGC_;
 
     // Generate single entrance block.
     MBasicBlock* entry;
-    MOZ_TRY_VAR(entry, newBlock(pc));
+    MOZ_TRY_VAR(entry, newBlock(info().firstStackSlot(), pc));
     MOZ_TRY(setCurrentAndSpecializePhis(entry));
 
     current->setCallerResumePoint(callerResumePoint);
 
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock* predecessor = callerBuilder->current;
     MOZ_ASSERT(predecessor == callerResumePoint->block());
 
@@ -1118,26 +1111,36 @@ IonBuilder::initLocals()
 
     MConstant* undef = MConstant::New(alloc(), UndefinedValue());
     current->add(undef);
 
     for (uint32_t i = 0; i < info().nlocals(); i++)
         current->initSlot(info().localSlot(i), undef);
 }
 
+bool
+IonBuilder::usesEnvironmentChain()
+{
+    // We don't have a BaselineScript if we're running the arguments analysis,
+    // but it's fine to assume we always use the environment chain in this case.
+    if (info().analysisMode() == Analysis_ArgumentsUsage)
+        return true;
+    return script()->baselineScript()->usesEnvironmentChain();
+}
+
 AbortReasonOr<Ok>
 IonBuilder::initEnvironmentChain(MDefinition* callee)
 {
     MInstruction* env = nullptr;
 
     // If the script doesn't use the envchain, then it's already initialized
     // from earlier.  However, always make a env chain when |needsArgsObj| is true
     // for the script, since arguments object construction requires the env chain
     // to be passed in.
-    if (!info().needsArgsObj() && !analysis().usesEnvironmentChain())
+    if (!info().needsArgsObj() && !usesEnvironmentChain())
         return Ok();
 
     // The env chain is only tracked in scripts that have NAME opcodes which
     // will try to access the env. For other scripts, the env instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
 
     if (JSFunction* fun = info().funMaybeLazy()) {
@@ -1366,19 +1369,16 @@ GetOrCreateControlFlowGraph(TempAllocato
                             const ControlFlowGraph** cfgOut)
 {
     if (script->hasBaselineScript() && script->baselineScript()->controlFlowGraph()) {
         *cfgOut = script->baselineScript()->controlFlowGraph();
         return CFGState::Success;
     }
 
     ControlFlowGenerator cfgenerator(tempAlloc, script);
-    if (!cfgenerator.init())
-        return CFGState::Alloc;
-
     if (!cfgenerator.traverseBytecode()) {
         if (cfgenerator.aborted())
             return CFGState::Abort;
         return CFGState::Alloc;
     }
 
     // If possible cache the control flow graph on the baseline script.
     TempAllocator* graphAlloc = nullptr;
@@ -1755,18 +1755,16 @@ IonBuilder::visitControlInstruction(CFGC
         return visitTableSwitch(ins->toTableSwitch());
     }
     MOZ_CRASH("Unknown Control Instruction");
 }
 
 AbortReasonOr<Ok>
 IonBuilder::inspectOpcode(JSOp op)
 {
-    MOZ_ASSERT(analysis_.maybeInfo(pc), "Compiling unreachable op");
-
     // Add not yet implemented opcodes at the bottom of the switch!
     switch (op) {
       case JSOP_NOP:
       case JSOP_NOP_DESTRUCTURING:
       case JSOP_TRY_DESTRUCTURING_ITERCLOSE:
       case JSOP_LINENO:
       case JSOP_JUMPTARGET:
       case JSOP_LABEL:
@@ -3011,59 +3009,48 @@ IonBuilder::visitCompare(CFGCompare* com
     current = nullptr;
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::visitTry(CFGTry* try_)
 {
-    // Try-finally is not yet supported.
-    if (analysis().hasTryFinally())
-        return abort(AbortReason::Disable, "Has try-finally");
+    // We don't support try-finally. The ControlFlowGenerator should have
+    // aborted compilation in this case.
 
     // Try-catch within inline frames is not yet supported.
     MOZ_ASSERT(!isInlineBuilder());
 
     // Try-catch during the arguments usage analysis is not yet supported. Code
     // accessing the arguments within the 'catch' block is not accounted for.
     if (info().analysisMode() == Analysis_ArgumentsUsage)
         return abort(AbortReason::Disable, "Try-catch during arguments usage analysis");
 
     graph().setHasTryBlock();
 
     MBasicBlock* tryBlock;
     MOZ_TRY_VAR(tryBlock, newBlock(current, try_->tryBlock()->startPc()));
 
     blockWorklist[try_->tryBlock()->id()] = tryBlock;
 
-    // If the code after the try catch is reachable we connected it to the
-    // graph with an MGotoWithFake instruction that always jumps to the try
-    // block. This ensures the successor block always has a predecessor.
-    if (try_->codeAfterTryCatchReachable()) {
-        MBasicBlock* successor;
-        MOZ_TRY_VAR(successor, newBlock(current, try_->getSuccessor(1)->startPc()));
-
-        blockWorklist[try_->afterTryCatchBlock()->id()] = successor;
-
-        current->end(MGotoWithFake::New(alloc(), tryBlock, successor));
-
-        // The baseline compiler should not attempt to enter the catch block
-        // via OSR.
-        MOZ_ASSERT(info().osrPc() < try_->catchStartPc() ||
-                   info().osrPc() >= try_->afterTryCatchBlock()->startPc());
-
-    } else {
-        current->end(MGoto::New(alloc(), tryBlock));
-
-        // The baseline compiler should not attempt to enter the catch block
-        // via OSR or the code after the catch block.
-        // TODO: pre-existing bug. OSR after the catch block. Shouldn't happen.
-        //MOZ_ASSERT(info().osrPc() < try_->catchStartPc());
-    }
+    // Connect the code after the try-catch to the graph with an MGotoWithFake
+    // instruction that always jumps to the try block. This ensures the
+    // successor block always has a predecessor.
+    MBasicBlock* successor;
+    MOZ_TRY_VAR(successor, newBlock(current, try_->getSuccessor(1)->startPc()));
+
+    blockWorklist[try_->afterTryCatchBlock()->id()] = successor;
+
+    current->end(MGotoWithFake::New(alloc(), tryBlock, successor));
+
+    // The baseline compiler should not attempt to enter the catch block
+    // via OSR.
+    MOZ_ASSERT(info().osrPc() < try_->catchStartPc() ||
+               info().osrPc() >= try_->afterTryCatchBlock()->startPc());
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::visitReturn(CFGControlInstruction* control)
 {
     MDefinition* def;
@@ -3840,17 +3827,17 @@ IonBuilder::inlineScriptedCall(CallInfo&
             return InliningStatus_NotInlined;
         }
         return abort(AbortReason::Inlining);
     }
 
     // Create return block.
     jsbytecode* postCall = GetNextPc(pc);
     MBasicBlock* returnBlock;
-    MOZ_TRY_VAR(returnBlock, newBlock(nullptr, postCall));
+    MOZ_TRY_VAR(returnBlock, newBlock(current->stackDepth(), postCall));
     graph().addBlock(returnBlock);
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
@@ -4536,20 +4523,23 @@ IonBuilder::inlineCalls(CallInfo& callIn
     MDispatchInstruction* dispatch;
     if (maybeCache) {
         dispatch = MObjectGroupDispatch::New(alloc(), maybeCache->value(), maybeCache->propTable());
         callInfo.fun()->setImplicitlyUsedUnchecked();
     } else {
         dispatch = MFunctionDispatch::New(alloc(), callInfo.fun());
     }
 
+    MOZ_ASSERT(dispatchBlock->stackDepth() >= callInfo.numFormals());
+    uint32_t stackDepth = dispatchBlock->stackDepth() - callInfo.numFormals() + 1;
+
     // Generate a return block to host the rval-collecting MPhi.
     jsbytecode* postCall = GetNextPc(pc);
     MBasicBlock* returnBlock;
-    MOZ_TRY_VAR(returnBlock, newBlock(nullptr, postCall));
+    MOZ_TRY_VAR(returnBlock, newBlock(stackDepth, postCall));
     graph().addBlock(returnBlock);
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
     // Set up stack, used to manually create a post-call resume point.
     returnBlock->inheritSlots(dispatchBlock);
     callInfo.popFormals(returnBlock);
 
     MPhi* retPhi = MPhi::New(alloc());
@@ -6505,19 +6495,21 @@ IonBuilder::jsop_initelem_getter_setter(
     MDefinition* obj = current->peek(-1);
 
     MInitElemGetterSetter* init = MInitElemGetterSetter::New(alloc(), obj, id, value);
     current->add(init);
     return resumeAfter(init);
 }
 
 AbortReasonOr<MBasicBlock*>
-IonBuilder::newBlock(MBasicBlock* predecessor, jsbytecode* pc)
-{
-    MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
+IonBuilder::newBlock(size_t stackDepth, jsbytecode* pc, MBasicBlock* maybePredecessor)
+{
+    MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
+
+    MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
                                           bytecodeSite(pc), MBasicBlock::NORMAL);
     if (!block)
         return abort(AbortReason::Alloc);
 
     block->setLoopDepth(loopDepth_);
     return block;
 }
 
@@ -6541,19 +6533,22 @@ IonBuilder::newBlockPopN(MBasicBlock* pr
     if (!block)
         return abort(AbortReason::Alloc);
 
     block->setLoopDepth(loopDepth_);
     return block;
 }
 
 AbortReasonOr<MBasicBlock*>
-IonBuilder::newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor, jsbytecode* pc)
-{
-    MBasicBlock* block = MBasicBlock::New(graph(), &analysis(), info(), predecessor,
+IonBuilder::newBlockAfter(MBasicBlock* at, size_t stackDepth, jsbytecode* pc,
+                          MBasicBlock* maybePredecessor)
+{
+    MOZ_ASSERT_IF(maybePredecessor, maybePredecessor->stackDepth() == stackDepth);
+
+    MBasicBlock* block = MBasicBlock::New(graph(), stackDepth, info(), maybePredecessor,
                                           bytecodeSite(pc), MBasicBlock::NORMAL);
     if (!block)
         return abort(AbortReason::Alloc);
 
     block->setLoopDepth(loopDepth_);
     block->setHitCount(0); // osr block
     graph().insertBlockAfter(at, block);
     return block;
@@ -6564,17 +6559,17 @@ IonBuilder::newOsrPreheader(MBasicBlock*
                             jsbytecode* beforeLoopEntry)
 {
     MOZ_ASSERT(loopEntry == GetNextPc(info().osrPc()));
 
     // Create two blocks: one for the OSR entry with no predecessors, one for
     // the preheader, which has the OSR entry block as a predecessor. The
     // OSR block is always the second block (with id 1).
     MBasicBlock* osrBlock;
-    MOZ_TRY_VAR(osrBlock, newBlockAfter(*graph().begin(), loopEntry));
+    MOZ_TRY_VAR(osrBlock, newBlockAfter(*graph().begin(), predecessor->stackDepth(), loopEntry));
     MBasicBlock* preheader;
     MOZ_TRY_VAR(preheader, newBlock(predecessor, loopEntry));
 
     graph().addBlock(preheader);
 
     // Give the pre-header the same hit count as the code before the loop.
     if (script()->hasScriptCounts())
         preheader->setHitCount(script()->getHitCount(beforeLoopEntry));
@@ -6582,17 +6577,17 @@ IonBuilder::newOsrPreheader(MBasicBlock*
     MOsrEntry* entry = MOsrEntry::New(alloc());
     osrBlock->add(entry);
 
     // Initialize |envChain|.
     {
         uint32_t slot = info().environmentChainSlot();
 
         MInstruction* envv;
-        if (analysis().usesEnvironmentChain()) {
+        if (usesEnvironmentChain()) {
             envv = MOsrEnvironmentChain::New(alloc(), entry);
         } else {
             // Use an undefined value if the script does not need its env
             // chain, to match the type that is already being tracked for the
             // slot.
             envv = MConstant::New(alloc(), UndefinedValue());
         }
 
@@ -7652,17 +7647,17 @@ IonBuilder::jsop_bindname(PropertyName* 
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_bindvar()
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
     MCallBindVar* ins = MCallBindVar::New(alloc(), current->environmentChain());
     current->add(ins);
     current->push(ins);
     return Ok();
 }
 
 static MIRType
 GetElemKnownType(bool needsHoleCheck, TemporaryTypeSet* types)
@@ -8369,17 +8364,18 @@ IonBuilder::getElemTryArguments(bool* em
     MInstruction* idInt32 = MToInt32::New(alloc(), index);
     current->add(idInt32);
     index = idInt32;
 
     // Bailouts if we read more than the number of actual arguments.
     index = addBoundsCheck(index, length);
 
     // Load the argument from the actual arguments.
-    MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, analysis_.hasSetArg());
+    bool modifiesArgs = script()->baselineScript()->modifiesArguments();
+    MGetFrameArgument* load = MGetFrameArgument::New(alloc(), index, modifiesArgs);
     current->add(load);
     current->push(load);
 
     TemporaryTypeSet* types = bytecodeTypes(pc);
     MOZ_TRY(pushTypeBarrier(load, types, BarrierKind::TypeSet));
 
     trackOptimizationSuccess();
     *emitted = true;
@@ -12170,17 +12166,17 @@ IonBuilder::jsop_classconstructor()
     current->add(constructor);
     current->push(constructor);
     return resumeAfter(constructor);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_lambda(JSFunction* fun)
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
     MOZ_ASSERT(!fun->isArrow());
 
     if (IsAsmJSModule(fun))
         return abort(AbortReason::Disable, "Lambda is an asm.js module function");
 
     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
     current->add(cst);
     MLambda* ins = MLambda::New(alloc(), constraints(), current->environmentChain(), cst);
@@ -12188,17 +12184,17 @@ IonBuilder::jsop_lambda(JSFunction* fun)
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_lambda_arrow(JSFunction* fun)
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
     MOZ_ASSERT(fun->isArrow());
     MOZ_ASSERT(!fun->isNative());
 
     MDefinition* newTargetDef = current->pop();
     MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
     current->add(cst);
     MLambdaArrow* ins = MLambdaArrow::New(alloc(), constraints(), current->environmentChain(),
                                           newTargetDef, cst);
@@ -12221,32 +12217,32 @@ IonBuilder::jsop_setfunname(uint8_t pref
     current->push(fun);
 
     return resumeAfter(ins);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_pushlexicalenv(uint32_t index)
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
 
     LexicalScope* scope = &script()->getScope(index)->as<LexicalScope>();
     MNewLexicalEnvironmentObject* ins =
         MNewLexicalEnvironmentObject::New(alloc(), current->environmentChain(), scope);
 
     current->add(ins);
     current->setEnvironmentChain(ins);
 
     return Ok();
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_copylexicalenv(bool copySlots)
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
 
     MCopyLexicalEnvironmentObject* ins =
         MCopyLexicalEnvironmentObject::New(alloc(), current->environmentChain(), copySlots);
 
     current->add(ins);
     current->setEnvironmentChain(ins);
 
     return Ok();
@@ -12255,17 +12251,18 @@ IonBuilder::jsop_copylexicalenv(bool cop
 AbortReasonOr<Ok>
 IonBuilder::jsop_setarg(uint32_t arg)
 {
     // To handle this case, we should spill the arguments to the space where
     // actual arguments are stored. The tricky part is that if we add a MIR
     // to wrap the spilling action, we don't want the spilling to be
     // captured by the GETARG and by the resume point, only by
     // MGetFrameArgument.
-    MOZ_ASSERT(analysis_.hasSetArg());
+    MOZ_ASSERT_IF(script()->hasBaselineScript(),
+                  script()->baselineScript()->modifiesArguments());
     MDefinition* val = current->peek(-1);
 
     // If an arguments object is in use, and it aliases formals, then all SETARGs
     // must go through the arguments object.
     if (info().argsObjAliasesFormals()) {
         if (NeedsPostBarrier(val))
             current->add(MPostWriteBarrier::New(alloc(), current->argumentsObject(), val));
         current->add(MSetArgumentsObjectArg::New(alloc(), current->argumentsObject(),
@@ -12341,17 +12338,17 @@ IonBuilder::jsop_defvar(uint32_t index)
 
     PropertyName* name = script()->getName(index);
 
     // Bake in attrs.
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
     MOZ_ASSERT(!script()->isForEval());
 
     // Pass the EnvironmentChain.
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
 
     // Bake the name pointer into the MDefVar.
     MDefVar* defvar = MDefVar::New(alloc(), name, attrs, current->environmentChain());
     current->add(defvar);
 
     return resumeAfter(defvar);
 }
 
@@ -12370,17 +12367,17 @@ IonBuilder::jsop_deflexical(uint32_t ind
     current->add(deflex);
 
     return resumeAfter(deflex);
 }
 
 AbortReasonOr<Ok>
 IonBuilder::jsop_deffun(uint32_t index)
 {
-    MOZ_ASSERT(analysis().usesEnvironmentChain());
+    MOZ_ASSERT(usesEnvironmentChain());
 
     MDefFun* deffun = MDefFun::New(alloc(), current->pop(), current->environmentChain());
     current->add(deffun);
 
     return resumeAfter(deffun);
 }
 
 AbortReasonOr<Ok>
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -71,32 +71,31 @@ class IonBuilder
     void spew(const char* message);
 
     JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
     AbortReasonOr<Ok> getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
                                          InliningTargets& targets, uint32_t maxTargets);
 
     AbortReasonOr<Ok> analyzeNewLoopTypes(const CFGBlock* loopEntryBlock);
 
-    AbortReasonOr<MBasicBlock*> newBlock(MBasicBlock* predecessor, jsbytecode* pc);
+    AbortReasonOr<MBasicBlock*> newBlock(size_t stackDepth, jsbytecode* pc,
+                                         MBasicBlock* maybePredecessor = nullptr);
     AbortReasonOr<MBasicBlock*> newBlock(MBasicBlock* predecessor, jsbytecode* pc,
                                          MResumePoint* priorResumePoint);
     AbortReasonOr<MBasicBlock*> newBlockPopN(MBasicBlock* predecessor, jsbytecode* pc,
                                              uint32_t popped);
-    AbortReasonOr<MBasicBlock*> newBlockAfter(MBasicBlock* at, MBasicBlock* predecessor,
-                                              jsbytecode* pc);
+    AbortReasonOr<MBasicBlock*> newBlockAfter(MBasicBlock* at, size_t stackDepth,
+                                              jsbytecode* pc, MBasicBlock* maybePredecessor = nullptr);
     AbortReasonOr<MBasicBlock*> newOsrPreheader(MBasicBlock* header, jsbytecode* loopEntry,
                                                 jsbytecode* beforeLoopEntry);
     AbortReasonOr<MBasicBlock*> newPendingLoopHeader(MBasicBlock* predecessor, jsbytecode* pc,
                                                      bool osr, bool canOsr, unsigned stackPhiCount);
-    AbortReasonOr<MBasicBlock*> newBlock(jsbytecode* pc) {
-        return newBlock(nullptr, pc);
-    }
-    AbortReasonOr<MBasicBlock*> newBlockAfter(MBasicBlock* at, jsbytecode* pc) {
-        return newBlockAfter(at, nullptr, pc);
+
+    AbortReasonOr<MBasicBlock*> newBlock(MBasicBlock* predecessor, jsbytecode* pc) {
+        return newBlock(predecessor->stackDepth(), pc, predecessor);
     }
 
     AbortReasonOr<Ok> visitBlock(const CFGBlock* hblock, MBasicBlock* mblock);
     AbortReasonOr<Ok> visitControlInstruction(CFGControlInstruction* ins, bool* restarted);
     AbortReasonOr<Ok> visitTest(CFGTest* test);
     AbortReasonOr<Ok> visitCompare(CFGCompare* compare);
     AbortReasonOr<Ok> visitLoopEntry(CFGLoopEntry* loopEntry);
     AbortReasonOr<Ok> visitReturn(CFGControlInstruction* ins);
@@ -131,16 +130,18 @@ class IonBuilder
     AbortReasonOr<Ok> resumeAt(MInstruction* ins, jsbytecode* pc);
     AbortReasonOr<Ok> resumeAfter(MInstruction* ins);
     AbortReasonOr<Ok> maybeInsertResume();
 
     bool blockIsOSREntry(const CFGBlock* block, const CFGBlock* predecessor);
 
     void insertRecompileCheck();
 
+    bool usesEnvironmentChain();
+
     AbortReasonOr<Ok> initParameters();
     void initLocals();
     void rewriteParameter(uint32_t slotIdx, MDefinition* param, int32_t argIndex);
     AbortReasonOr<Ok> rewriteParameters();
     AbortReasonOr<Ok> initEnvironmentChain(MDefinition* callee = nullptr);
     void initArgumentsObject();
     void pushConstant(const Value& v);
 
@@ -980,29 +981,22 @@ class IonBuilder
     AbortReasonOr<Ok> init();
 
     JSContext* analysisContext;
     BaselineFrameInspector* baselineFrame_;
 
     // Constraints for recording dependencies on type information.
     CompilerConstraintList* constraints_;
 
-    // Basic analysis information about the script.
-    BytecodeAnalysis analysis_;
-    BytecodeAnalysis& analysis() {
-        return analysis_;
-    }
-
     TemporaryTypeSet* thisTypes;
     TemporaryTypeSet* argTypes;
     TemporaryTypeSet* typeArray;
     uint32_t typeArrayHint;
     uint32_t* bytecodeTypeMap;
 
-    GSNCache gsn;
     EnvironmentCoordinateNameCache envCoordinateNameCache;
 
     jsbytecode* pc;
     MBasicBlock* current;
     uint32_t loopDepth_;
     Vector<MBasicBlock*, 0, JitAllocPolicy> blockWorklist;
     const CFGBlock* cfgCurrent;
     const ControlFlowGraph* cfg;
--- a/js/src/jit/IonControlFlow.cpp
+++ b/js/src/jit/IonControlFlow.cpp
@@ -17,26 +17,20 @@ ControlFlowGenerator::ControlFlowGenerat
   : script(script),
     current(nullptr),
     alloc_(temp),
     blocks_(temp),
     cfgStack_(temp),
     loops_(temp),
     switches_(temp),
     labels_(temp),
-    analysis_(temp, script),
-    aborted_(false)
+    aborted_(false),
+    checkedTryFinally_(false)
 { }
 
-bool
-ControlFlowGenerator::init()
-{
-    return analysis_.init(alloc(), gsn);
-}
-
 static inline int32_t
 GetJumpOffset(jsbytecode* pc)
 {
     MOZ_ASSERT(CodeSpec[JSOp(*pc)].type() == JOF_JUMP);
     return GET_JUMP_OFFSET(pc);
 }
 
 void
@@ -534,18 +528,25 @@ ControlFlowGenerator::processLabelEnd(CF
 }
 
 ControlFlowGenerator::ControlStatus
 ControlFlowGenerator::processTry()
 {
     MOZ_ASSERT(JSOp(*pc) == JSOP_TRY);
 
     // Try-finally is not yet supported.
-    if (analysis_.hasTryFinally())
-        return ControlStatus::Abort;
+    if (!checkedTryFinally_) {
+        JSTryNote* tn = script->trynotes()->vector;
+        JSTryNote* tnlimit = tn + script->trynotes()->length;
+        for (; tn < tnlimit; tn++) {
+            if (tn->kind == JSTRY_FINALLY)
+                return ControlStatus::Abort;
+        }
+        checkedTryFinally_ = true;
+    }
 
     jssrcnote* sn = GetSrcNote(gsn, script, pc);
     MOZ_ASSERT(SN_TYPE(sn) == SRC_TRY);
 
     // Get the pc of the last instruction in the try block. It's a JSOP_GOTO to
     // jump over the catch block.
     jsbytecode* endpc = pc + GetSrcNoteOffset(sn, 0);
     MOZ_ASSERT(JSOp(*endpc) == JSOP_GOTO);
@@ -561,31 +562,21 @@ ControlFlowGenerator::processTry()
     //     try {
     //         throw 3;
     //     } catch(e) { }
     //
     //     for (var i=0; i<1000; i++) {}
     //
     // To handle this, we create two blocks: one for the try block and one
     // for the code following the try-catch statement.
-    //
-    // If the code after the try block is unreachable (control flow in both the
-    // try and catch blocks is terminated), only create the try block, to avoid
-    // parsing unreachable code.
 
     CFGBlock* tryBlock = CFGBlock::New(alloc(), GetNextPc(pc));
 
-    CFGBlock* successor;
-    if (analysis_.maybeInfo(afterTry)) {
-        successor = CFGBlock::New(alloc(), afterTry);
-        current->setStopIns(CFGTry::New(alloc(), tryBlock, endpc, successor));
-    } else {
-        successor = nullptr;
-        current->setStopIns(CFGTry::New(alloc(), tryBlock, endpc));
-    }
+    CFGBlock* successor = CFGBlock::New(alloc(), afterTry);
+    current->setStopIns(CFGTry::New(alloc(), tryBlock, endpc, successor));
     current->setStopPc(pc);
 
     if (!cfgStack_.append(CFGState::Try(endpc, successor)))
         return ControlStatus::Error;
 
     current = tryBlock;
     pc = current->startPc();
 
@@ -594,21 +585,17 @@ ControlFlowGenerator::processTry()
 
     return ControlStatus::Jumped;
 }
 
 ControlFlowGenerator::ControlStatus
 ControlFlowGenerator::processTryEnd(CFGState& state)
 {
     MOZ_ASSERT(state.state == CFGState::TRY);
-
-    if (!state.try_.successor) {
-        MOZ_ASSERT(!current);
-        return ControlStatus::Ended;
-    }
+    MOZ_ASSERT(state.try_.successor);
 
     if (current) {
         current->setStopIns(CFGGoto::New(alloc(), state.try_.successor));
         current->setStopPc(pc);
     }
 
     // Start parsing the code after this try-catch statement.
     current = state.try_.successor;
--- a/js/src/jit/IonControlFlow.h
+++ b/js/src/jit/IonControlFlow.h
@@ -204,34 +204,34 @@ class CFGAryControlInstruction : public 
 };
 
 class CFGTry : public CFGControlInstruction
 {
     CFGBlock* tryBlock_;
     jsbytecode* catchStartPc_;
     CFGBlock* mergePoint_;
 
-    CFGTry(CFGBlock* successor, jsbytecode* catchStartPc, CFGBlock* mergePoint = nullptr)
+    CFGTry(CFGBlock* successor, jsbytecode* catchStartPc, CFGBlock* mergePoint)
       : tryBlock_(successor),
         catchStartPc_(catchStartPc),
         mergePoint_(mergePoint)
     { }
 
   public:
     CFG_CONTROL_HEADER(Try)
     TRIVIAL_CFG_NEW_WRAPPERS
 
     static CFGTry* CopyWithNewTargets(TempAllocator& alloc, CFGTry* old,
                                       CFGBlock* tryBlock, CFGBlock* merge)
     {
         return new(alloc) CFGTry(tryBlock, old->catchStartPc(), merge);
     }
 
     size_t numSuccessors() const final override {
-        return mergePoint_ ? 2 : 1;
+        return 2;
     }
     CFGBlock* getSuccessor(size_t i) const final override {
         MOZ_ASSERT(i < numSuccessors());
         return (i == 0) ? tryBlock_ : mergePoint_;
     }
     void replaceSuccessor(size_t i, CFGBlock* succ) final override {
         MOZ_ASSERT(i < numSuccessors());
         if (i == 0)
@@ -246,20 +246,16 @@ class CFGTry : public CFGControlInstruct
 
     jsbytecode* catchStartPc() const {
         return catchStartPc_;
     }
 
     CFGBlock* afterTryCatchBlock() const {
         return getSuccessor(1);
     }
-
-    bool codeAfterTryCatchReachable() const {
-        return !!mergePoint_;
-    }
 };
 
 class CFGTableSwitch : public CFGControlInstruction
 {
     Vector<CFGBlock*, 4, JitAllocPolicy> successors_;
     size_t low_;
     size_t high_;
 
@@ -804,24 +800,22 @@ class ControlFlowGenerator
         static CFGState Label(jsbytecode* exitpc);
         static CFGState Try(jsbytecode* exitpc, CFGBlock* successor);
     };
 
     Vector<CFGState, 8, JitAllocPolicy> cfgStack_;
     Vector<ControlFlowInfo, 4, JitAllocPolicy> loops_;
     Vector<ControlFlowInfo, 0, JitAllocPolicy> switches_;
     Vector<ControlFlowInfo, 2, JitAllocPolicy> labels_;
-    BytecodeAnalysis analysis_;
     bool aborted_;
+    bool checkedTryFinally_;
 
   public:
     ControlFlowGenerator(TempAllocator& alloc, JSScript* script);
 
-    MOZ_MUST_USE bool init();
-
     MOZ_MUST_USE bool traverseBytecode();
     MOZ_MUST_USE bool addBlock(CFGBlock* block);
     ControlFlowGraph* getGraph(TempAllocator& alloc) {
         ControlFlowGraph* cfg = ControlFlowGraph::New(alloc);
         if (!cfg)
             return nullptr;
         if (!cfg->init(alloc, blocks_))
             return nullptr;
--- a/js/src/jit/LoopUnroller.cpp
+++ b/js/src/jit/LoopUnroller.cpp
@@ -207,27 +207,27 @@ LoopUnroller::go(LoopIterationBound* bou
 
     JitSpew(JitSpew_Unrolling, "Unrolling loop");
 
     // The old preheader will go before the unrolled loop, and the old loop
     // will need a new empty preheader.
     const CompileInfo& info = oldPreheader->info();
     if (header->trackedPc()) {
         unrolledHeader =
-            MBasicBlock::New(graph, nullptr, info,
+            MBasicBlock::New(graph, oldPreheader->stackDepth(), info,
                              oldPreheader, header->trackedSite(), MBasicBlock::LOOP_HEADER);
         if (!unrolledHeader)
             return false;
         unrolledBackedge =
-            MBasicBlock::New(graph, nullptr, info,
+            MBasicBlock::New(graph, unrolledHeader->stackDepth(), info,
                              unrolledHeader, backedge->trackedSite(), MBasicBlock::NORMAL);
         if (!unrolledBackedge)
             return false;
         newPreheader =
-            MBasicBlock::New(graph, nullptr, info,
+            MBasicBlock::New(graph, unrolledHeader->stackDepth(), info,
                              unrolledHeader, oldPreheader->trackedSite(), MBasicBlock::NORMAL);
         if (!newPreheader)
             return false;
     } else {
         unrolledHeader = MBasicBlock::New(graph, info, oldPreheader, MBasicBlock::LOOP_HEADER);
         if (!unrolledHeader)
             return false;
         unrolledBackedge = MBasicBlock::New(graph, info, unrolledHeader, MBasicBlock::NORMAL);
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -281,40 +281,40 @@ MIRGraph::removeBlockIncludingPhis(MBasi
 void
 MIRGraph::unmarkBlocks()
 {
     for (MBasicBlockIterator i(blocks_.begin()); i != blocks_.end(); i++)
         i->unmark();
 }
 
 MBasicBlock*
-MBasicBlock::New(MIRGraph& graph, BytecodeAnalysis* analysis, const CompileInfo& info,
-                 MBasicBlock* pred, BytecodeSite* site, Kind kind)
+MBasicBlock::New(MIRGraph& graph, size_t stackDepth, const CompileInfo& info,
+                 MBasicBlock* maybePred, BytecodeSite* site, Kind kind)
 {
     MOZ_ASSERT(site->pc() != nullptr);
 
     MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
     if (!block->init())
         return nullptr;
 
-    if (!block->inherit(graph.alloc(), analysis, pred, 0))
+    if (!block->inherit(graph.alloc(), stackDepth, maybePred, 0))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock*
 MBasicBlock::NewPopN(MIRGraph& graph, const CompileInfo& info,
                      MBasicBlock* pred, BytecodeSite* site, Kind kind, uint32_t popped)
 {
     MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, kind);
     if (!block->init())
         return nullptr;
 
-    if (!block->inherit(graph.alloc(), nullptr, pred, popped))
+    if (!block->inherit(graph.alloc(), pred->stackDepth(), pred, popped))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock*
 MBasicBlock::NewWithResumePoint(MIRGraph& graph, const CompileInfo& info,
                                 MBasicBlock* pred, BytecodeSite* site,
@@ -343,17 +343,17 @@ MBasicBlock::NewPendingLoopHeader(MIRGra
                                   unsigned stackPhiCount)
 {
     MOZ_ASSERT(site->pc() != nullptr);
 
     MBasicBlock* block = new(graph.alloc()) MBasicBlock(graph, info, site, PENDING_LOOP_HEADER);
     if (!block->init())
         return nullptr;
 
-    if (!block->inherit(graph.alloc(), nullptr, pred, 0, stackPhiCount))
+    if (!block->inherit(graph.alloc(), pred->stackDepth(), pred, 0, stackPhiCount))
         return nullptr;
 
     return block;
 }
 
 MBasicBlock*
 MBasicBlock::NewSplitEdge(MIRGraph& graph, MBasicBlock* pred, size_t predEdgeIdx, MBasicBlock* succ)
 {
@@ -541,90 +541,86 @@ MBasicBlock::copySlots(MBasicBlock* from
 
     MDefinition** thisSlots = slots_.begin();
     MDefinition** fromSlots = from->slots_.begin();
     for (size_t i = 0, e = stackPosition_; i < e; ++i)
         thisSlots[i] = fromSlots[i];
 }
 
 bool
-MBasicBlock::inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
+MBasicBlock::inherit(TempAllocator& alloc, size_t stackDepth, MBasicBlock* maybePred,
                      uint32_t popped, unsigned stackPhiCount)
 {
-    if (pred) {
-        stackPosition_ = pred->stackPosition_;
-        MOZ_ASSERT(stackPosition_ >= popped);
-        stackPosition_ -= popped;
-        if (kind_ != PENDING_LOOP_HEADER)
-            copySlots(pred);
-    } else {
-        uint32_t stackDepth = analysis->info(pc()).stackDepth;
-        stackPosition_ = info().firstStackSlot() + stackDepth;
-        MOZ_ASSERT(stackPosition_ >= popped);
-        stackPosition_ -= popped;
-    }
+    MOZ_ASSERT_IF(maybePred, maybePred->stackDepth() == stackDepth);
+
+    MOZ_ASSERT(stackDepth >= popped);
+    stackDepth -= popped;
+    stackPosition_ = stackDepth;
+
+    if (maybePred && kind_ != PENDING_LOOP_HEADER)
+        copySlots(maybePred);
 
     MOZ_ASSERT(info_.nslots() >= stackPosition_);
     MOZ_ASSERT(!entryResumePoint_);
 
     // Propagate the caller resume point from the inherited block.
-    callerResumePoint_ = pred ? pred->callerResumePoint() : nullptr;
+    callerResumePoint_ = maybePred ? maybePred->callerResumePoint() : nullptr;
 
     // Create a resume point using our initial stack state.
     entryResumePoint_ = new(alloc) MResumePoint(this, pc(), MResumePoint::ResumeAt);
     if (!entryResumePoint_->init(alloc))
         return false;
 
-    if (pred) {
-        if (!predecessors_.append(pred))
+    if (maybePred) {
+        if (!predecessors_.append(maybePred))
             return false;
 
         if (kind_ == PENDING_LOOP_HEADER) {
             size_t i = 0;
             for (i = 0; i < info().firstStackSlot(); i++) {
                 MPhi* phi = MPhi::New(alloc.fallible());
                 if (!phi)
                     return false;
-                phi->addInlineInput(pred->getSlot(i));
+                phi->addInlineInput(maybePred->getSlot(i));
                 addPhi(phi);
                 setSlot(i, phi);
                 entryResumePoint()->initOperand(i, phi);
             }
 
-            MOZ_ASSERT(stackPhiCount <= stackDepth());
-            MOZ_ASSERT(info().firstStackSlot() <= stackDepth() - stackPhiCount);
+            MOZ_ASSERT(stackPhiCount <= stackDepth);
+            MOZ_ASSERT(info().firstStackSlot() <= stackDepth - stackPhiCount);
 
             // Avoid creating new phis for stack values that aren't part of the
             // loop.  Note that for loop headers that can OSR, all values on the
             // stack are part of the loop.
-            for (; i < stackDepth() - stackPhiCount; i++) {
-                MDefinition* val = pred->getSlot(i);
+            for (; i < stackDepth - stackPhiCount; i++) {
+                MDefinition* val = maybePred->getSlot(i);
                 setSlot(i, val);
                 entryResumePoint()->initOperand(i, val);
             }
 
-            for (; i < stackDepth(); i++) {
+            for (; i < stackDepth; i++) {
                 MPhi* phi = MPhi::New(alloc.fallible());
                 if (!phi)
                     return false;
-                phi->addInlineInput(pred->getSlot(i));
+                phi->addInlineInput(maybePred->getSlot(i));
                 addPhi(phi);
                 setSlot(i, phi);
                 entryResumePoint()->initOperand(i, phi);
             }
         } else {
-            for (size_t i = 0; i < stackDepth(); i++)
+            for (size_t i = 0; i < stackDepth; i++)
                 entryResumePoint()->initOperand(i, getSlot(i));
         }
     } else {
         /*
          * Don't leave the operands uninitialized for the caller, as it may not
          * initialize them later on.
          */
-        for (size_t i = 0; i < stackDepth(); i++)
+        for (size_t i = 0; i < stackDepth; i++)
             entryResumePoint()->clearOperand(i);
     }
 
     return true;
 }
 
 bool
 MBasicBlock::inheritResumePoint(MBasicBlock* pred)
--- a/js/src/jit/MIRGraph.h
+++ b/js/src/jit/MIRGraph.h
@@ -44,18 +44,18 @@ class MBasicBlock : public TempObject, p
         SPLIT_EDGE,
         DEAD
     };
 
   private:
     MBasicBlock(MIRGraph& graph, const CompileInfo& info, BytecodeSite* site, Kind kind);
     MOZ_MUST_USE bool init();
     void copySlots(MBasicBlock* from);
-    MOZ_MUST_USE bool inherit(TempAllocator& alloc, BytecodeAnalysis* analysis, MBasicBlock* pred,
-                                        uint32_t popped, unsigned stackPhiCount = 0);
+    MOZ_MUST_USE bool inherit(TempAllocator& alloc, size_t stackDepth, MBasicBlock* maybePred,
+                              uint32_t popped, unsigned stackPhiCount = 0);
     MOZ_MUST_USE bool inheritResumePoint(MBasicBlock* pred);
     void assertUsesAreNotWithin(MUseIterator use, MUseIterator end);
 
     // This block cannot be reached by any means.
     bool unreachable_;
 
     // Keeps track if the phis has been type specialized already.
     bool specialized_;
@@ -104,18 +104,18 @@ class MBasicBlock : public TempObject, p
 
   public:
     ///////////////////////////////////////////////////////
     ////////// BEGIN GRAPH BUILDING INSTRUCTIONS //////////
     ///////////////////////////////////////////////////////
 
     // Creates a new basic block for a MIR generator. If |pred| is not nullptr,
     // its slots and stack depth are initialized from |pred|.
-    static MBasicBlock* New(MIRGraph& graph, BytecodeAnalysis* analysis, const CompileInfo& info,
-                            MBasicBlock* pred, BytecodeSite* site, Kind kind);
+    static MBasicBlock* New(MIRGraph& graph, size_t stackDepth, const CompileInfo& info,
+                            MBasicBlock* maybePred, BytecodeSite* site, Kind kind);
     static MBasicBlock* New(MIRGraph& graph, const CompileInfo& info, MBasicBlock* pred, Kind kind);
     static MBasicBlock* NewPopN(MIRGraph& graph, const CompileInfo& info,
                                 MBasicBlock* pred, BytecodeSite* site, Kind kind, uint32_t popn);
     static MBasicBlock* NewWithResumePoint(MIRGraph& graph, const CompileInfo& info,
                                            MBasicBlock* pred, BytecodeSite* site,
                                            MResumePoint* resumePoint);
     static MBasicBlock* NewPendingLoopHeader(MIRGraph& graph, const CompileInfo& info,
                                              MBasicBlock* pred, BytecodeSite* site,
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -564,20 +564,23 @@ LexicalEnvironmentObject*
 JSCompartment::getOrCreateNonSyntacticLexicalEnvironment(JSContext* cx, HandleObject enclosing)
 {
     if (!nonSyntacticLexicalEnvironments_) {
         nonSyntacticLexicalEnvironments_ = cx->new_<ObjectWeakMap>(cx);
         if (!nonSyntacticLexicalEnvironments_ || !nonSyntacticLexicalEnvironments_->init())
             return nullptr;
     }
 
-    // The key is the unwrapped dynamic scope, as we may be creating different
-    // WithEnvironmentObject wrappers each time.
-    MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
-    RootedObject key(cx, &enclosing->as<WithEnvironmentObject>().object());
+    // If a wrapped WithEnvironmentObject was passed in, unwrap it, as we may
+    // be creating different WithEnvironmentObject wrappers each time.
+    RootedObject key(cx, enclosing);
+    if (enclosing->is<WithEnvironmentObject>()) {
+        MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
+        key = &enclosing->as<WithEnvironmentObject>().object();
+    }
     RootedObject lexicalEnv(cx, nonSyntacticLexicalEnvironments_->lookup(key));
 
     if (!lexicalEnv) {
         lexicalEnv = LexicalEnvironmentObject::createNonSyntactic(cx, enclosing);
         if (!lexicalEnv)
             return nullptr;
         if (!nonSyntacticLexicalEnvironments_->add(cx, key, lexicalEnv))
             return nullptr;
@@ -586,19 +589,23 @@ JSCompartment::getOrCreateNonSyntacticLe
     return &lexicalEnv->as<LexicalEnvironmentObject>();
 }
 
 LexicalEnvironmentObject*
 JSCompartment::getNonSyntacticLexicalEnvironment(JSObject* enclosing) const
 {
     if (!nonSyntacticLexicalEnvironments_)
         return nullptr;
-    if (!enclosing->is<WithEnvironmentObject>())
-        return nullptr;
-    JSObject* key = &enclosing->as<WithEnvironmentObject>().object();
+    // If a wrapped WithEnvironmentObject was passed in, unwrap it as in
+    // getOrCreateNonSyntacticLexicalEnvironment.
+    JSObject* key = enclosing;
+    if (enclosing->is<WithEnvironmentObject>()) {
+        MOZ_ASSERT(!enclosing->as<WithEnvironmentObject>().isSyntactic());
+        key = &enclosing->as<WithEnvironmentObject>().object();
+    }
     JSObject* lexicalEnv = nonSyntacticLexicalEnvironments_->lookup(key);
     if (!lexicalEnv)
         return nullptr;
     return &lexicalEnv->as<LexicalEnvironmentObject>();
 }
 
 bool
 JSCompartment::addToVarNames(JSContext* cx, JS::Handle<JSAtom*> name)
--- a/js/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -206,16 +206,51 @@ this.XPCOMUtils = {
         return value;
       },
       configurable: true,
       enumerable: true
     });
   },
 
   /**
+   * Defines a getter on a specified object for a script.  The script will not
+   * be loaded until first use.
+   *
+   * @param aObject
+   *        The object to define the lazy getter on.
+   * @param aNames
+   *        The name of the getter to define on aObject for the script.
+   *        This can be a string if the script exports only one symbol,
+   *        or an array of strings if the script can be first accessed
+   *        from several different symbols.
+   * @param aResource
+   *        The URL used to obtain the script.
+   */
+  defineLazyScriptGetter: function XPCU_defineLazyScriptGetter(aObject, aNames,
+                                                               aResource)
+  {
+    if (!Array.isArray(aNames)) {
+      aNames = [aNames];
+    }
+    for (let name of aNames) {
+      Object.defineProperty(aObject, name, {
+        get: function() {
+          for (let n of aNames) {
+            delete aObject[n];
+          }
+          Services.scriptloader.loadSubScript(aResource, aObject);
+          return aObject[name];
+        },
+        configurable: true,
+        enumerable: true
+      });
+    }
+  },
+
+  /**
    * Defines a getter on a specified object for a service.  The service will not
    * be obtained until first use.
    *
    * @param aObject
    *        The object to define the lazy getter on.
    * @param aName
    *        The name of the getter to define on aObject for the service.
    * @param aContract
--- a/netwerk/mime/nsMIMEHeaderParamImpl.cpp
+++ b/netwerk/mime/nsMIMEHeaderParamImpl.cpp
@@ -816,18 +816,18 @@ nsMIMEHeaderParamImpl::DecodeRFC5987Para
                                           nsACString& aLang,
                                           nsAString& aResult)
 {
   nsAutoCString charset;
   nsAutoCString language;
   nsAutoCString value;
 
   uint32_t delimiters = 0;
-  const char *encoded = PromiseFlatCString(aParamVal).get();
-  const char *c = encoded;
+  const nsCString& encoded = PromiseFlatCString(aParamVal);
+  const char *c = encoded.get();
 
   while (*c) {
     char tc = *c++;
 
     if (tc == '\'') {
       // single quote
       delimiters++;
     } else if (((unsigned char)tc) >= 128) {
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -160,16 +160,35 @@ function Tester(aTests, structuredLogger
   this.PromiseTestUtils.init();
 
   this.SimpleTestOriginal = {};
   SIMPLETEST_OVERRIDES.forEach(m => {
     this.SimpleTestOriginal[m] = this.SimpleTest[m];
   });
 
   this._coverageCollector = null;
+
+  // Avoid failing tests when XPCOMUtils.defineLazyScriptGetter is used.
+  Services.scriptloader = {
+    loadSubScript: (url, obj, charset) => {
+      let before = Object.keys(window);
+      try {
+        return this._scriptLoader.loadSubScript(url, obj, charset);
+      } finally {
+        for (let property of Object.keys(window)) {
+          if (!before.includes(property) && !this._globalProperties.includes(property)) {
+            this._globalProperties.push(property);
+            this.SimpleTest.info("Global property added while loading " + url + ": " + property);
+          }
+        }
+      }
+    },
+    loadSubScriptWithOptions: this._scriptLoader.loadSubScriptWithOptions.bind(this._scriptLoader),
+    precompileScript: this._scriptLoader.precompileScript.bind(this._scriptLoader)
+  };
 }
 Tester.prototype = {
   EventUtils: {},
   SimpleTest: {},
   Task: null,
   ContentTask: null,
   ExtensionTestUtils: null,
   Assert: null,
--- a/testing/talos/talos/startup_test/sessionrestore/addon/SessionRestoreTalosTest.js
+++ b/testing/talos/talos/startup_test/sessionrestore/addon/SessionRestoreTalosTest.js
@@ -85,17 +85,17 @@ nsSessionRestoreTalosTest.prototype = {
   onReady(hasRestoredTabs) {
     if (hasRestoredTabs) {
       Services.obs.removeObserver(this, StartupPerformance.RESTORED_TOPIC);
     }
 
     // onReady might fire before the browser window has finished initializing
     // or sometimes soon after. We handle both cases here.
     let win = Services.wm.getMostRecentWindow("navigator:browser");
-    if (!win || !win.gBrowser) {
+    if (!win || !win.gBrowserInit || !win.gBrowserInit.delayedStartupFinished) {
       // We didn't have a window around yet, so we'll wait until one becomes
       // available before opening the result tab.
       Services.obs.addObserver(this, WINDOW_READY_TOPIC);
     } else {
       // We have a window, so we can open the result tab in it right away.
       this.onWindow(win);
     }
 
--- a/testing/talos/talos/startup_test/sessionrestore/addon/install.rdf
+++ b/testing/talos/talos/startup_test/sessionrestore/addon/install.rdf
@@ -1,14 +1,14 @@
 <?xml version="1.0"?><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">
 
 <!-- Required Items -->
 <em:id>session-restore-test-2@mozilla.org</em:id>
 <em:name>Session Restore Startup Performance Test</em:name>
-<em:version>2.0.10</em:version>
+<em:version>2.0.11</em:version>
 
 <em:targetApplication>
     <Description>
         <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
         <em:minVersion>1.5</em:minVersion>
         <em:maxVersion>*</em:maxVersion>
     </Description>
 </em:targetApplication>
index 9ed9924cacc541d06355ea25e2e5a8551f25fd07..725b1273078607372b5bab3533afafb1576c2913
GIT binary patch
literal 8905
zc$}451yCK^mc2Lx3r+|Y<l-(Dmjri*-~=x27Th(syE_SP;o^F6m*DQfgWKf&nm_N)
zoA;~!oT{$s)758n_1<gk-g}=`l7)lE1polZfa$<%8Q~u-d5>rSfZ=NZ;E${F;%cHy
z(h3qRHg+Bs*49SMj!s4^nif2X-b<i}8V>e%{q1jixO&Lrxx|udmz8Uyi&*F0)kb9`
zGEqri3ohUdlIvi{Q9ZOXP(P^XA2y$QoZj?29Pnmuxe4jbpYo-=xIb52K4w8tV%JlQ
zI5#&IH+0a*>Fp6wDv@H>&AP~6hhXITE9VowGsKX2<#&>TO&ch-h7e2355O*@fP+Q!
z?_!PZ;}RWo0`#nDlS4if#Lkf^kOP$nQGfIz2g3721DH@FNV~=d7Z%brv(`5x&Z@5+
zDZYOy1pJCij26FTDJ##0smep@8e6)S@}D0V?3x{%^`A#R6?LxdGiLRhiGv9epE<Up
zuHRFnuCHh)Y{(+%npK_JDFI0bgu+Q6c0Q9(poggsT$V5>a(0d4T&cd4v<(?(G{O`3
zWkkVHA`KYT5Pq~kJ1OLS3y1HC%=Qu>B1IU2VEFt-;JC`~<@3ciz$UU%4eLHut6tl;
zEM0f`tTQ3P;vSs`2RhbSvb`6u_$aJ~W?wF#hK!fuR`5JKTN1@%U9b(A)UAGg11C!T
zj)~t7e)r{xz>k)WoQf5vZ{xEzD+*e^+vSi9Xod5k-XN`yVZjq>^7tNIUC!p0gLJH>
zT?1zc0iH8co&DDR+k2sbVa8*ZI`%-`9=od59&wq%a4u`MZM*n=?({cmyam0N^2<a>
z4l9`&bay{r)$#0D(qG^3s>Xr(_MC$G^Wl=M1#Bxc0*(V>P^YaCej-;vqcNhx*jD7c
z3`PtR+cZ)-Y+hUi_y*iub$U;p&Er2IG`R(#y9OKLYUkyJX?51$;(|T)>B`}&Of-JA
zr)Gy6(F-)s^A$u`cWY5)3ZhL$ob=uZAF&Qc8I&V^pGnvyV|mBD78Tdlf%Yp@sQsaa
zr>VDPW8@Vm!N8|gG`2fszxQcD**1@v!d0{T`}7Z24-wL+Zj3`&ruw$mUOq5R6JBKo
zBPl!sTd=#LhE50A8yY^D8@KgDLnfC{XwT0(o)ft`e3s^$k1VIn_j(b^)^bDV;HVF7
z@In4#=hvzD6Bg}mi5*0;OcJb_KXlx=8wENEuqq}(J+tOA>8dS`#7~kGbrnc!DdvT7
zu4EwMY&z&wk+rv^tQjHZS2InY**KJ_GZ{JV;A(Dem9L1tL-sc|E8ioxbMRB}dU0lD
zS~*bn**Q#y9K#&X&wkB;vQU>`zx{c+hd56Bpr4k?rfQqrM1@n!@TEM5ZMfMwhnPYk
z$83O2{g8VG(VbddR2G%Rco_c<5aLHG@?x$spln2LAzweA)I8f!N-s&3PvJqDp1VD-
z!9XO3$}%I|wK!5vrl?JeAqblI!BuuOb_Nr*;>X;Zv74^a*=%Tlk?oig$NpVP>howM
zgp}+4u+&nE(KFU~jDL`Inddo|)heI(E^~Ba7jNdSGV<*Om?8&woAEG!_lT7fh9#R8
z(Qc2Ndx9ZsdU1chLyt#g?368q@DS)GJra~En-m%HszMJIuT55DP{7%KV0lh1&#9wO
z#7zLMlDJV(LV%4Ziiky#-wki<Mg!i~{Uxs*48D8)u}sTaqT^v>UTF*Cg=UKBipU#f
zo@V?Gaa3bXID#rv!r-*PU=VUXked)&-(Q-odG>Bc5GrW~KchS$^03&zit6;NwSbl_
z`xz}?u5tJ0;nkA-qDw<>+KnP`YX4#_Ib5~^U5R}wXcIQLnD24jvQ>MNINMbWk?6r_
z1azeb(eg2~O-?GVn`P&4N>o#q%8-5!I@qLX3pIUQvdjN;qjedMqLvPU!wunASFqQN
zYAI)X7-()1O?<vEGgb7BC@<1sFv@?08#yEUshp2;ziP0O1G=89O<72#h~iU0T-8d@
zRd-V(cSIk1nydLOO;fuq5!=GWyRy^@I&&91KG4k`fhm-vQ^rjl<4`WaLeNbDYUysZ
zB2b$z49~l61P#GXW**U$T<wf6w3?l5AV^gU`oU*C5AMF^LAbr4&l4`Z@bhIzxCH15
zr)ACtik$In+y1hPv^{=*+ttY<LPQyw)Ncf?mPGS{2R|dcSOG_0pzHEkQ-O+cP@o0E
z0H(r^U1q|S?Xy;fMvqhDx)0^oK89u_R{VF-Pswuc=0nfT)MKg+1#72h9!5&`leqIB
z_ji;Bp~j%usok3jvP>D+>EL0MBTjXt%*6t#PebRQN>343P+&bLu##c!`>nUQglyje
zBYyhi?5Cm$AFEe3;y{39x-6=Ps8v`p1d8YPjZm*P^&A9xltfxRR=&O1bxSLvb*(Z{
zeS5!wLeCqcts+Y->1WM)MIK)RD%}A+oZYBI+z3MkZ1<LSUP1iES$0Q!;YHfw_s^?e
z1)YVIx+}a9Bqzh;W9sI&@q#j$4cUWx`MsEN&c4E?DL)<DjIL9}RnfR{i&^g5$V*=I
zF?&f>Oiy%zPI7@J!YxMd5h&w1{0%avt-I67hm1Szxm&NFXJ|8X2xlQk5hAuV*0ZKT
z(9Q?@CQ`2eg)ePIy^bQp1;V~JKe~-8l01JkL2+GM;IKd79Y@HF(q~)8Hk?Wy9}+>1
zhVD2Sv{wl0cHG?gzTC7^z5DSLUnKWT2U4vVj?te`wgTSVaGaH|SWX8f9wF1HnTcT$
zL}??n1-GWIj@Bb(rAM}U=RrJTb+x9aD%VHfqfTK)JtRj_c<581;$5>hyyN$@Th3|R
z#$#!x@oW*c%P~1`*pLJ2F4`SW`;Q=%_T!(v7Ulv*?~OQq1rnk8rwxD;L}*~Jk&!0I
z@Tmz<6S4jLM34YmSfBX30^egU5h7NT8s$;SVNP_HDzm<(rpAN6D46|DnD<R7XdYcw
z>_pA>(YXlq8+oLxMqm^TEHWKWJgUeCcE8veLox8mlz3IXw5GxNm;6xJWrRj->Djb*
z?|4ODjAY3DMf^A;`{{Mx;<i^H2D}gch(OZTx{O;`_-qG~S<seU@Zgb!Z0J3US_}6Z
zoQH<^`EMu^zFF(M{=v5u?nw#BCy>;+l?xcwN;@IeZCE+kXa>^Q8<msgmsw}!2R-f+
z@Rz7m!wMN5sXeQ<)e>ZkH;44nWnKlFCngRM=(%Y=!>sT*=mwLQcJ1X0@CqZOn5hX(
z=4o`zwNk|)fUEX823z@mIJ`=-wttd#VdI3<UyjY6dGEb7abP|np?m^Tlz5R5zdys9
zQ{>nldSR{TuuNulxx#YD_u87`2wp0Cl85S{PYQ9AQ7=?mCsx4Fl|*@mISVFg3@;e6
zWOC3XrxnhNYFW5ZX7S10vZpdHdo_Jx1R2np`o@q(s|xrQ%PcvzQCGuoA<1S3oUNgZ
zdl9Xcf7FTg96(%&Y7J;&A!5hY$z~}I^)~jKmn`|lZz+Y=$6PiY>H{NKUIvpS=myN^
z><Z7!q43?kO?;62D!x}i51EgI24lyAS7xo5Yh3hcOY9@hQ$8`D6&Zk6(?nRncvQQ^
z>Z`3lv9|vH2ja9=i}aKX%C3X8gje=47DA+|V~pVt<2qB;a}~*=0zAD0^#L43<Lg4@
zJf~JM*9f2!ExE2BNv*x3ryYI3MHB@at$gm_183dE+1KN)m;)#kEI0lHaCuyZNqcTl
z$2&{mJCWVz&}cWO;!Btf<8g(ai#%jqI}v#}4_@#J_OE(AMYi2apR{m(YruzqM~Ckt
zge10tB2weN!>o6n3=a+cU@gGU4q{Q(4m%gXMM@BM&##yyq}R99!Jjai1A*FPD_m=A
z6O9v5vDD2_xo@I-FKm!pD%N1$IhO+M3#RN_7vZ&aW&^o}WL2;J(1VL4yp_E`st<de
z;ZB|3Hp2`w!v|8WX1pY5&N*IjncCXiFO1of53Dy5QSH=scQ1e20_D`Cs6cBrg|PV)
z)sAx}r+tzT#l+-CX1i6_dS|(#iQ)Q^3)8}ofHqy5PT!zJPfFWZ@n{2T5QzN@l5u9}
znc^wC3YNPiIzd~FD;tY;6wLb>6>LvBnj?;5Zh`A`y9qAM0xwF%1*~6>Lez7d&*1ji
z78*XC2TdTE82M(h^DZZ6a#Y*7UH|K6npBA+*L&=Hub@h^3d{~#AA!X0Z=h?g@1c!0
zSkKCR-uX5eVu!g^=DuxpmFb$<%6<BNc)S|UI3R>y+gQ(G7O~{3rZSs6B$=8=?Nggh
z@eN|%x`lnmn&ul!LEUlqHKTQ0H1uw7S%4EgU<tH3CIo|@i^!1%&73!mv_lJ=EPKqd
za*>L~k`d-nJsdL1k&<n)rXC0GK{q02^ixus<X@{q5yD;w1FX=L`R(*y0>0BO(d-1D
z=+e%yrc`&Z6n%AYss+WHO_^uaSq%@RcJ0fqExXCwscKwvSJ+WRTbzH6KyF>DVy{ig
zGuMk~9l*WCHrOkjK!@~hxK^E#=9GMFp@O&s33#eHN!2ZU?2M+r4EmN9U|!IW0ZO7<
zVVJ+DK8@^0Dn<@&dx25<pK3`r;aY~o0+g``0{~dU0RX4~LV$&>le3YvHM66Msk#~h
z0B*v|>7$DqDgXxl>i1p$SZI@=7n2}@8*+MwN9k!sOqjcb|Gq4}zqi*!zRAN%Aq_5a
zV>r!dY4&#QGoMnCgf01L>-b&z2Z$hu%l&<s*u3p>r|}85>`e=1?|#MN_oO-R&4-fd
zL4I+48d>46tsiE2WFcx70p)udJ!y@HW7H{7M-*<5`ScU63wko<8=c!9y6gNP+0Uhp
zbRISrI6SU_tKS2y6T+p$zH-iR+2rCO&>x#dC$#4${yc}0Q#oEJ8y6rKr6qhyK+dL9
zOR+8GyjGx?-IecYsv}bIb=J-gMy!Lzu_-fsAmx%0{I$(7l%D`}kKQ&MW3)gSRtOw^
zALA}+h*=eHj1H6wzt>xvr$Dl~8jju7i}|7sE%z~MH^pMm;KY10(dVc<aXS!;h$Z&g
zxb@3i#G0>1>U5NOhr%7)>8iHrR0ZCc=F^q=BSR$DJxE-8+;{Pf-BuSQ_j!-oG2C!B
z?RZ_<zBcsKJ@^>w;tOeYrSf8~W&-ss<K9Cal|#L*#7}hI^i#jSqkW&9xDV90Fk@xS
zH3dKY_HyefAn&`8EKn)+M2p4ArzzaH-*#9GA*;_DVp97o_zp2Md>t7}x8AB9A<$G`
zImqBWzfiG8Sd-iNQF=)}cD${!_Nb6{3Hv8CT;d00+#4XXXuq9~3kv{X{-%bpxucy8
znAyh2*1{C*<opkMkht^yl^#*CT?##{Xd$QGuVh=|Z42~5dxJqDMBXqUmkc>Q-M)}l
zuTK>g(Ys(K6dH{mUOL<mkPh2u#YL*Uh<KHhltMx}L!-CCwbwDk*bXmKKODC{rq&aR
zkRXH+WLYEV2Zz|=rzusyy~7$8O{7d43{9qCi%W)#Zm|8>E?QUedz*Lr-bUr_^O323
z>LV*4rv7!O*l3vd7yXrsb=Hu=IJ33-(+3;%M7-mX{$zetiq-VuMQiy8@mV}q1McG$
zB!R37OtU&7q-4IGJLPzdi7<_gjb6J!GN<S#?Y+3G;9Jxw^Dj3`4DuWiGJ;S|LdwrR
z<bis8B{diy?$u+3b1RVs@o(?YI<7XRbj@=9G$>WbwM-fFR+1qy0N{lW01*8)s4CdW
z$->T7<+m~I9KmWv)^<*6zt5R1o&I6lE%5)5UW1mE(-JrSQ>B4JO5OC7ez+s4JpYAT
zOXaZ}=awd8vjueY$7>X6DJee$k>M|2UsgRuWAjbuM#o&Ax(Y?sg)qjv0V{g@KV3xz
zf@GI!tWEgRWZW<drEWF~4sKcKeAg{7^&tx_D;l2(NX6#hdYH7F&alrcOmFM!0?Doo
z8C7iec!h5VNK?Zb25iPz-YNw9`_ns*Y#^(5=@fBvvZ+Q2T@d!sRjXnoAC34CwUF%{
z)=8-%YWC6MIiR|3$o1+Ht%igm>q~fI{epIqYuvT!JGmfon2OM&hO=r!C>y!Mau}8H
zh!SFd2x;8VR2LbcDvblflEuT-u{Lm@y0m;O<5%&R5>L~cnTr}o(@-MBpyFxl^7)F#
zV8KY-XYUsc=9P(?6LT1f+%WC9XVefz4UdlgxSXo|ZH)Gt)Vc9dX&5|)<y*w`w@BpL
zph;pTxi5(sCoGzhDSIGJ!F%&sLnH#O5Nsj*_?(*YODE7vqK+B~Hnt5iqOLG`A<(=D
z&`4JbRNw5-=9@C1Wv_hoAb!Uw`7+;)lx(51tr<qC5<ZwJtg2$wnZ+7ls8M=-*G<%9
zOTa9NxUxgkIxjV%+Y7u{;v^*1$F!?F2}#j>qFk;apa1e>#>lw70!J||^DK99=H@A4
zYxw5#JWVLHgE=L#K0Pm9p<9)EPa2wt=rW;WR4jlZBU@SjegZvnOPE{Klp`Fo&2-{!
z9e1arh4&4bbmL8ZAZ=_NNyIo=!h4R0f(E_%{loi<z3216M)9`({JHP3Y;A#@fsQ9G
zye<PO;E-F=lTI8)?TQK<3iu)VA&k;<6wr{k)XVpID(@E_wCr}@^WE7a&|}M({^Nbi
zm{q^NtAyRPzBBgq;pnaU<I|DyMGwUfizcOk6=Tw`KQSm#l14oz49A0Hqe;*FV{znU
zW!jl&f_3B>#VIaUI?k)&!zQkf#1-PKrAAu<j@&zB;NCL%&SToehy|fCsQBD=WN2AX
zaFu(VincvL1NnssYemz(FLj29NqLcS47)C`!fGHGZP5koKrL}UXBl1SqU2}4z%uh2
zo8n3pKY7>}@;=me)ZV*4>Qg?jR4GP-WD2892L<XO>jt=w0@^9@hs0selIdoL{+dd6
zLBG6QKBR%R1?SYczSb<6q639A3L6iQiqT@d5NWg-NLJSd6jeq|(oolP6sbodJXsw4
zUkIqxT|Zlx5D4gs+`dZwGS2+HX1}W#4Zm5uH_}n~@j09==<PZ|f2d0B^1jUki(g5Y
z3l~!nMn0M(OX`rJ*W1UEkAB|$y1Nb_rx)?Fyb$vvvXF$U7J}qSPOV8zyYBKg=AI)D
zcv=GC)y=W*C>+jC7jLm+8(g0ECcu2Zz=1>in-{kB?1aBqSS+P|A8Two=aKn@+&vxj
z1{W;|8yjJaLvlg;jg6?|gk$k0QLDahT;va)ZECWjS253e^Auj=xJNs8_FHS4kM7E1
zXz0s*l&^nx{Y_rO_U;##|3gDIllF<f`_vL#TB;lqcY^s#Sj-^$p@`tenyo@Zq<XaS
zq4r22MwtdqbP4eCCxW2k>i0^zR1#;|Tc6SjX;q9aWq5}=6OY*W^!9TxB2yNjA0@1X
zHT(6QewPH}Ab%6gPu;JxvO&yeLRii(<nN?}JtPuxhERGRqXOejwFNBg+RS7-Cn-4v
zu)-hlGc2zC<XsRDrpsedMtC*Q3k>@^<W;TblvUr=Tr^{N^~NDBGX`%czb~{-oFn|2
zDH}9y6h(pxl<CBgtGDRLnRc^E){h9Aiom;U8{LV6vC-lH+~7~(9P@OUW_MI)KPb=9
z=X}ea*G@rq(wbeaelu%6`-BK7j^t}hf1!n;0pAH4LRG6dvouS&JJqm5)enNUWA+2W
zgvA?xDJin@pWjETCxCsHspl>lVmQ*pGc<k+o0v0xL_4+8<dh5~zKK7dTlYyXiQ!3L
z^We+=YKke1FOzl0>@sX5VzMFU95R!4(UrOTT{c(%%iNdG4u@AaW@GI8j6FBP?#!w&
zqDh&nJGJ<omP0m(%sm@CS*qe|zRP}H5?DPs)wEH@9>#93I*l-So5qv`gIiN{!Y<)7
z>Ll?|9&5ZtT9MX>vsyen{Y9T#Ni8X>Q(npX6250T!#9M+<<O(2-=VneC;#-}<=Afg
zPGQpEQv@snFM9cN)xL&G@@WM3&sk||(@ON+7}?<jis7CICbL*l`@^&9qOF*SwQX;v
z*^12=T(9$z*~xX#8|$k2Q1xr^fTUmD-u~Sam9`4Tquomb+N&^MY>H_RIbdfIy_nef
zT31fIe^k*tDwJgS&nA|f`@M0)S7~;z>bNUTd{>&%!=q`uwnnV7V?YPAD-%-5bYY#K
zeM88zb50|^XMCu`H3J01ag{`*6haicHzOT9oV-KIli3d<I3`!N2Bcdcq?^6&fw_E(
z3v_itG7>wOz4pC>bY#F0=HNMru^<MkDLqR(<_X+qgY=IKPD?(8XSlRB#|rKaruo_z
zDt(Fqrc|#PXgsvdKC_!PP!RRLDM&)xRPnM9My%)et0dn@`Kk2D@FY?6d9|5&du-Ia
zo?Yxsc754v8U0P`?{ewbnpx_YCu7|N=?mDyA3qM2mCuRnS&=jL@hJ@wyhBZiGnJXY
zDsQLam!AzQLI#gD;4KF=!JK%X<zWy~nB}QbQ7;6n5tC6$h9o2=42E(qTWKCQ3fH8l
zt%hUsZEDnNNJp#GII@x`Y7TkU`Q`_Of?3c$35<iCKQtWd79D;5CYw?nGdW{w0eG>r
zbi!xUj=0F)KW%*_Qk$d+vlFHBbtgR~qKmHS`5s<y#a?|f6KmDB%_z_N7U$1=1qt?4
zCQS(j9`koyAc6Sb380;=GuYOd#m30O_Mb^g<$H_&lK`qM*)H+C_P)_X3-VUNj_^>d
zriDc5z)L~Gv#!fTWMqvqQ1$Dnz*V4ujvFNcsKsJ_h>Z-D*=}YUM?!-?omf?Qs-7HB
z=fL9khn*hd7fqW}u|u*AT7HxaQr_8#ake`~<rQ8|%Fd#bh3<+a#*R7tGYT+cYHK>2
zuxY)O@nDW$0148Sqsy@(715{a9915Wsx}rP1&YALyj)l*S=b~cuk{-qT7VTTxYQN<
zW_LI@9Hy7+!wt!LdAd7vyyhslq$~*kcC>&$Q>h+iy|9<&T8nt7ZuQlfPB;&WLNfKP
zyq^%}E6hXJ3GEr~P599Cv~7_pL@jU9QPH6Xo~j~R^_8`i3RY&zanvvFBC$_5YZ~Hg
zKayAIdsa1&r0P{Pg5_<yAxJ4g;qB9m^*Vb7QEK=L!jko0W|OVT!HMwG!lW^NLf%;_
zj*e`%ZqieA4m!_x<1hnVLI<XD%(a;F6n%JGt8S<ImISN3s4-aDT6ClOo$6kvS$CWr
z?N$q?c10<`?M3yh3S>s_D`MFbs@j_micvwCmp^?9V2c70jNWKdwGTV1g;)$aK&>q%
zpsguTJaT6|w`M&>=?W&&uqFULXsA-!V}8?gc_8%(qf|&eg4WwQ!-v4uF%_e6KccVy
z7}38_J9r4LAUCEIg=tA@PMlb_;>Lv6CHB{YD6LvcITteR9I;vY^p?THSH5w5RmOaV
zcH`8nuz{C87*5O2v|De$KocnKUM8eN6L)5*Mm$7ghUZq-k`$|<xH*KVE;KaOT=;g3
zwV-aJ`_?`BY6XUIk~(mO;GxfyW$FqmERF^^v%mpC6f<9F`lxwRQ`WyMihrfpx$*(e
zjdj$@Erq?d+BQ_$ey(rFZ>ld}Lo~p~p8&Y^tf@V$Vw+VSVWJ{pUV4T}N0J{^c;C~|
z^5Lk%hN!}0a&Ki)b(|=!icuZAfRC*gjXt(63SKxSEjVz%<+DjhGh|y`<(S#Krg#vM
z?WR(tTP2#RH+n%?m>;XEUoTzVZW@^=MAU~fz2(k_O}fMGPfgLq%*eb$G2|rpt(5TJ
zMHTkHXv)IY1nkCa?rdZI4_U2<OaE0?Me$t<z2vX|sH?iAF$ZP|hx$x#aO5NBkPL@^
z)aJ<rJIr4l1W(PH2%cE5znuA^^_0c|tKM=2)Icm|;7zoI(^LJlpXY3PnNpe+q6PLf
zr3Q@*Sz?hXw|RQi4{Hle1e1BoVr+pD&^Ou&XX1`#*Tv<TML(Ka$tV@<X?Cm;&&(Oc
z7L3V}sdCPi-0ir>dMGjAdNcGkwFh5K5L^lik>~|bZLx*F!F-*-BdS=#(D#roAn7$8
z^S$&%O_K3b79^M0mn9<-6}9!Q05k+?vn5mwrw9@KYzy0nzz2YD+?*5kLXJ*Fxt428
z(`Z9A<@V=Yx_HqSr5*d$>Q<?1=+p}qu0;KSFLmH}zyxJlG#)%cd<H1}ug%{Rzky+g
z-xm7{``69i|E^k@ZA_CUhiwa3aS_)A6}w22f^s<t=up{3hPJ2dtw>x6ty#Oc`xHK0
z{F0v;mKp`t@lV|``QDH7f#E6Nk?AWjAe*VqPCR_6m8MHC4?;<sYgpaYs*!ex`smW7
zLm^9Fe7HzjzL5Z?P1)FR5>{6XIe=_7Sp0Mfe}|4U8Z6G1K{vmxIArBPQ(3`4?8*|n
zK$5kx5Xk`)1)8pPn1P4srVdihY(*y{GY6fj$vh_eKL(>@+w)!cqDoXYeMvx0yLcm7
zebHdJ9Jw2Pin&E58rIv8uzum!L@Q&(oIm_cbJKRD0JDWKgEcmHnK}B>HuRN;DijzU
z99FRkTd}b|lV&QttHbZF2tKdE7^C`BE2$OD*Opk{{#IH2tP`0-r}By+rmY>l^GBQL
zw(kb_0yAai$tWB0%^i$n*C$>n2iL${I-?iW4`h7Mn__(}fkv7h4ynl3iF0;cIWRmY
z=zs=r3X7u2=Fk3xg3ybmGmiw!|Lr1ExD<QK-!u2$2hQK^&pVl-j>(P7GSV^0%%aOO
z(T|Oc*Qv11aI9aru+6#*$|*82PhQE749mf>f5WASRIO_b5Qra^k)@9xLZ_sp7_My~
zv9W0m5F|!tOKM5uqd^cuAV{GqHgn@gEWuNAGutt3#x>DGTy*?R6eU?0SX{V&|MS2<
zhaU{!-~3AO2LdVFzi+7hG5%Ow=+BG}_)pqv{|fkbZJ0j+r7;2jX;bE3LI185@h52d
z?>GPdHzxiS|L;<Vf8rne%e(>nr-{VBg8p6X{1Y^r81Vmyq<?k#UxUV<PJbf#`w;S1
hguin7PlQ~uzh$_RECM3nAIp&eZoePPPyWa3zW^d4D+K@m
--- a/toolkit/components/passwordmgr/test/browser/head.js
+++ b/toolkit/components/passwordmgr/test/browser/head.js
@@ -1,10 +1,11 @@
 const DIRECTORY_PATH = "/browser/toolkit/components/passwordmgr/test/browser/";
 
+Cu.import("resource://gre/modules/LoginHelper.jsm", this);
 Cu.import("resource://testing-common/LoginTestUtils.jsm", this);
 Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
 
 add_task(async function common_initialize() {
   await SpecialPowers.pushPrefEnv({"set": [["signon.rememberSignons", true]]});
 });
 
 registerCleanupFunction(async function cleanup_removeAllLoginsAndResetRecipes() {
--- a/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
+++ b/tools/lint/eslint/eslint-plugin-mozilla/lib/helpers.js
@@ -19,16 +19,17 @@ var directoryManifests = new Map();
 const callExpressionDefinitions = [
   /^loader\.lazyGetter\(this, "(\w+)"/,
   /^loader\.lazyImporter\(this, "(\w+)"/,
   /^loader\.lazyServiceGetter\(this, "(\w+)"/,
   /^loader\.lazyRequireGetter\(this, "(\w+)"/,
   /^XPCOMUtils\.defineLazyGetter\(this, "(\w+)"/,
   /^XPCOMUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
   /^XPCOMUtils\.defineLazyPreferenceGetter\(this, "(\w+)"/,
+  /^XPCOMUtils\.defineLazyScriptGetter\(this, "(\w+)"/,
   /^XPCOMUtils\.defineLazyServiceGetter\(this, "(\w+)"/,
   /^XPCOMUtils\.defineConstant\(this, "(\w+)"/,
   /^DevToolsUtils\.defineLazyModuleGetter\(this, "(\w+)"/,
   /^DevToolsUtils\.defineLazyGetter\(this, "(\w+)"/,
   /^Object\.defineProperty\(this, "(\w+)"/,
   /^Reflect\.defineProperty\(this, "(\w+)"/,
   /^this\.__defineGetter__\("(\w+)"/
 ];
@@ -98,16 +99,18 @@ module.exports = {
         var args = node.arguments.map(a => this.getASTSource(a)).join(", ");
         return this.getASTSource(node.callee) + "(" + args + ")";
       case "ObjectExpression":
         return "{}";
       case "ExpressionStatement":
         return this.getASTSource(node.expression) + ";";
       case "FunctionExpression":
         return "function() {}";
+      case "ArrayExpression":
+        return "[" + node.elements.map(this.getASTSource, this).join(",") + "]";
       case "ArrowFunctionExpression":
         return "() => {}";
       case "AssignmentExpression":
         return this.getASTSource(node.left) + " = " +
           this.getASTSource(node.right);
       default:
         throw new Error("getASTSource unsupported node type: " + node.type);
     }
@@ -273,16 +276,24 @@ module.exports = {
 
     for (let reg of callExpressionDefinitions) {
       let match = source.match(reg);
       if (match) {
         return [{ name: match[1], writable: true }];
       }
     }
 
+    if (node.expression.callee.type == "MemberExpression" &&
+        node.expression.callee.property.type == "Identifier" &&
+        node.expression.callee.property.name == "defineLazyScriptGetter") {
+      // The case where we have a single symbol as a string has already been
+      // handled by the regexp, so we have an array of symbols here.
+      return node.expression.arguments[1].elements.map(n => ({ name: n.value, writable: true }));
+    }
+
     return [];
   },
 
   /**
    * Add a variable to the current scope.
    * HACK: This relies on eslint internals so it could break at any time.
    *
    * @param {String} name