Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 03 Apr 2017 14:50:18 -0700
changeset 350987 b5d8b27a753725c1de41ffae2e338798f3b5cacd
parent 350963 2a593ea93f6637df49eedc998b1f5ae4781a0f56 (current diff)
parent 350986 d4e6fb6fb40afe99415863dcdc771dcd6f6e9e02 (diff)
child 351030 381d637f43a18df25ccd7e759793080cff040d66
child 351065 a31c259f06ac5ca1f5211cc1b66724b07b0176a7
push id31597
push userkwierso@gmail.com
push dateMon, 03 Apr 2017 21:50:26 +0000
treeherdermozilla-central@b5d8b27a7537 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone55.0a1
first release with
nightly linux32
b5d8b27a7537 / 55.0a1 / 20170404100210 / files
nightly linux64
b5d8b27a7537 / 55.0a1 / 20170404100210 / files
nightly mac
b5d8b27a7537 / 55.0a1 / 20170404030204 / files
nightly win32
b5d8b27a7537 / 55.0a1 / 20170404030204 / files
nightly win64
b5d8b27a7537 / 55.0a1 / 20170404030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to central, a=merge
.eslintignore
browser/app/firefox.exe.manifest
ipc/app/plugin-container.exe.manifest
modules/libpref/init/all.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -53,17 +53,16 @@ b2g/graphene/graphene.js
 b2g/locales/en-US/b2g-l10n.js
 
 # browser/ exclusions
 browser/app/**
 browser/branding/**/firefox-branding.js
 browser/base/content/test/general/file_csp_block_all_mixedcontent.html
 browser/base/content/test/urlbar/file_blank_but_not_blank.html
 browser/base/content/newtab/**
-browser/components/downloads/**
 # Test files that are really json not js, and don't need to be linted.
 browser/components/sessionstore/test/unit/data/sessionstore_valid.js
 browser/components/sessionstore/test/unit/data/sessionstore_invalid.js
 browser/components/tabview/**
 # generated & special files in cld2
 browser/components/translation/cld2/**
 browser/extensions/pdfjs/content/build**
 browser/extensions/pdfjs/content/web**
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -32,17 +32,21 @@ CXXFLAGS += -mno-sse -mno-sse2 -mfpmath=
 CXX += -march=pentiumpro
 endif
 
 ifeq ($(OS_ARCH),WINNT)
 # Rebuild firefox.exe if the manifest changes - it's included by splash.rc.
 # (this dependency should really be just for firefox.exe, not other targets)
 # Note the manifest file exists in the tree, so we use the explicit filename
 # here.
-EXTRA_DEPS += firefox.exe.manifest
+ifdef HAVE_64BIT_BUILD
+EXTRA_DEPS += firefox.exe.64.manifest
+else
+EXTRA_DEPS += firefox.exe.32.manifest
+endif
 endif
 
 PROGRAMS_DEST = $(DIST)/bin
 
 include $(topsrcdir)/config/rules.mk
 
 ifneq (,$(filter-out WINNT,$(OS_ARCH)))
 
rename from browser/app/firefox.exe.manifest
rename to browser/app/firefox.exe.32.manifest
--- a/browser/app/firefox.exe.manifest
+++ b/browser/app/firefox.exe.32.manifest
@@ -14,16 +14,22 @@
                         name="Microsoft.Windows.Common-Controls"
                         version="6.0.0.0"
                         processorArchitecture="*"
                         publicKeyToken="6595b64144ccf1df"
                         language="*"
                 />
         </dependentAssembly>
 </dependency>
+<comInterfaceExternalProxyStub
+    iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
+    name="IAccessible"
+    tlbid="{1EA4DBF0-3C3B-11CF-810C-00AA00389B71}"
+/>
 <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
   <ms_asmv3:security>
     <ms_asmv3:requestedPrivileges>
       <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
     </ms_asmv3:requestedPrivileges>
   </ms_asmv3:security>
 </ms_asmv3:trustInfo>
   <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
copy from browser/app/firefox.exe.manifest
copy to browser/app/firefox.exe.64.manifest
--- a/browser/app/splash.rc
+++ b/browser/app/splash.rc
@@ -1,17 +1,22 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <windows.h>
+#include "mozilla-config.h"
 #include "nsNativeAppSupportWin.h"
 
-1 24 "firefox.exe.manifest"
+#if defined(HAVE_64BIT_BUILD)
+1 24 "firefox.exe.64.manifest"
+#else
+1 24 "firefox.exe.32.manifest"
+#endif
 
 IDI_APPICON ICON FIREFOX_ICO
 IDI_DOCUMENT ICON DOCUMENT_ICO
 IDI_APPLICATION ICON FIREFOX_ICO
 IDI_NEWWINDOW ICON NEWWINDOW_ICO
 IDI_NEWTAB ICON NEWTAB_ICO
 IDI_PBMODE ICON PBMODE_ICO
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1335,33 +1335,34 @@ var gBrowserInit = {
         }
       } else if (window.arguments.length >= 3) {
         // window.arguments[2]: referrer (nsIURI | string)
         //                 [3]: postData (nsIInputStream)
         //                 [4]: allowThirdPartyFixup (bool)
         //                 [5]: referrerPolicy (int)
         //                 [6]: userContextId (int)
         //                 [7]: originPrincipal (nsIPrincipal)
+        //                 [8]: triggeringPrincipal (nsIPrincipal)
         let referrerURI = window.arguments[2];
         if (typeof(referrerURI) == "string") {
           try {
             referrerURI = makeURI(referrerURI);
           } catch (e) {
             referrerURI = null;
           }
         }
         let referrerPolicy = (window.arguments[5] != undefined ?
             window.arguments[5] : Ci.nsIHttpChannel.REFERRER_POLICY_UNSET);
         let userContextId = (window.arguments[6] != undefined ?
             window.arguments[6] : Ci.nsIScriptSecurityManager.DEFAULT_USER_CONTEXT_ID);
         loadURI(uriToLoad, referrerURI, window.arguments[3] || null,
                 window.arguments[4] || false, referrerPolicy, userContextId,
                 // pass the origin principal (if any) and force its use to create
                 // an initial about:blank viewer if present:
-                window.arguments[7], !!window.arguments[7]);
+                window.arguments[7], !!window.arguments[7], window.arguments[8]);
         window.focus();
       } else {
         // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
         // Such callers expect that window.arguments[0] is handled as a single URI.
         loadOneOrMoreURIs(uriToLoad);
       }
     }
 
@@ -2243,25 +2244,27 @@ function BrowserCloseTabOrWindow() {
 }
 
 function BrowserTryToCloseWindow() {
   if (WindowIsClosing())
     window.close();     // WindowIsClosing does all the necessary checks
 }
 
 function loadURI(uri, referrer, postData, allowThirdPartyFixup, referrerPolicy,
-                 userContextId, originPrincipal, forceAboutBlankViewerInCurrent) {
+                 userContextId, originPrincipal, forceAboutBlankViewerInCurrent,
+                 triggeringPrincipal) {
   try {
     openLinkIn(uri, "current",
                { referrerURI: referrer,
                  referrerPolicy,
                  postData,
                  allowThirdPartyFixup,
                  userContextId,
                  originPrincipal,
+                 triggeringPrincipal,
                  forceAboutBlankViewerInCurrent,
                });
   } catch (e) {}
 }
 
 /**
  * Given a string, will generate a more appropriate urlbar value if a Places
  * keyword or a search alias is found at the beginning of it.
@@ -5777,16 +5780,17 @@ function handleLinkClick(event, href, li
   urlSecurityCheck(href, doc.nodePrincipal);
   let params = {
     charset: doc.characterSet,
     allowMixedContent: persistAllowMixedContentInChildTab,
     referrerURI,
     referrerPolicy,
     noReferrer: BrowserUtils.linkHasNoReferrer(linkNode),
     originPrincipal: doc.nodePrincipal,
+    triggeringPrincipal: doc.nodePrincipal,
     frameOuterWindowID,
   };
 
   // The new tab/window must use the same userContextId
   if (doc.nodePrincipal.originAttributes.userContextId) {
     params.userContextId = doc.nodePrincipal.originAttributes.userContextId;
   }
 
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -573,16 +573,17 @@ var ClickEventHandler = {
         const sm = Services.scriptSecurityManager;
         try {
           let targetURI = BrowserUtils.makeURI(href);
           sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
           json.allowMixedContent = true;
         } catch (e) {}
       }
       json.originPrincipal = ownerDoc.nodePrincipal;
+      json.triggeringPrincipal = ownerDoc.nodePrincipal;
 
       sendAsyncMessage("Content:Click", json);
       return;
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
       sendAsyncMessage("Content:Click", json);
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -980,16 +980,17 @@ nsContextMenu.prototype = {
   _isProprietaryDRM() {
     return this.target.isEncrypted && this.target.mediaKeys &&
            this.target.mediaKeys.keySystem != "org.w3.clearkey";
   },
 
   _openLinkInParameters(extra) {
     let params = { charset: gContextMenuContentData.charSet,
                    originPrincipal: this.principal,
+                   triggeringPrincipal: this.principal,
                    referrerURI: gContextMenuContentData.documentURIObject,
                    referrerPolicy: gContextMenuContentData.referrerPolicy,
                    frameOuterWindowID: gContextMenuContentData.frameOuterWindowID,
                    noReferrer: this.linkHasNoReferrer };
     for (let p in extra) {
       params[p] = extra[p];
     }
 
@@ -1169,17 +1170,17 @@ nsContextMenu.prototype = {
   // Change current window to the URL of the image, video, or audio.
   viewMedia(e) {
     let referrerURI = gContextMenuContentData.documentURIObject;
     let systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
     if (this.onCanvas) {
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         openUILink(blobURL, e, { disallowInheritPrincipal: true,
                                  referrerURI,
-                                 originPrincipal: systemPrincipal});
+                                 triggeringPrincipal: systemPrincipal});
       }, Cu.reportError);
     } else {
       urlSecurityCheck(this.mediaURL,
                        this.browser.contentPrincipal,
                        Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
       openUILink(this.mediaURL, e, { disallowInheritPrincipal: true,
                                      referrerURI });
     }
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -215,16 +215,17 @@ function openLinkIn(url, where, params) 
   var aIsPrivate            = params.private;
   var aSkipTabAnimation     = params.skipTabAnimation;
   var aAllowPinnedTabHostChange = !!params.allowPinnedTabHostChange;
   var aNoReferrer           = params.noReferrer;
   var aAllowPopups          = !!params.allowPopups;
   var aUserContextId        = params.userContextId;
   var aIndicateErrorPageLoad = params.indicateErrorPageLoad;
   var aPrincipal            = params.originPrincipal;
+  var aTriggeringPrincipal  = params.triggeringPrincipal;
   var aForceAboutBlankViewerInCurrent =
       params.forceAboutBlankViewerInCurrent;
 
   if (where == "save") {
     // TODO(1073187): propagate referrerPolicy.
 
     // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
     if ("isContentWindowPrivate" in params) {
@@ -255,23 +256,28 @@ function openLinkIn(url, where, params) 
     aRelatedToCurrent = false;
   }
 
   // Teach the principal about the right OA to use, e.g. in case when
   // opening a link in a new private window, or in a new container tab.
   // Please note we do not have to do that for SystemPrincipals and we
   // can not do it for NullPrincipals since NullPrincipals are only
   // identical if they actually are the same object (See Bug: 1346759)
-  if (aPrincipal && aPrincipal.isCodebasePrincipal) {
-    let attrs = {
-      userContextId: aUserContextId,
-      privateBrowsingId: aIsPrivate || (w && PrivateBrowsingUtils.isWindowPrivate(w)),
-    };
-    aPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(aPrincipal.URI, attrs);
+  function useOAForPrincipal(principal) {
+    if (principal && principal.isCodebasePrincipal) {
+      let attrs = {
+        userContextId: aUserContextId,
+        privateBrowsingId: aIsPrivate || (w && PrivateBrowsingUtils.isWindowPrivate(w)),
+      };
+      return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
+    }
+    return principal;
   }
+  aPrincipal = useOAForPrincipal(aPrincipal);
+  aTriggeringPrincipal = useOAForPrincipal(aTriggeringPrincipal);
 
   if (!w || where == "window") {
     // This propagates to window.arguments.
     var sa = Cc["@mozilla.org/array;1"].
              createInstance(Ci.nsIMutableArray);
 
     var wuri = Cc["@mozilla.org/supports-string;1"].
                createInstance(Ci.nsISupportsString);
@@ -306,16 +312,17 @@ function openLinkIn(url, where, params) 
     sa.appendElement(wuri, /* weak =*/ false);
     sa.appendElement(charset, /* weak =*/ false);
     sa.appendElement(referrerURISupports, /* weak =*/ false);
     sa.appendElement(aPostData, /* weak =*/ false);
     sa.appendElement(allowThirdPartyFixupSupports, /* weak =*/ false);
     sa.appendElement(referrerPolicySupports, /* weak =*/ false);
     sa.appendElement(userContextIdSupports, /* weak =*/ false);
     sa.appendElement(aPrincipal, /* weak =*/ false);
+    sa.appendElement(aTriggeringPrincipal, /* weak =*/ false);
 
     let features = "chrome,dialog=no,all";
     if (aIsPrivate) {
       features += ",private";
     }
 
     const sourceWindow = (w || window);
     let win;
@@ -409,17 +416,17 @@ function openLinkIn(url, where, params) 
       flags |= Ci.nsIWebNavigation.LOAD_FLAGS_ERROR_LOAD_CHANGES_RV;
     }
 
     if (aForceAboutBlankViewerInCurrent) {
       targetBrowser.createAboutBlankContentViewer(aPrincipal);
     }
 
     targetBrowser.loadURIWithFlags(url, {
-      triggeringPrincipal: aPrincipal,
+      triggeringPrincipal: aTriggeringPrincipal,
       flags,
       referrerURI: aNoReferrer ? null : aReferrerURI,
       referrerPolicy: aReferrerPolicy,
       postData: aPostData,
       userContextId: aUserContextId
     });
     break;
   case "tabshifted":
@@ -434,17 +441,17 @@ function openLinkIn(url, where, params) 
       inBackground: loadInBackground,
       allowThirdPartyFixup: aAllowThirdPartyFixup,
       relatedToCurrent: aRelatedToCurrent,
       skipAnimation: aSkipTabAnimation,
       allowMixedContent: aAllowMixedContent,
       noReferrer: aNoReferrer,
       userContextId: aUserContextId,
       originPrincipal: aPrincipal,
-      triggeringPrincipal: aPrincipal,
+      triggeringPrincipal: aTriggeringPrincipal,
     });
     targetBrowser = tabUsedForLoad.linkedBrowser;
 
     if (params.frameOuterWindowID && w) {
       // Only notify it as a WebExtensions' webNavigation.onCreatedNavigationTarget
       // event if it contains the expected frameOuterWindowID params.
       // (e.g. we should not notify it as a onCreatedNavigationTarget if the user is
       // opening a new tab using the keyboard shortcut).
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -25,18 +25,17 @@ this.EXPORTED_SYMBOLS = [
  * to build a consistent view of the available data.
  *
  * DownloadsIndicatorData
  * This object registers itself with DownloadsData as a view, and transforms the
  * notifications it receives into overall status data, that is then broadcast to
  * the registered download status indicators.
  */
 
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
+// Globals
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
@@ -106,40 +105,39 @@ var PrefObserver = {
           return kPrefBranch.getBoolPref(name);
       }
     } catch (ex) { }
     return this.prefs[name];
   },
   observe(aSubject, aTopic, aData) {
     if (this.prefs.hasOwnProperty(aData)) {
       delete this[aData];
-      return this[aData] = this.getPref(aData);
+      this[aData] = this.getPref(aData);
     }
   },
   register(prefs) {
     this.prefs = prefs;
     kPrefBranch.addObserver("", this, true);
     for (let key in prefs) {
       let name = key;
-      XPCOMUtils.defineLazyGetter(this, name, function () {
+      XPCOMUtils.defineLazyGetter(this, name, function() {
         return PrefObserver.getPref(name);
       });
     }
   },
 };
 
 PrefObserver.register({
   // prefName: defaultValue
   animateNotifications: true,
   showPanelDropmarker: true,
 });
 
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsCommon
+// DownloadsCommon
 
 /**
  * This object is exposed directly to the consumers of this JavaScript module,
  * and provides shared methods for all the instances of the user interface.
  */
 this.DownloadsCommon = {
   ATTENTION_NONE: "",
   ATTENTION_SUCCESS: "success",
@@ -161,24 +159,24 @@ this.DownloadsCommon = {
   get strings() {
     let strings = {};
     let sb = Services.strings.createBundle(kDownloadsStringBundleUrl);
     let enumerator = sb.getSimpleEnumeration();
     while (enumerator.hasMoreElements()) {
       let string = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement);
       let stringName = string.key;
       if (stringName in kDownloadsStringsRequiringFormatting) {
-        strings[stringName] = function () {
+        strings[stringName] = function() {
           // Convert "arguments" to a real array before calling into XPCOM.
           return sb.formatStringFromName(stringName,
                                          Array.slice(arguments, 0),
                                          arguments.length);
         };
       } else if (stringName in kDownloadsStringsRequiringPluralForm) {
-        strings[stringName] = function (aCount) {
+        strings[stringName] = function(aCount) {
           // Convert "arguments" to a real array before calling into XPCOM.
           let formattedString = sb.formatStringFromName(stringName,
                                          Array.slice(arguments, 0),
                                          arguments.length);
           return PluralForm.get(aCount, formattedString);
         };
       } else {
         strings[stringName] = string.value;
@@ -238,19 +236,18 @@ this.DownloadsCommon = {
    * depending on the privacy status of the window in question.
    *
    * @param aWindow
    *        The browser window which owns the download button.
    */
   getData(aWindow) {
     if (PrivateBrowsingUtils.isContentWindowPrivate(aWindow)) {
       return PrivateDownloadsData;
-    } else {
-      return DownloadsData;
     }
+    return DownloadsData;
   },
 
   /**
    * Initializes the Downloads back-end and starts receiving events for both the
    * private and non-private downloads data objects.
    */
   initializeAllDataLinks() {
     DownloadsData.initializeDataLink();
@@ -260,19 +257,18 @@ this.DownloadsCommon = {
   /**
    * Get access to one of the DownloadsIndicatorData or
    * PrivateDownloadsIndicatorData objects, depending on the privacy status of
    * the window in question.
    */
   getIndicatorData(aWindow) {
     if (PrivateBrowsingUtils.isContentWindowPrivate(aWindow)) {
       return PrivateDownloadsIndicatorData;
-    } else {
-      return DownloadsIndicatorData;
     }
+    return DownloadsIndicatorData;
   },
 
   /**
    * Returns a reference to the DownloadsSummaryData singleton - creating one
    * in the process if one hasn't been instantiated yet.
    *
    * @param aWindow
    *        The browser window which owns the download button.
@@ -281,22 +277,21 @@ this.DownloadsCommon = {
    *        from the summary.
    */
   getSummary(aWindow, aNumToExclude) {
     if (PrivateBrowsingUtils.isContentWindowPrivate(aWindow)) {
       if (this._privateSummary) {
         return this._privateSummary;
       }
       return this._privateSummary = new DownloadsSummaryData(true, aNumToExclude);
-    } else {
-      if (this._summary) {
-        return this._summary;
-      }
-      return this._summary = new DownloadsSummaryData(false, aNumToExclude);
     }
+    if (this._summary) {
+      return this._summary;
+    }
+    return this._summary = new DownloadsSummaryData(false, aNumToExclude);
   },
   _summary: null,
   _privateSummary: null,
 
   /**
    * Returns the legacy state integer value for the provided Download object.
    */
   stateOfDownload(download) {
@@ -661,27 +656,26 @@ XPCOMUtils.defineLazyGetter(this.Downloa
 });
 XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => {
   return DownloadsLogger.error.bind(DownloadsLogger);
 });
 
 /**
  * Returns true if we are executing on Windows Vista or a later version.
  */
-XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
+XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function() {
   let os = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
   if (os != "WINNT") {
     return false;
   }
   let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
   return parseFloat(sysInfo.getProperty("version")) >= 6;
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsData
+// DownloadsData
 
 /**
  * Retrieves the list of past and completed downloads from the underlying
  * Download Manager data, and provides asynchronous notifications allowing to
  * build a consistent view of the available data.
  *
  * This object responds to real-time changes in the underlying Download Manager
  * data.  For example, the deletion of one or more downloads is notified through
@@ -751,18 +745,17 @@ DownloadsDataCtor.prototype = {
                                                         : Downloads.PUBLIC);
     promiseList.then(list => list.removeFinished())
                .then(null, Cu.reportError);
     let indicatorData = this._isPrivate ? PrivateDownloadsIndicatorData
                                         : DownloadsIndicatorData;
     indicatorData.attention = DownloadsCommon.ATTENTION_NONE;
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Integration with the asynchronous Downloads back-end
+  // Integration with the asynchronous Downloads back-end
 
   onDownloadAdded(download) {
     // Download objects do not store the end time of downloads, as the Downloads
     // API does not need to persist this information for all platforms. Once a
     // download terminates on a Desktop browser, it becomes a history download,
     // for which the end time is stored differently, as a Places annotation.
     download.endTime = Date.now();
 
@@ -842,18 +835,17 @@ DownloadsDataCtor.prototype = {
   onDownloadRemoved(download) {
     this.oldDownloadStates.delete(download);
 
     for (let view of this._views) {
       view.onDownloadRemoved(download);
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Registration of views
+  // Registration of views
 
   /**
    * Adds an object to be notified when the available download data changes.
    * The specified object is initialized with the currently available downloads.
    *
    * @param aView
    *        DownloadsView object to be added.  This reference must be passed to
    *        removeView before termination.
@@ -891,18 +883,17 @@ DownloadsDataCtor.prototype = {
     let downloadsArray = [...this.downloads];
     downloadsArray.sort((a, b) => b.startTime - a.startTime);
     downloadsArray.forEach(download => aView.onDownloadAdded(download, false));
 
     // Notify the view that all data is available.
     aView.onDataLoadCompleted();
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Notifications sent to the most recent browser window only
+  // Notifications sent to the most recent browser window only
 
   /**
    * Set to true after the first download causes the downloads panel to be
    * displayed.
    */
   get panelHasShownBefore() {
     try {
       return Services.prefs.getBoolPref("browser.download.panel.shown");
@@ -947,26 +938,24 @@ DownloadsDataCtor.prototype = {
 XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsData", function() {
   return new DownloadsDataCtor(true);
 });
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
   return new DownloadsDataCtor(false);
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsViewPrototype
+// DownloadsViewPrototype
 
 /**
  * A prototype for an object that registers itself with DownloadsData as soon
  * as a view is registered with it.
  */
 const DownloadsViewPrototype = {
-  //////////////////////////////////////////////////////////////////////////////
-  //// Registration of views
+  // Registration of views
 
   /**
    * Array of view objects that should be notified when the available status
    * data changes.
    *
    * SUBCLASSES MUST OVERRIDE THIS PROPERTY.
    */
   _views: null,
@@ -1031,18 +1020,17 @@ const DownloadsViewPrototype = {
       if (this._isPrivate) {
         PrivateDownloadsData.removeView(this);
       } else {
         DownloadsData.removeView(this);
       }
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsData
+  // Callback functions from DownloadsData
 
   /**
    * Indicates whether we are still loading downloads data asynchronously.
    */
   _loading: false,
 
   /**
    * Called before multiple downloads are about to be loaded.
@@ -1131,18 +1119,17 @@ const DownloadsViewPrototype = {
    *
    * @note Subclasses should override this.
    */
   _updateView() {
     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
   },
 };
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsIndicatorData
+// DownloadsIndicatorData
 
 /**
  * This object registers itself with DownloadsData as a view, and transforms the
  * notifications it receives into overall status data, that is then broadcast to
  * the registered download status indicators.
  *
  * Note that using this object does not automatically start the Download Manager
  * service.  Consumers will see an empty list of downloads until the service is
@@ -1165,18 +1152,17 @@ DownloadsIndicatorDataCtor.prototype = {
   removeView(aView) {
     DownloadsViewPrototype.removeView.call(this, aView);
 
     if (this._views.length == 0) {
       this._itemCount = 0;
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsData
+  // Callback functions from DownloadsData
 
   onDataLoadCompleted() {
     DownloadsViewPrototype.onDataLoadCompleted.call(this);
     this._updateViews();
   },
 
   onDownloadAdded(download, newest) {
     this._itemCount++;
@@ -1223,18 +1209,17 @@ DownloadsIndicatorDataCtor.prototype = {
     this._updateViews();
   },
 
   onDownloadRemoved(download) {
     this._itemCount--;
     this._updateViews();
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Propagation of properties to our views
+  // Propagation of properties to our views
 
   // The following properties are updated by _refreshProperties and are then
   // propagated to the views.  See _refreshProperties for details.
   _hasDownloads: false,
   _counter: "",
   _percentComplete: -1,
   _paused: false,
 
@@ -1283,18 +1268,17 @@ DownloadsIndicatorDataCtor.prototype = {
     aView.hasDownloads = this._hasDownloads;
     aView.counter = this._counter;
     aView.percentComplete = this._percentComplete;
     aView.paused = this._paused;
     aView.attention = this._attentionSuppressed ? DownloadsCommon.ATTENTION_NONE
                                                 : this._attention;
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Property updating based on current download status
+  // Property updating based on current download status
 
   /**
    * Number of download items that are available to be displayed.
    */
   _itemCount: 0,
 
   /**
    * Floating point value indicating the last number of seconds estimated until
@@ -1365,18 +1349,17 @@ DownloadsIndicatorDataCtor.prototype = {
 XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsIndicatorData", function() {
   return new DownloadsIndicatorDataCtor(true);
 });
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsIndicatorData", function() {
   return new DownloadsIndicatorDataCtor(false);
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsSummaryData
+// DownloadsSummaryData
 
 /**
  * DownloadsSummaryData is a view for DownloadsData that produces a summary
  * of all downloads after a certain exclusion point aNumToExclude. For example,
  * if there were 5 downloads in progress, and a DownloadsSummaryData was
  * constructed with aNumToExclude equal to 3, then that DownloadsSummaryData
  * would produce a summary of the last 2 downloads.
  *
@@ -1434,20 +1417,19 @@ DownloadsSummaryData.prototype = {
 
     if (this._views.length == 0) {
       // Clear out our collection of Download objects. If we ever have
       // another view registered with us, this will get re-populated.
       this._downloads = [];
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsData - see the documentation in
-  //// DownloadsViewPrototype for more information on what these functions
-  //// are used for.
+  // Callback functions from DownloadsData - see the documentation in
+  // DownloadsViewPrototype for more information on what these functions
+  // are used for.
 
   onDataLoadCompleted() {
     DownloadsViewPrototype.onDataLoadCompleted.call(this);
     this._updateViews();
   },
 
   onDownloadAdded(download, newest) {
     if (newest) {
@@ -1470,18 +1452,17 @@ DownloadsSummaryData.prototype = {
   },
 
   onDownloadRemoved(download) {
     let itemIndex = this._downloads.indexOf(download);
     this._downloads.splice(itemIndex, 1);
     this._updateViews();
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Propagation of properties to our views
+  // Propagation of properties to our views
 
   /**
    * Computes aggregate values and propagates the changes to our views.
    */
   _updateViews() {
     // Do not update the status indicators during batch loads of download items.
     if (this._loading) {
       return;
@@ -1499,18 +1480,17 @@ DownloadsSummaryData.prototype = {
    */
   _updateView(aView) {
     aView.showingProgress = this._showingProgress;
     aView.percentComplete = this._percentComplete;
     aView.description = this._description;
     aView.details = this._details;
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Property updating based on current download status
+  // Property updating based on current download status
 
   /**
    * A generator function for the Download objects this summary is currently
    * interested in. This generator is passed off to summarizeDownloads in order
    * to generate statistics about the downloads we care about - in this case,
    * it's the downloads in this._downloads after the first few to exclude,
    * which was set when constructing this DownloadsSummaryData instance.
    */
--- a/browser/components/downloads/DownloadsTaskbar.jsm
+++ b/browser/components/downloads/DownloadsTaskbar.jsm
@@ -9,50 +9,48 @@
  */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = [
   "DownloadsTaskbar",
 ];
 
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
+// Globals
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
                                   "resource:///modules/RecentWindow.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () {
+XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function() {
   if (!("@mozilla.org/windows-taskbar;1" in Cc)) {
     return null;
   }
   let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"]
                      .getService(Ci.nsIWinTaskbar);
   return winTaskbar.available && winTaskbar;
 });
 
-XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () {
+XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function() {
   return ("@mozilla.org/widget/macdocksupport;1" in Cc) &&
          Cc["@mozilla.org/widget/macdocksupport;1"]
            .getService(Ci.nsITaskbarProgress);
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsTaskbar
+// DownloadsTaskbar
 
 /**
  * Handles the download progress indicator in the taskbar.
  */
 this.DownloadsTaskbar = {
   /**
    * Underlying DownloadSummary providing the aggregate download information, or
    * null if the indicator has never been initialized.
@@ -106,17 +104,16 @@ this.DownloadsTaskbar = {
     if (!this._summary) {
       Downloads.getSummary(Downloads.ALL).then(summary => {
         // In case the method is re-entered, we simply ignore redundant
         // invocations of the callback, instead of keeping separate state.
         if (this._summary) {
           return;
         }
         this._summary = summary;
-        return this._summary.addView(this);
       }).then(null, Cu.reportError);
     }
   },
 
   /**
    * On Windows, attaches the taskbar indicator to the specified browser window.
    */
   _attachIndicator(aWindow) {
@@ -145,18 +142,17 @@ this.DownloadsTaskbar = {
         // The last browser window has been closed.  We remove the reference to
         // the taskbar progress object so that the indicator will be registered
         // again on the next browser window that is opened.
         this._taskbarProgress = null;
       }
     });
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// DownloadSummary view
+  // DownloadSummary view
 
   onSummaryChanged() {
     // If the last browser window has been closed, we have no indicator any more.
     if (!this._taskbarProgress) {
       return;
     }
 
     if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) {
--- a/browser/components/downloads/DownloadsViewUI.jsm
+++ b/browser/components/downloads/DownloadsViewUI.jsm
@@ -44,17 +44,17 @@ this.DownloadsViewUI = {
  * from the JavaScript API for downloads, and commands are executed using a
  * combination of Download methods and DownloadsCommon.jsm helper functions.
  *
  * Specialized versions of this shell must be defined, and they are required to
  * implement the "download" property or getter. Currently these objects are the
  * HistoryDownloadElementShell and the DownloadsViewItem for the panel. The
  * history view may use a HistoryDownload object in place of a Download object.
  */
-this.DownloadsViewUI.DownloadElementShell = function () {}
+this.DownloadsViewUI.DownloadElementShell = function() {}
 
 this.DownloadsViewUI.DownloadElementShell.prototype = {
   /**
    * The richlistitem for the download, initialized by the derived object.
    */
   element: null,
 
   /**
@@ -237,20 +237,20 @@ this.DownloadsViewUI.DownloadElementShel
         stateLabel = s.stateBlockedParentalControls;
       } else if (this.download.error.becauseBlockedByReputationCheck) {
         stateLabel = this.rawBlockedTitleAndDetails[0];
       } else {
         stateLabel = s.stateFailed;
       }
 
       let referrer = this.download.source.referrer || this.download.source.url;
-      let [displayHost, fullHost] = DownloadUtils.getURIHost(referrer);
+      let [displayHost /* ,fullHost */] = DownloadUtils.getURIHost(referrer);
 
       let date = new Date(this.download.endTime);
-      let [displayDate, fullDate] = DownloadUtils.getReadableDates(date);
+      let [displayDate /* ,fullDate */] = DownloadUtils.getReadableDates(date);
 
       let firstPart = s.statusSeparator(stateLabel, displayHost);
       fullStatus = s.statusSeparator(firstPart, displayDate);
       status = status || stateLabel;
     }
 
     return {
       status,
@@ -274,18 +274,16 @@ this.DownloadsViewUI.DownloadElementShel
       case Downloads.Error.BLOCK_VERDICT_POTENTIALLY_UNWANTED:
         return [s.blockedPotentiallyUnwanted,
                 [s.unblockTypePotentiallyUnwanted2, s.unblockTip2]];
       case Downloads.Error.BLOCK_VERDICT_MALWARE:
         return [s.blockedMalware, [s.unblockTypeMalware, s.unblockTip2]];
     }
     throw new Error("Unexpected reputationCheckVerdict: " +
                     this.download.error.reputationCheckVerdict);
-    // return anyway to avoid a JS strict warning.
-    return [null, null];
   },
 
   /**
    * Shows the appropriate unblock dialog based on the verdict, and executes the
    * action selected by the user in the dialog, which may involve unblocking,
    * opening or removing the file.
    *
    * @param window
@@ -301,16 +299,17 @@ this.DownloadsViewUI.DownloadElementShel
     }).then(action => {
       if (action == "open") {
         return this.unblockAndOpenDownload();
       } else if (action == "unblock") {
         return this.download.unblock();
       } else if (action == "confirmBlock") {
         return this.download.confirmBlock();
       }
+      return Promise.resolve();
     }).catch(Cu.reportError);
   },
 
   /**
    * Unblocks the downloaded file and opens it.
    *
    * @return A promise that's resolved after the file has been opened.
    */
--- a/browser/components/downloads/content/allDownloadsViewOverlay.js
+++ b/browser/components/downloads/content/allDownloadsViewOverlay.js
@@ -1,11 +1,12 @@
 /* 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 */
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
                                   "resource://gre/modules/DownloadUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
@@ -938,19 +939,17 @@ DownloadsPlacesView.prototype = {
       delete this._resultNode;
       delete this._result;
     }
 
     return val;
   },
 
   get selectedNodes() {
-    return [for (element of this._richlistbox.selectedItems)
-            if (element._placesNode)
-            element._placesNode];
+      return this._richlistbox.selectedItems.filter(element => element._placesNode);
   },
 
   get selectedNode() {
     let selectedNodes = this.selectedNodes;
     return selectedNodes.length == 1 ? selectedNodes[0] : null;
   },
 
   get hasSelection() {
@@ -1173,18 +1172,17 @@ DownloadsPlacesView.prototype = {
       if (download.stopped && !(download.canceled && download.hasPartialData)) {
         return true;
       }
     }
     return false;
   },
 
   _copySelectedDownloadsToClipboard() {
-    let urls = [for (element of this._richlistbox.selectedItems)
-                element._shell.download.source.url];
+    let urls = this._richlistbox.selectedItems.map(element => element._shell.download.source.url);
 
     Cc["@mozilla.org/widget/clipboardhelper;1"]
       .getService(Ci.nsIClipboardHelper)
       .copyString(urls.join("\n"));
   },
 
   _getURLFromClipboardData() {
     let trans = Cc["@mozilla.org/widget/transferable;1"].
@@ -1206,17 +1204,17 @@ DownloadsPlacesView.prototype = {
         return [NetUtil.newURI(url).spec, name];
       }
     } catch (ex) {}
 
     return ["", ""];
   },
 
   _canDownloadClipboardURL() {
-    let [url, name] = this._getURLFromClipboardData();
+    let [url /* ,name */] = this._getURLFromClipboardData();
     return url != "";
   },
 
   _downloadURLFromClipboard() {
     let [url, name] = this._getURLFromClipboardData();
     let browserWin = RecentWindow.getMostRecentBrowserWindow();
     let initiatingDoc = browserWin ? browserWin.document : document;
     DownloadURL(url, name, initiatingDoc);
@@ -1303,18 +1301,17 @@ DownloadsPlacesView.prototype = {
       // supported when a single item is selected. To be consistent, do the
       // same here.
       if (selectedElements.length == 1) {
         let element = selectedElements[0];
         if (element._shell) {
           element._shell.doDefaultCommand();
         }
       }
-    }
-    else if (aEvent.charCode == " ".charCodeAt(0)) {
+    } else if (aEvent.charCode == " ".charCodeAt(0)) {
       // Pause/Resume every selected download
       for (let element of selectedElements) {
         if (element._shell.isCommandEnabled("downloadsCmd_pauseResume")) {
           element._shell.doCommand("downloadsCmd_pauseResume");
         }
       }
     }
   },
@@ -1404,17 +1401,17 @@ DownloadsPlacesView.prototype = {
       if (link.url.startsWith("about:"))
         continue;
       DownloadURL(link.url, link.name, initiatingDoc);
     }
   },
 };
 
 for (let methodName of ["load", "applyFilter", "selectNode", "selectItems"]) {
-  DownloadsPlacesView.prototype[methodName] = function () {
+  DownloadsPlacesView.prototype[methodName] = function() {
     throw new Error("|" + methodName +
                     "| is not implemented by the downloads view.");
   }
 }
 
 function goUpdateDownloadCommands() {
   function updateCommandsForObject(object) {
     for (let name in object) {
--- a/browser/components/downloads/content/contentAreaDownloadsView.js
+++ b/browser/components/downloads/content/contentAreaDownloadsView.js
@@ -1,12 +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/. */
 
+/* import-globals-from allDownloadsViewOverlay.js */
+
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 var ContentAreaDownloadsView = {
   init() {
     let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"));
     // Do not display the Places downloads in private windows
     if (!PrivateBrowsingUtils.isContentWindowPrivate(window)) {
       view.place = "place:transition=7&sort=4";
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -1,13 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint-env mozilla/browser-window */
 
 /**
  * Handles the Downloads panel user interface for each browser window.
  *
  * This file includes the following constructors and global objects:
  *
  * DownloadsPanel
  * Main entry point for the downloads panel interface.
@@ -73,25 +74,23 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "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
+// DownloadsPanel
 
 /**
  * Main entry point for the downloads panel interface.
  */
 const DownloadsPanel = {
-  //////////////////////////////////////////////////////////////////////////////
-  //// Initialization and termination
+  // 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,
 
   /** The panel is not linked to downloads data yet. */
@@ -187,18 +186,17 @@ const DownloadsPanel = {
     this._unattachEventListeners();
 
     this._state = this.kStateUninitialized;
 
     DownloadsSummary.active = false;
     DownloadsCommon.log("DownloadsPanel terminated.");
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Panel interface
+  // Panel interface
 
   /**
    * Main panel element in the browser window, or null if the panel overlay
    * hasn't been loaded yet.
    */
   get panel() {
     // If the downloads panel overlay hasn't loaded yet, just return null
     // without resetting this.panel.
@@ -303,19 +301,21 @@ const DownloadsPanel = {
    * visualization.
    */
   handleEvent(aEvent) {
     switch (aEvent.type) {
       case "mousemove":
         this.keyFocusing = false;
         break;
       case "keydown":
-        return this._onKeyDown(aEvent);
+        this._onKeyDown(aEvent);
+        break;
       case "keypress":
-        return this._onKeyPress(aEvent);
+        this._onKeyPress(aEvent);
+        break;
       case "popupshown":
         if (this.setHeightToFitOnShow) {
           this.setHeightToFitOnShow = false;
           this.setHeightToFit();
         }
         break;
     }
   },
@@ -323,28 +323,26 @@ const DownloadsPanel = {
   setHeightToFit() {
     if (this._state == this.kStateShown) {
       DownloadsBlockedSubview.view.setHeightToFit();
     } else {
       this.setHeightToFitOnShow = true;
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsView
+  // Callback functions from DownloadsView
 
   /**
    * Called after data loading finished.
    */
   onViewLoadCompleted() {
     this._openPopupIfDataReady();
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// User interface event functions
+  // User interface event functions
 
   onWindowUnload() {
     // This function is registered as an event listener, we can't use "this".
     DownloadsPanel.terminate();
   },
 
   onPopupShown(aEvent) {
     // Ignore events raised by nested popups.
@@ -402,18 +400,17 @@ const DownloadsPanel = {
       .setAttribute("showingdropdown", true);
   },
 
   onFooterPopupHidden(aEvent) {
     document.getElementById("downloadsFooter")
       .removeAttribute("showingdropdown");
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Related operations
+  // Related operations
 
   /**
    * Shows or focuses the user interface dedicated to downloads history.
    */
   showDownloadsHistory() {
     DownloadsCommon.log("Showing download history.");
     // Hide the panel before showing another window, otherwise focus will return
     // to the browser window when the panel closes automatically.
@@ -424,18 +421,17 @@ const DownloadsPanel = {
 
   openDownloadsFolder() {
     Downloads.getPreferredDownloadsDirectory().then(downloadsPath => {
       DownloadsCommon.showDirectory(new FileUtils.File(downloadsPath));
     }).catch(Cu.reportError);
     this.hidePanel();
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Internal functions
+  // Internal functions
 
   /**
    * Attach event listeners to a panel element. These listeners should be
    * removed in _unattachEventListeners. This is called automatically after the
    * panel has successfully loaded.
    */
   _attachEventListeners() {
     // Handle keydown to support accel-V.
@@ -620,18 +616,17 @@ const DownloadsPanel = {
       DownloadsCommon.log("Opening downloads panel popup.");
       this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, null);
     });
   },
 };
 
 XPCOMUtils.defineConstant(this, "DownloadsPanel", DownloadsPanel);
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsOverlayLoader
+// DownloadsOverlayLoader
 
 /**
  * Allows loading the downloads panel and the status indicator interfaces on
  * demand, to improve startup performance.
  */
 const DownloadsOverlayLoader = {
   /**
    * We cannot load two overlays at the same time, thus we use a queue of
@@ -700,27 +695,25 @@ const DownloadsOverlayLoader = {
       // for the associated overlay to load.
       this.ensureOverlayLoaded(request.overlay, request.callback);
     }
   },
 };
 
 XPCOMUtils.defineConstant(this, "DownloadsOverlayLoader", DownloadsOverlayLoader);
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsView
+// 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 = {
-  //////////////////////////////////////////////////////////////////////////////
-  //// Functions handling download items in the list
+  // Functions handling download items in the list
 
   /**
    * Maximum number of items shown by the list at any given time.
    */
   kItemCountLimit: 5,
 
   /**
    * Indicates whether there is an open contextMenu for a download item.
@@ -786,18 +779,17 @@ const DownloadsView = {
   /**
    * Element corresponding to the button for showing more downloads.
    */
   get downloadsHistory() {
     delete this.downloadsHistory;
     return this.downloadsHistory = document.getElementById("downloadsHistory");
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsData
+  // Callback functions from DownloadsData
 
   /**
    * Called before multiple downloads are about to be loaded.
    */
   onDataLoadStarting() {
     DownloadsCommon.log("onDataLoadStarting called for DownloadsView.");
     this.loading = true;
   },
@@ -913,18 +905,17 @@ const DownloadsView = {
   itemForElement(element) {
     return this._itemsForElements.get(element);
   },
 
   /**
    * Creates a new view item associated with the specified data item, and adds
    * it to the top or the bottom of the list.
    */
-  _addViewItem(download, aNewest)
-  {
+  _addViewItem(download, aNewest) {
     DownloadsCommon.log("Adding a new DownloadsViewItem to the downloads list.",
                         "aNewest =", aNewest);
 
     let element = document.createElement("richlistitem");
     let viewItem = new DownloadsViewItem(download, element);
     this._visibleViewItems.set(download, viewItem);
     this._itemsForElements.set(element, viewItem);
     if (aNewest) {
@@ -945,18 +936,17 @@ const DownloadsView = {
     if (previousSelectedIndex != -1) {
       this.richListBox.selectedIndex = Math.min(previousSelectedIndex,
                                                 this.richListBox.itemCount - 1);
     }
     this._visibleViewItems.delete(download);
     this._itemsForElements.delete(element);
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// User interface event functions
+  // User interface event functions
 
   /**
    * Helper function to do commands on a specific download item.
    *
    * @param aEvent
    *        Event object for the event being handled.  If the event target is
    *        not a richlistitem that represents a download, this function will
    *        walk up the parent nodes until it finds a DOM node that is.
@@ -1113,18 +1103,17 @@ const DownloadsView = {
     dataTransfer.addElement(element);
 
     aEvent.stopPropagation();
   },
 }
 
 XPCOMUtils.defineConstant(this, "DownloadsView", DownloadsView);
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsViewItem
+// DownloadsViewItem
 
 /**
  * Builds and updates a single item in the downloads list widget, responding to
  * changes in the download state and real-time data, and handles the user
  * interaction events related to a single item in the downloads list widgets.
  *
  * @param download
  *        Download object to be associated with the view item.
@@ -1194,18 +1183,17 @@ DownloadsViewItem.prototype = {
   },
 
   doCommand(aCommand) {
     if (this.isCommandEnabled(aCommand)) {
       this[aCommand]();
     }
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Item commands
+  // Item commands
 
   cmd_delete() {
     DownloadsCommon.removeAndFinalizeDownload(this.download);
     PlacesUtils.history.remove(this.download.source.url).catch(Cu.reportError);
   },
 
   downloadsCmd_unblock() {
     DownloadsPanel.hidePanel();
@@ -1263,38 +1251,35 @@ DownloadsViewItem.prototype = {
   downloadsCmd_doDefault() {
     let defaultCommand = this.currentDefaultCommandName;
     if (defaultCommand && this.isCommandEnabled(defaultCommand)) {
       this.doCommand(defaultCommand);
     }
   },
 };
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsViewController
+// 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 = {
-  //////////////////////////////////////////////////////////////////////////////
-  //// Initialization and termination
+  // Initialization and termination
 
   initialize() {
     window.controllers.insertControllerAt(0, this);
   },
 
   terminate() {
     window.controllers.removeController(this);
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// nsIController
+  // nsIController
 
   supportsCommand(aCommand) {
     if (aCommand === "downloadsCmd_clearList") {
       return true;
     }
     // Firstly, determine if this is a command that we can handle.
     if (!DownloadsViewUI.isCommandName(aCommand)) {
       return false;
@@ -1347,43 +1332,40 @@ const DownloadsViewController = {
     if (element) {
       // The doCommand function also checks if the command is enabled.
       DownloadsView.itemForElement(element).doCommand(aCommand);
     }
   },
 
   onEvent() {},
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Other functions
+  // Other functions
 
   updateCommands() {
     function updateCommandsForObject(object) {
       for (let name in object) {
         if (DownloadsViewUI.isCommandName(name)) {
           goUpdateCommand(name);
         }
       }
     }
     updateCommandsForObject(this);
     updateCommandsForObject(DownloadsViewItem.prototype);
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Selection-independent commands
+  // Selection-independent commands
 
   downloadsCmd_clearList() {
     DownloadsCommon.getData(window).removeFinished();
   },
 };
 
 XPCOMUtils.defineConstant(this, "DownloadsViewController", DownloadsViewController);
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsSummary
+// 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 = {
 
   /**
@@ -1557,18 +1539,17 @@ const DownloadsSummary = {
     }
     delete this._detailsNode;
     return this._detailsNode = node;
   }
 };
 
 XPCOMUtils.defineConstant(this, "DownloadsSummary", DownloadsSummary);
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsFooter
+// DownloadsFooter
 
 /**
  * Manages events sent to to the footer vbox, which contains both the
  * DownloadsSummary as well as the "Show All Downloads" button.
  */
 const DownloadsFooter = {
 
   /**
@@ -1617,18 +1598,17 @@ const DownloadsFooter = {
     delete this._footerNode;
     return this._footerNode = node;
   }
 };
 
 XPCOMUtils.defineConstant(this, "DownloadsFooter", DownloadsFooter);
 
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsBlockedSubview
+// DownloadsBlockedSubview
 
 /**
  * Manages the blocked subview that slides in when you click a blocked download.
  */
 const DownloadsBlockedSubview = {
 
   get subview() {
     let subview = document.getElementById("downloadsPanel-blockedSubview");
--- a/browser/components/downloads/content/indicator.js
+++ b/browser/components/downloads/content/indicator.js
@@ -1,13 +1,14 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+/* eslint-env mozilla/browser-window */
 
 /**
  * Handles the indicator that displays the progress of ongoing downloads, which
  * is also used as the anchor for the downloads panel.
  *
  * This module includes the following constructors and global objects:
  *
  * DownloadsButton
@@ -21,18 +22,17 @@
  * Builds and updates the actual downloads status widget, responding to changes
  * in the global status data, or provides a neutral view if the indicator is
  * removed from the toolbars and only used as a temporary anchor.  In addition,
  * handles the user interaction events raised by the widget.
  */
 
 "use strict";
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsButton
+// DownloadsButton
 
 /**
  * Main entry point for the downloads indicator.  Depending on how the toolbars
  * have been customized, this object determines if we should show a fully
  * functional indicator, a placeholder used during customization and in the
  * customization palette, or a neutral view as a temporary anchor for the
  * downloads panel.
  */
@@ -182,18 +182,17 @@ const DownloadsButton = {
 };
 
 Object.defineProperty(this, "DownloadsButton", {
   value: DownloadsButton,
   enumerable: true,
   writable: false
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// DownloadsIndicatorView
+// DownloadsIndicatorView
 
 /**
  * Builds and updates the actual downloads status widget, responding to changes
  * in the global status data, or provides a neutral view if the indicator is
  * removed from the toolbars and only used as a temporary anchor.  In addition,
  * handles the user interaction events raised by the widget.
  */
 const DownloadsIndicatorView = {
@@ -282,18 +281,17 @@ const DownloadsIndicatorView = {
         }
 
         if (aCallback) {
           aCallback();
         }
       });
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Direct control functions
+  // Direct control functions
 
   /**
    * Set while we are waiting for a notification to fade out.
    */
   _notificationTimeout: null,
 
   /**
    * Check if the panel containing aNode is open.
@@ -352,39 +350,38 @@ const DownloadsIndicatorView = {
     }
 
     // The notification element is positioned to show in the same location as
     // the downloads button. It's not in the downloads button itself in order to
     // be able to anchor the notification elsewhere if required, and to ensure
     // the notification isn't clipped by overflow properties of the anchor's
     // container.
     let notifier = this.notifier;
-    if (notifier.style.transform == '') {
+    if (notifier.style.transform == "") {
       let anchorRect = anchor.getBoundingClientRect();
       let notifierRect = notifier.getBoundingClientRect();
       let topDiff = anchorRect.top - notifierRect.top;
       let leftDiff = anchorRect.left - notifierRect.left;
       let heightDiff = anchorRect.height - notifierRect.height;
       let widthDiff = anchorRect.width - notifierRect.width;
       let translateX = (leftDiff + .5 * widthDiff) + "px";
       let translateY = (topDiff + .5 * heightDiff) + "px";
-      notifier.style.transform = "translate(" +  translateX + ", " + translateY + ")";
+      notifier.style.transform = "translate(" + translateX + ", " + translateY + ")";
     }
     notifier.setAttribute("notification", aType);
     anchor.setAttribute("notification", aType);
     this._notificationTimeout = setTimeout(() => {
       anchor.removeAttribute("notification");
       notifier.removeAttribute("notification");
-      notifier.style.transform = '';
+      notifier.style.transform = "";
       // This value is determined by the overall duration of animation in CSS.
     }, 2000);
   },
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// Callback functions from DownloadsIndicatorData
+  // Callback functions from DownloadsIndicatorData
 
   /**
    * Indicates whether the indicator should be shown because there are some
    * downloads to be displayed.
    */
   set hasDownloads(aValue) {
     if (this._hasDownloads != aValue || (!this._operational && aValue)) {
       this._hasDownloads = aValue;
@@ -519,18 +516,17 @@ const DownloadsIndicatorView = {
         let badgeClass = "download-" + this._attention;
         gMenuButtonBadgeManager.addBadge(
                           gMenuButtonBadgeManager.BADGEID_DOWNLOAD, badgeClass);
       }
     }
   },
   _attention: DownloadsCommon.ATTENTION_NONE,
 
-  //////////////////////////////////////////////////////////////////////////////
-  //// User interface event functions
+  // User interface event functions
 
   onWindowUnload() {
     // This function is registered as an event listener, we can't use "this".
     DownloadsIndicatorView.ensureTerminated();
   },
 
   onCommand(aEvent) {
     // If the downloads button is in the menu panel, open the Library
--- a/browser/components/downloads/test/browser/browser_basic_functionality.js
+++ b/browser/components/downloads/test/browser/browser_basic_functionality.js
@@ -25,17 +25,17 @@ add_task(function* test_basic_functional
   yield promiseFocus();
 
   // Ensure that state is reset in case previous tests didn't finish.
   yield task_resetState();
 
   // For testing purposes, show all the download items at once.
   var originalCountLimit = DownloadsView.kItemCountLimit;
   DownloadsView.kItemCountLimit = DownloadData.length;
-  registerCleanupFunction(function () {
+  registerCleanupFunction(function() {
     DownloadsView.kItemCountLimit = originalCountLimit;
   });
 
   // Populate the downloads database with the data required by this test.
   yield task_addDownloads(DownloadData);
 
   // Open the user interface and wait for data to be fully loaded.
   yield task_openPanel();
--- a/browser/components/downloads/test/browser/browser_downloads_panel_block.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_block.js
@@ -103,30 +103,28 @@ function* openPanel() {
     let interval = setInterval(() => {
       if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
         if (verifyCount > 0) {
           verifyCount--;
         } else {
           clearInterval(interval);
           resolve();
         }
-      } else {
-        if (iBackoff < backoff) {
+      } else if (iBackoff < backoff) {
           // Keep backing off before trying again.
           iBackoff++;
-        } else {
+      } else {
           // Try (or retry) opening the panel.
           verifyCount = 5;
           backoff = Math.max(1, 2 * backoff);
           iBackoff = 0;
           if (DownloadsPanel._state != DownloadsPanel.kStateUninitialized) {
-            DownloadsPanel._state = DownloadsPanel.kStateHidden;
+              DownloadsPanel._state = DownloadsPanel.kStateHidden;
           }
           DownloadsPanel.showPanel();
-        }
       }
     }, 100);
   });
 }
 
 function promisePanelHidden() {
   return new Promise(resolve => {
     if (!DownloadsPanel.panel || DownloadsPanel.panel.state == "closed") {
@@ -157,17 +155,16 @@ function promiseSubviewShown(shown) {
   // More terribleness, but I'm tired of fighting intermittent timeouts on try.
   // Just poll for the subview and wait a second before resolving the promise.
   return new Promise(resolve => {
     let interval = setInterval(() => {
       if (shown == DownloadsBlockedSubview.view.showingSubView &&
           !DownloadsBlockedSubview.view._transitioning) {
         clearInterval(interval);
         setTimeout(resolve, 1000);
-        return;
       }
     }, 0);
   });
 }
 
 function promiseUnblockAndOpenDownloadCalled(item) {
   return new Promise(resolve => {
     let realFn = item._shell.unblockAndOpenDownload;
--- a/browser/components/downloads/test/browser/browser_downloads_panel_footer.js
+++ b/browser/components/downloads/test/browser/browser_downloads_panel_footer.js
@@ -36,27 +36,27 @@ add_task(function* test_clearList() {
     downloads: [
       { state: nsIDM.DOWNLOAD_NOTSTARTED },
       { state: nsIDM.DOWNLOAD_FINISHED },
       { state: nsIDM.DOWNLOAD_FAILED },
       { state: nsIDM.DOWNLOAD_CANCELED },
     ],
     expectClearListShown: true,
     expectedItemNumber: 0,
-  },{
+  }, {
     downloads: [
       { state: nsIDM.DOWNLOAD_NOTSTARTED },
       { state: nsIDM.DOWNLOAD_FINISHED },
       { state: nsIDM.DOWNLOAD_FAILED },
       { state: nsIDM.DOWNLOAD_PAUSED },
       { state: nsIDM.DOWNLOAD_CANCELED },
     ],
     expectClearListShown: true,
     expectedItemNumber: 1,
-  },{
+  }, {
     downloads: [
       { state: nsIDM.DOWNLOAD_PAUSED },
     ],
     expectClearListShown: false,
     expectedItemNumber: 1,
   }];
 
   for (let testCase of kTestCases) {
--- a/browser/components/downloads/test/browser/browser_first_download_panel.js
+++ b/browser/components/downloads/test/browser/browser_first_download_panel.js
@@ -39,17 +39,17 @@ add_task(function* test_first_download_p
   DownloadsPanel.hidePanel();
 
   ok(DownloadsCommon.getData(window).panelHasShownBefore,
      "Should have recorded that the panel was opened on a download.")
 
   // Next, make sure that if we start another download, we don't open the
   // panel automatically.
   let originalOnPopupShown = DownloadsPanel.onPopupShown;
-  DownloadsPanel.onPopupShown = function () {
+  DownloadsPanel.onPopupShown = function() {
     originalOnPopupShown.apply(this, arguments);
     ok(false, "Should not have opened the downloads panel.");
   };
 
   DownloadsCommon.getData(window)._notifyDownloadEvent("start");
 
   // Wait 2 seconds to ensure that the panel does not open.
   yield new Promise(resolve => setTimeout(resolve, 2000));
--- a/browser/components/downloads/test/browser/browser_iframe_gone_mid_download.js
+++ b/browser/components/downloads/test/browser/browser_iframe_gone_mid_download.js
@@ -1,11 +1,11 @@
 const SAVE_PER_SITE_PREF = "browser.download.lastDir.savePerSite";
 
-function test_deleted_iframe(perSitePref, windowOptions={}) {
+function test_deleted_iframe(perSitePref, windowOptions = {}) {
   return function*() {
     Services.prefs.setBoolPref(SAVE_PER_SITE_PREF, perSitePref);
     let {DownloadLastDir} = Cu.import("resource://gre/modules/DownloadLastDir.jsm", {});
 
     let win = yield promiseOpenAndLoadWindow(windowOptions);
     let tab = win.gBrowser.addTab();
     yield promiseTabLoadEvent(tab, "about:mozilla");
 
--- a/browser/components/downloads/test/browser/browser_indicatorDrop.js
+++ b/browser/components/downloads/test/browser/browser_indicatorDrop.js
@@ -24,20 +24,20 @@ add_task(function* test_indicatorDrop() 
     let dragData = [[{type: "text/plain", data: urls.join("\n")}]];
 
     let list = yield Downloads.getList(Downloads.ALL);
 
     let added = new Set();
     let succeeded = new Set();
     yield new Promise(function(resolve) {
       let view = {
-        onDownloadAdded: function(download) {
+        onDownloadAdded(download) {
           added.add(download.source.url);
         },
-        onDownloadChanged: function(download) {
+        onDownloadChanged(download) {
           if (!added.has(download.source.url))
             return;
           if (!download.succeeded)
             return;
           succeeded.add(download.source.url);
           if (succeeded.size == urls.length) {
             list.removeView(view).then(resolve);
           }
--- a/browser/components/downloads/test/browser/browser_libraryDrop.js
+++ b/browser/components/downloads/test/browser/browser_libraryDrop.js
@@ -24,20 +24,20 @@ add_task(function* test_indicatorDrop() 
     ok(listBox, "download list box present");
 
     let list = await Downloads.getList(Downloads.ALL);
 
     let added = new Set();
     let succeeded = new Set();
     await new Promise(resolve => {
       let view = {
-        onDownloadAdded: function(download) {
+        onDownloadAdded(download) {
           added.add(download.source.url);
         },
-        onDownloadChanged: function(download) {
+        onDownloadChanged(download) {
           if (!added.has(download.source.url))
             return;
           if (!download.succeeded)
             return;
           succeeded.add(download.source.url);
           if (succeeded.size == urls.length) {
             list.removeView(view).then(resolve);
           }
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -2,53 +2,54 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Provides infrastructure for automated download components tests.
  */
 
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
+// Globals
 
 XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
                                   "resource://gre/modules/Downloads.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
                                   "resource:///modules/DownloadsCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
+    "resource://testing-common/httpd.js");
+
 const nsIDM = Ci.nsIDownloadManager;
 
 var gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
 gTestTargetFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/docs/
+/* global sinon:false */
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-1.16.1.js");
 
-registerCleanupFunction(function () {
+registerCleanupFunction(function() {
   gTestTargetFile.remove(false);
 
   delete window.sinon;
   delete window.setImmediate;
   delete window.clearImmediate;
 });
 
-////////////////////////////////////////////////////////////////////////////////
-//// Asynchronous support subroutines
+// Asynchronous support subroutines
 
-function promiseOpenAndLoadWindow(aOptions)
-{
+function promiseOpenAndLoadWindow(aOptions) {
   return new Promise((resolve, reject) => {
     let win = OpenBrowserWindow(aOptions);
     win.addEventListener("load", function() {
       resolve(win);
     }, {once: true});
   });
 }
 
@@ -61,18 +62,17 @@ function promiseOpenAndLoadWindow(aOptio
  * @param [optional] url
  *        The url to load, or the current url.
  * @param [optional] event
  *        The load event type to wait for.  Defaults to "load".
  * @return {Promise} resolved when the event is handled.
  * @resolves to the received event
  * @rejects if a valid load event is not received within a meaningful interval
  */
-function promiseTabLoadEvent(tab, url, eventType="load")
-{
+function promiseTabLoadEvent(tab, url, eventType = "load") {
   let deferred = Promise.defer();
   info("Wait tab event: " + eventType);
 
   function handle(event) {
     if (event.originalTarget != tab.linkedBrowser.contentDocument ||
         event.target.location.href == "about:blank" ||
         (url && event.target.location.href != url)) {
       info("Skipping spurious '" + eventType + "'' event" +
@@ -91,77 +91,72 @@ function promiseTabLoadEvent(tab, url, e
   registerCleanupFunction(() => realCleanup());
 
   tab.linkedBrowser.addEventListener(eventType, handle, true, true);
   if (url)
     tab.linkedBrowser.loadURI(url);
   return deferred.promise;
 }
 
-function promiseWindowClosed(win)
-{
+function promiseWindowClosed(win) {
   let promise = new Promise((resolve, reject) => {
     Services.obs.addObserver(function obs(subject, topic) {
       if (subject == win) {
         Services.obs.removeObserver(obs, topic);
         resolve();
       }
     }, "domwindowclosed", false);
   });
   win.close();
   return promise;
 }
 
 
-function promiseFocus()
-{
+function promiseFocus() {
   let deferred = Promise.defer();
   waitForFocus(deferred.resolve);
   return deferred.promise;
 }
 
-function promisePanelOpened()
-{
+function promisePanelOpened() {
   let deferred = Promise.defer();
 
   if (DownloadsPanel.panel && DownloadsPanel.panel.state == "open") {
     return deferred.resolve();
   }
 
   // Hook to wait until the panel is shown.
   let originalOnPopupShown = DownloadsPanel.onPopupShown;
-  DownloadsPanel.onPopupShown = function () {
+  DownloadsPanel.onPopupShown = function() {
     DownloadsPanel.onPopupShown = originalOnPopupShown;
     originalOnPopupShown.apply(this, arguments);
 
     // Defer to the next tick of the event loop so that we don't continue
     // processing during the DOM event handler itself.
     setTimeout(deferred.resolve, 0);
   };
 
   return deferred.promise;
 }
 
-function* task_resetState()
-{
+function* task_resetState() {
   // Remove all downloads.
   let publicList = yield Downloads.getList(Downloads.PUBLIC);
   let downloads = yield publicList.getAll();
   for (let download of downloads) {
     publicList.remove(download);
     yield download.finalize(true);
   }
 
   DownloadsPanel.hidePanel();
 
   yield promiseFocus();
 }
 
-function* task_addDownloads(aItems)
-{
+function* task_addDownloads(aItems) {
   let startTimeMs = Date.now();
 
   let publicList = yield Downloads.getList(Downloads.PUBLIC);
   for (let item of aItems) {
     let download = {
       source: {
         url: "http://www.example.com/test-download.txt",
       },
@@ -179,31 +174,30 @@ function* task_addDownloads(aItems)
     // `"errorObj" in download` must be false when there's no error.
     if (item.errorObj) {
       download.errorObj = item.errorObj;
     }
     yield publicList.add(yield Downloads.createDownload(download));
   }
 }
 
-function* task_openPanel()
-{
+function* task_openPanel() {
   yield promiseFocus();
 
   let promise = promisePanelOpened();
   DownloadsPanel.showPanel();
   yield promise;
 }
 
 function* setDownloadDir() {
   let tmpDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
   tmpDir.append("testsavedir");
   if (!tmpDir.exists()) {
     tmpDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
-    registerCleanupFunction(function () {
+    registerCleanupFunction(function() {
       try {
         tmpDir.remove(true);
       } catch (e) {
         // On Windows debug build this may fail.
       }
     });
   }
 
--- a/browser/components/downloads/test/unit/head.js
+++ b/browser/components/downloads/test/unit/head.js
@@ -2,17 +2,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Provides infrastructure for automated download components tests.
  */
 
-////////////////////////////////////////////////////////////////////////////////
-//// Globals
+// Globals
 
 var Cc = Components.classes;
 var Ci = Components.interfaces;
 var Cu = Components.utils;
 var Cr = Components.results;
 
 Cu.import("resource:///modules/DownloadsCommon.jsm");
--- a/browser/components/downloads/test/unit/test_DownloadsCommon.js
+++ b/browser/components/downloads/test/unit/test_DownloadsCommon.js
@@ -2,36 +2,34 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests for the functions located directly in the "DownloadsCommon" object.
  */
 
-function testFormatTimeLeft(aSeconds, aExpectedValue, aExpectedUnitString)
-{
+function testFormatTimeLeft(aSeconds, aExpectedValue, aExpectedUnitString) {
   let expected = "";
   if (aExpectedValue) {
     // Format the expected result based on the current language.
     expected = DownloadsCommon.strings[aExpectedUnitString](aExpectedValue);
   }
   do_check_eq(DownloadsCommon.formatTimeLeft(aSeconds), expected);
 }
 
-function run_test()
-{
-  testFormatTimeLeft(      0,   "", "");
-  testFormatTimeLeft(      1,  "1", "shortTimeLeftSeconds");
-  testFormatTimeLeft(     29, "29", "shortTimeLeftSeconds");
-  testFormatTimeLeft(     30, "30", "shortTimeLeftSeconds");
-  testFormatTimeLeft(     31,  "1", "shortTimeLeftMinutes");
-  testFormatTimeLeft(     60,  "1", "shortTimeLeftMinutes");
-  testFormatTimeLeft(     89,  "1", "shortTimeLeftMinutes");
-  testFormatTimeLeft(     90,  "2", "shortTimeLeftMinutes");
-  testFormatTimeLeft(     91,  "2", "shortTimeLeftMinutes");
-  testFormatTimeLeft(   3600,  "1", "shortTimeLeftHours");
-  testFormatTimeLeft(  86400, "24", "shortTimeLeftHours");
-  testFormatTimeLeft( 169200, "47", "shortTimeLeftHours");
-  testFormatTimeLeft( 172800,  "2", "shortTimeLeftDays");
+function run_test() {
+  testFormatTimeLeft(0, "", "");
+  testFormatTimeLeft(1, "1", "shortTimeLeftSeconds");
+  testFormatTimeLeft(29, "29", "shortTimeLeftSeconds");
+  testFormatTimeLeft(30, "30", "shortTimeLeftSeconds");
+  testFormatTimeLeft(31, "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(60, "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(89, "1", "shortTimeLeftMinutes");
+  testFormatTimeLeft(90, "2", "shortTimeLeftMinutes");
+  testFormatTimeLeft(91, "2", "shortTimeLeftMinutes");
+  testFormatTimeLeft(3600, "1", "shortTimeLeftHours");
+  testFormatTimeLeft(86400, "24", "shortTimeLeftHours");
+  testFormatTimeLeft(169200, "47", "shortTimeLeftHours");
+  testFormatTimeLeft(172800, "2", "shortTimeLeftDays");
   testFormatTimeLeft(8553600, "99", "shortTimeLeftDays");
   testFormatTimeLeft(8640000, "99", "shortTimeLeftDays");
 }
--- a/browser/modules/ContentClick.jsm
+++ b/browser/modules/ContentClick.jsm
@@ -80,16 +80,17 @@ var ContentClick = {
     let params = {
       charset: browser.characterSet,
       referrerURI: browser.documentURI,
       referrerPolicy: json.referrerPolicy,
       noReferrer: json.noReferrer,
       allowMixedContent: json.allowMixedContent,
       isContentWindowPrivate: json.isContentWindowPrivate,
       originPrincipal: json.originPrincipal,
+      triggeringPrincipal: json.triggeringPrincipal,
       frameOuterWindowID: json.frameOuterWindowID,
     };
 
     // The new tab/window must use the same userContextId.
     if (json.originAttributes.userContextId) {
       params.userContextId = json.originAttributes.userContextId;
     }
 
--- a/devtools/client/inspector/shared/tooltips-overlay.js
+++ b/devtools/client/inspector/shared/tooltips-overlay.js
@@ -275,17 +275,18 @@ TooltipsOverlay.prototype = {
     let fillStyle = getColor("body-color");
     let {data, size: maxDim} = yield nodeFront.getFontFamilyDataURL(font, fillStyle);
 
     let imageUrl = yield data.string();
     let doc = this.view.inspector.panelDoc;
     let {naturalWidth, naturalHeight} = yield getImageDimensions(doc, imageUrl);
 
     yield setImageTooltip(this.previewTooltip, doc, imageUrl,
-      {hideDimensionLabel: true, maxDim, naturalWidth, naturalHeight});
+      {hideDimensionLabel: true, hideCheckeredBackground: true,
+       maxDim, naturalWidth, naturalHeight});
   }),
 
   _onNewSelection: function () {
     if (this.previewTooltip) {
       this.previewTooltip.hide();
     }
 
     if (this.colorPicker) {
--- a/devtools/client/netmonitor/src/components/properties-view.js
+++ b/devtools/client/netmonitor/src/components/properties-view.js
@@ -199,16 +199,17 @@ const PropertiesView = createClass({
               id: "value",
               width: "100%",
             }],
             decorator: decorator || {
               getRowClass: (rowObject) => this.getRowClass(rowObject, sectionNames),
             },
             enableInput,
             expandableStrings,
+            useQuotes: false,
             expandedNodes: this.getExpandedNodes(object),
             onFilter: (props) => this.onFilter(props, sectionNames),
             renderRow: renderRow || this.renderRowWithEditor,
             renderValue: renderValue || this.renderValueWithRep,
           }),
         ),
       )
     );
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -25,51 +25,51 @@ add_task(function* () {
   yield wait;
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#params-tab"));
   yield wait;
-  testParamsTab1("a", '""', '{ "foo": "bar" }', '""');
+  testParamsTab1("a", "", '{ "foo": "bar" }', "");
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[1]);
   yield wait;
-  testParamsTab1("a", '"b"', '{ "foo": "bar" }', '""');
+  testParamsTab1("a", "b", '{ "foo": "bar" }', "");
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[2]);
   yield wait;
-  testParamsTab1("a", '"b"', "?foo", '"bar"');
+  testParamsTab1("a", "b", "?foo", "bar");
 
   wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[3]);
   yield wait;
-  testParamsTab2("a", '""', '{ "foo": "bar" }', "js");
+  testParamsTab2("a", "", '{ "foo": "bar" }', "js");
 
   wait = waitForDOM(document, "#params-panel tr:not(.tree-section).treeRow", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[4]);
   yield wait;
-  testParamsTab2("a", '"b"', '{ "foo": "bar" }', "js");
+  testParamsTab2("a", "b", '{ "foo": "bar" }', "js");
 
   // Wait for all tree sections and editor updated by react
   let waitSections = waitForDOM(document, "#params-panel .tree-section", 2);
   let waitEditor = waitForDOM(document, "#params-panel .editor-mount iframe");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[5]);
   let [, editorFrames] = yield Promise.all([waitSections, waitEditor]);
   yield once(editorFrames[0], "DOMContentLoaded");
   yield waitForDOM(editorFrames[0].contentDocument, ".CodeMirror-code");
-  testParamsTab2("a", '"b"', "?foo=bar", "text");
+  testParamsTab2("a", "b", "?foo=bar", "text");
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[6]);
   testParamsTab3();
 
   yield teardown(monitor);
 
   function testParamsTab1(queryStringParamName, queryStringParamValue,
--- a/devtools/client/netmonitor/test/browser_net_content-type.js
+++ b/devtools/client/netmonitor/test/browser_net_content-type.js
@@ -223,17 +223,17 @@ add_task(function* () {
         let labels = tabpanel
           .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
         let values = tabpanel
           .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
         is(labels[0].textContent, "greeting",
           "The first json property name was incorrect.");
         is(values[0].textContent,
-          "\"Hello JSON!\"", "The first json property value was incorrect.");
+          "Hello JSON!", "The first json property value was incorrect.");
         break;
       }
       case "html": {
         checkVisibility("textarea");
 
         let editor = document.querySelector(".editor-mount iframe");
         let text = editor.contentDocument.querySelector(".CodeMirror-line").textContent;
 
--- a/devtools/client/netmonitor/test/browser_net_json-b64.js
+++ b/devtools/client/netmonitor/test/browser_net_json-b64.js
@@ -51,13 +51,13 @@ add_task(function* () {
 
   let labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   let values = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
   is(labels[0].textContent, "greeting",
     "The first json property name was incorrect.");
-  is(values[0].textContent, "\"This is a base 64 string.\"",
+  is(values[0].textContent, "This is a base 64 string.",
     "The first json property value was incorrect.");
 
   yield teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_custom_mime.js
@@ -75,12 +75,12 @@ add_task(function* () {
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
     is(labels[0].textContent, "greeting",
       "The first json property name was incorrect.");
-    is(values[0].textContent, "\"Hello oddly-named JSON!\"",
+    is(values[0].textContent, "Hello oddly-named JSON!",
       "The first json property value was incorrect.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_json_text_mime.js
+++ b/devtools/client/netmonitor/test/browser_net_json_text_mime.js
@@ -76,12 +76,12 @@ add_task(function* () {
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
     is(labels[0].textContent, "greeting",
       "The first json property name was incorrect.");
-    is(values[0].textContent, "\"Hello third-party JSON!\"",
+    is(values[0].textContent, "Hello third-party JSON!",
       "The first json property value was incorrect.");
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_jsonp.js
+++ b/devtools/client/netmonitor/test/browser_net_jsonp.js
@@ -59,24 +59,24 @@ add_task(function* () {
 
   wait = waitForDOM(document, "#response-panel");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector(".network-details-panel-toggle"));
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#response-tab"));
   yield wait;
 
-  testResponseTab("$_0123Fun", "\"Hello JSONP!\"");
+  testResponseTab("$_0123Fun", "Hello JSONP!");
 
   wait = waitForDOM(document, "#response-panel .tree-section");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[1]);
   yield wait;
 
-  testResponseTab("$_4567Sad", "\"Hello weird JSONP!\"");
+  testResponseTab("$_4567Sad", "Hello weird JSONP!");
 
   yield teardown(monitor);
 
   function testResponseTab(func, greeting) {
     let tabpanel = document.querySelector("#response-panel");
 
     is(tabpanel.querySelector(".response-error-header") === null, true,
       "The response error header doesn't have the intended visibility.");
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -110,30 +110,29 @@ add_task(function* () {
       "The post section doesn't have the correct title.");
 
     let labels = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
     let values = tabpanel
       .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
     is(labels[0].textContent, "foo", "The first query param name was incorrect.");
-    is(values[0].textContent, "\"bar\"", "The first query param value was incorrect.");
+    is(values[0].textContent, "bar", "The first query param value was incorrect.");
     is(labels[1].textContent, "baz", "The second query param name was incorrect.");
-    is(values[1].textContent, "\"42\"", "The second query param value was incorrect.");
+    is(values[1].textContent, "42", "The second query param value was incorrect.");
     is(labels[2].textContent, "type", "The third query param name was incorrect.");
-    is(values[2].textContent, "\"" + type + "\"",
-      "The third query param value was incorrect.");
+    is(values[2].textContent, type, "The third query param value was incorrect.");
 
     if (type == "urlencoded") {
       checkVisibility("params");
       is(labels.length, 5, "There should be 5 param values displayed in this tabpanel.");
       is(labels[3].textContent, "foo", "The first post param name was incorrect.");
-      is(values[3].textContent, "\"bar\"", "The first post param value was incorrect.");
+      is(values[3].textContent, "bar", "The first post param value was incorrect.");
       is(labels[4].textContent, "baz", "The second post param name was incorrect.");
-      is(values[4].textContent, "\"123\"", "The second post param value was incorrect.");
+      is(values[4].textContent, "123", "The second post param value was incorrect.");
     } else {
       checkVisibility("params editor");
 
       is(labels.length, 3, "There should be 3 param values displayed in this tabpanel.");
 
       let text = editorFrames[0].contentDocument.querySelector(".CodeMirror-code")
                                                 .textContent;
 
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -50,14 +50,14 @@ add_task(function* () {
     "The post section doesn't have the correct title.");
 
   let labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   let values = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
   is(labels[0].textContent, "foo", "The first query param name was incorrect.");
-  is(values[0].textContent, "\"bar\"", "The first query param value was incorrect.");
+  is(values[0].textContent, "bar", "The first query param value was incorrect.");
   is(labels[1].textContent, "baz", "The second query param name was incorrect.");
-  is(values[1].textContent, "\"123\"", "The second query param value was incorrect.");
+  is(values[1].textContent, "123", "The second query param value was incorrect.");
 
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -45,21 +45,21 @@ add_task(function* () {
 
   let labels = tabpanel
     .querySelectorAll(".properties-view tr:not(.tree-section) .treeLabelCell .treeLabel");
   let values = tabpanel
     .querySelectorAll(".properties-view tr:not(.tree-section) .treeValueCell .objectBox");
 
   is(labels[labels.length - 2].textContent, "content-type",
     "The first request header name was incorrect.");
-  is(values[values.length - 2].textContent, "\"application/x-www-form-urlencoded\"",
+  is(values[values.length - 2].textContent, "application/x-www-form-urlencoded",
     "The first request header value was incorrect.");
   is(labels[labels.length - 1].textContent, "custom-header",
     "The second request header name was incorrect.");
-  is(values[values.length - 1].textContent, "\"hello world!\"",
+  is(values[values.length - 1].textContent, "hello world!",
     "The second request header value was incorrect.");
 
   // Wait for all tree sections updated by react
   wait = waitForDOM(document, "#params-panel .tree-section");
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelector("#params-tab"));
   yield wait;
 
@@ -75,14 +75,14 @@ add_task(function* () {
     "The form data section doesn't have the correct title.");
 
   labels = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeLabelCell .treeLabel");
   values = tabpanel
     .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
   is(labels[0].textContent, "foo", "The first payload param name was incorrect.");
-  is(values[0].textContent, "\"bar\"", "The first payload param value was incorrect.");
+  is(values[0].textContent, "bar", "The first payload param value was incorrect.");
   is(labels[1].textContent, "baz", "The second payload param name was incorrect.");
-  is(values[1].textContent, "\"123\"", "The second payload param value was incorrect.");
+  is(values[1].textContent, "123", "The second payload param value was incorrect.");
 
   return teardown(monitor);
 });
--- a/devtools/client/netmonitor/test/browser_net_status-codes.js
+++ b/devtools/client/netmonitor/test/browser_net_status-codes.js
@@ -177,17 +177,16 @@ add_task(function* () {
   function* testParams(data, index) {
     EventUtils.sendMouseEvent({ type: "mousedown" },
       document.querySelectorAll(".request-list-item")[index]);
     EventUtils.sendMouseEvent({ type: "click" },
       document.querySelector("#params-tab"));
 
     let panel = document.querySelector("#params-panel");
     let statusParamValue = data.uri.split("=").pop();
-    let statusParamShownValue = "\"" + statusParamValue + "\"";
     let treeSections = panel.querySelectorAll(".tree-section");
 
     is(treeSections.length, 1,
       "There should be 1 param section displayed in this panel.");
     is(panel.querySelectorAll("tr:not(.tree-section).treeRow").length, 1,
       "There should be 1 param row displayed in this panel.");
     is(panel.querySelectorAll(".empty-notice").length, 0,
       "The empty notice should not be displayed in this panel.");
@@ -197,17 +196,17 @@ add_task(function* () {
     let values = panel
       .querySelectorAll("tr:not(.tree-section) .treeValueCell .objectBox");
 
     is(treeSections[0].querySelector(".treeLabel").textContent,
       L10N.getStr("paramsQueryString"),
       "The params scope doesn't have the correct title.");
 
     is(labels[0].textContent, "sts", "The param name was incorrect.");
-    is(values[0].textContent, statusParamShownValue, "The param value was incorrect.");
+    is(values[0].textContent, statusParamValue, "The param value was incorrect.");
 
     ok(panel.querySelector(".treeTable"),
       "The request params tree view should be displayed.");
     is(panel.querySelector(".editor-mount") === null,
       true,
       "The request post data editor should be hidden.");
   }
 });
--- a/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js
+++ b/devtools/client/shared/widgets/tooltip/ImageTooltipHelper.js
@@ -51,46 +51,54 @@ function getImageDimensions(doc, imageUr
  *        A document element to create the HTML elements needed for the tooltip
  * @param {String} imageUrl
  *        Absolute URL of the image to display in the tooltip
  * @param {Object} options
  *        - {Number} naturalWidth mandatory, width of the image to display
  *        - {Number} naturalHeight mandatory, height of the image to display
  *        - {Number} maxDim optional, max width/height of the preview
  *        - {Boolean} hideDimensionLabel optional, pass true to hide the label
+ *        - {Boolean} hideCheckeredBackground optional, pass true to hide
+                      the checkered background
  */
 function setImageTooltip(tooltip, doc, imageUrl, options) {
-  let {naturalWidth, naturalHeight, hideDimensionLabel, maxDim} = options;
+  let {naturalWidth, naturalHeight, hideDimensionLabel,
+       hideCheckeredBackground, maxDim} = options;
   maxDim = maxDim || MAX_DIMENSION;
 
   let imgHeight = naturalHeight;
   let imgWidth = naturalWidth;
   if (imgHeight > maxDim || imgWidth > maxDim) {
     let scale = maxDim / Math.max(imgHeight, imgWidth);
     // Only allow integer values to avoid rounding errors.
     imgHeight = Math.floor(scale * naturalHeight);
     imgWidth = Math.ceil(scale * naturalWidth);
   }
 
+  let imageClass = "";
+  if (!hideCheckeredBackground) {
+    imageClass = "devtools-tooltip-tiles";
+  }
+
   // Create tooltip content
   let div = doc.createElementNS(XHTML_NS, "div");
   div.style.cssText = `
     height: 100%;
     min-width: 100px;
     display: flex;
     flex-direction: column;
     text-align: center;`;
   let html = `
     <div style="flex: 1;
                 display: flex;
                 padding: ${IMAGE_PADDING}px;
                 align-items: center;
                 justify-content: center;
                 min-height: 1px;">
-      <img class="devtools-tooltip-tiles"
+      <img class="${imageClass}"
            style="height: ${imgHeight}px; max-height: 100%;"
            src="${imageUrl}"/>
     </div>`;
 
   if (!hideDimensionLabel) {
     let label = naturalWidth + " \u00D7 " + naturalHeight;
     html += `
       <div style="height: ${LABEL_HEIGHT}px;
--- a/dom/base/test/chrome/window_nsITextInputProcessor.xul
+++ b/dom/base/test/chrome/window_nsITextInputProcessor.xul
@@ -2292,20 +2292,18 @@ function runKeyTests()
        desc + " should cause keydown event");
     if (aEvent.type != aExpectedData.type) {
       return;
     }
     is(aEvent.defaultPrevented, expectedValue("defaultPrevented"),
        desc + ".defaultPrevented is wrong");
     is(aEvent.key, expectedValue("key"),
        desc + ".key is wrong");
-    if (SpecialPowers.getBoolPref("dom.keyboardevent.code.enabled")) {
-      is(aEvent.code, expectedValue("code"),
-         desc + ".code is wrong");
-    }
+    is(aEvent.code, expectedValue("code"),
+       desc + ".code is wrong");
     is(aEvent.location, expectedValue("location"),
        desc + ".location is wrong");
     is(aEvent.repeat, expectedValue("repeat"),
        desc + ".repeat is wrong");
     is(aEvent.isComposing, expectedValue("isComposing"),
        desc + ".isComposing is wrong");
     is(aEvent.keyCode, expectedValue("keyCode"),
        desc + ".keyCode is wrong");
@@ -2870,20 +2868,18 @@ function runKeyTests()
     var desc = description + aTestDesc + ", type=\"" + aEvent.type + "\", key=\"" + aEvent.key + "\", code=\"" + aEvent.code + "\"";
     is(aEvent.type, aType,
        desc + ", .type value is wrong");
     if (aEvent.type != aType) {
       return;
     }
     is(aEvent.key, aKey,
        desc + ", .key value is wrong");
-    if (SpecialPowers.getBoolPref("dom.keyboardevent.code.enabled")) {
-      is(aEvent.code, aCode,
-         desc + ", .code value is wrong");
-    }
+    is(aEvent.code, aCode,
+       desc + ", .code value is wrong");
     is(aEvent.altKey, aModifiers.indexOf("Alt") >= 0,
        desc + ", .altKey value is wrong");
     is(aEvent.ctrlKey, aModifiers.indexOf("Control") >= 0,
        desc + ", .ctrlKey value is wrong");
     is(aEvent.metaKey, aModifiers.indexOf("Meta") >= 0,
        desc + ", .metaKey value is wrong");
     is(aEvent.shiftKey, aModifiers.indexOf("Shift") >= 0,
        desc + ", .shiftKey value is wrong");
--- a/dom/events/test/test_dom_keyboard_event.html
+++ b/dom/events/test/test_dom_keyboard_event.html
@@ -9,18 +9,16 @@
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-var gCodeEnabled = SpecialPowers.getBoolPref("dom.keyboardevent.code.enabled");
-
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(runTests, window);
 
 function testInitializingUntrustedEvent()
 {
   const kTests = [
     { createEventArg: "KeyboardEvent",
       type: "keydown", bubbles: true, cancelable: true, view: null,
@@ -121,19 +119,17 @@ function testInitializingUntrustedEvent(
       } else {
         is(e[attr], kTest[attr], description + attr + " returns wrong value");
       }
     }
     is(e.isTrusted, false, description + "isTrusted returns wrong value");
 
     // key and code values cannot be initialized with initKeyEvent().
     is(e.key, "", description + "key must return empty string");
-    if (gCodeEnabled) {
-      is(e.code, "", description + "code must be empty string");
-    }
+    is(e.code, "", description + "code must be empty string");
 
     // getModifierState() tests
     is(e.getModifierState("Shift"), kTest.shiftKey,
        description + "getModifierState(\"Shift\") returns wrong value");
     is(e.getModifierState("Control"), kTest.ctrlKey,
        description + "getModifierState(\"Control\") returns wrong value");
     is(e.getModifierState("Alt"), kTest.altKey,
        description + "getModifierState(\"Alt\") returns wrong value");
--- a/dom/events/test/test_eventctors.html
+++ b/dom/events/test/test_eventctors.html
@@ -428,37 +428,30 @@ var testKeyboardProps =
   { keyCode: 3 },
   { which: 4 },
   { charCode: 5, which: 6 },
   { keyCode: 7, which: 8 },
   { keyCode: 9, charCode: 10 },
   { keyCode: 11, charCode: 12, which: 13 },
 ];
 
-var codeEnabled = SpecialPowers.getBoolPref("dom.keyboardevent.code.enabled");
 var defaultKeyboardEventValues = {};
 for (var i = 0; i < keyboardEventProps.length; ++i) {
   for (prop in keyboardEventProps[i]) {
-    if (!codeEnabled && prop == "code") {
-      continue;
-    }
     if (!isMethodResultInitializer(prop)) {
       ok(prop in e, "keyboardEvent: KeyboardEvent doesn't have property " + prop + "!");
     }
     defaultKeyboardEventValues[prop] = keyboardEventProps[i][prop]; 
   }
 }
 
 while (testKeyboardProps.length) {
   var p = testKeyboardProps.shift();
   e = new KeyboardEvent("foo", p);
   for (var def in defaultKeyboardEventValues) {
-    if (!codeEnabled && def == "code") {
-      continue;
-    }
     if (!(def in p)) {
       is(getPropValue(e, def), defaultKeyboardEventValues[def],
          "KeyboardEvent: Wrong default value for " + def + "!");
     } else {
       is(getPropValue(e, def), p[def],
          "KeyboardEvent: Wrong event init value for " + def + "!");
     }
   }
--- a/dom/url/tests/urlSearchParams_commons.js
+++ b/dom/url/tests/urlSearchParams_commons.js
@@ -1,303 +1,309 @@
-  function testSimpleURLSearchParams() {
-    var u = new URLSearchParams();
-    ok(u, "URLSearchParams created");
-    is(u.has('foo'), false, 'URLSearchParams.has(foo)');
-    is(u.get('foo'), null, 'URLSearchParams.get(foo)');
-    is(u.getAll('foo').length, 0, 'URLSearchParams.getAll(foo)');
+function testSimpleURLSearchParams() {
+  var u = new URLSearchParams();
+  ok(u, "URLSearchParams created");
+  is(u.has('foo'), false, 'URLSearchParams.has(foo)');
+  is(u.get('foo'), null, 'URLSearchParams.get(foo)');
+  is(u.getAll('foo').length, 0, 'URLSearchParams.getAll(foo)');
+
+  u.append('foo', 'bar');
+  is(u.has('foo'), true, 'URLSearchParams.has(foo)');
+  is(u.get('foo'), 'bar', 'URLSearchParams.get(foo)');
+  is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+
+  u.set('foo', 'bar2');
+  is(u.get('foo'), 'bar2', 'URLSearchParams.get(foo)');
+  is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+
+  is(u + "", "foo=bar2", "stringifier");
+
+  u.delete('foo');
+
+  runTest();
+}
 
-    u.append('foo', 'bar');
-    is(u.has('foo'), true, 'URLSearchParams.has(foo)');
-    is(u.get('foo'), 'bar', 'URLSearchParams.get(foo)');
-    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+function testCopyURLSearchParams() {
+  var u = new URLSearchParams();
+  ok(u, "URLSearchParams created");
+  u.append('foo', 'bar');
+
+  var uu = new URLSearchParams(u);
+  is(uu.get('foo'), 'bar', 'uu.get()');
+
+  u.append('foo', 'bar2');
+  is(u.getAll('foo').length, 2, "u.getAll()");
+  is(uu.getAll('foo').length, 1, "uu.getAll()");
 
-    u.set('foo', 'bar2');
-    is(u.get('foo'), 'bar2', 'URLSearchParams.get(foo)');
-    is(u.getAll('foo').length, 1, 'URLSearchParams.getAll(foo)');
+  runTest();
+}
 
-    is(u + "", "foo=bar2", "stringifier");
+function testURL() {
+  var url = new URL('http://www.example.net?a=b&c=d');
+  ok(url.searchParams, "URL searchParams exists!");
+  ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
+  is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+  ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
+  is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
+
+  url.searchParams.set('e', 'f');
+  ok(url.href.indexOf('e=f') != 1, 'URL right');
 
-    u.delete('foo');
-
-    runTest();
-  }
+  runTest();
+}
+function testParserURLSearchParams() {
+  var checks = [
+    { input: '', data: {} },
+    { input: 'a', data: { 'a' : [''] } },
+    { input: 'a=b', data: { 'a' : ['b'] } },
+    { input: 'a=', data: { 'a' : [''] } },
+    { input: '=b', data: { '' : ['b'] } },
+    { input: '&', data: {} },
+    { input: '&a', data: { 'a' : [''] } },
+    { input: 'a&', data: { 'a' : [''] } },
+    { input: 'a&a', data: { 'a' : ['', ''] } },
+    { input: 'a&b&c', data: { 'a' : [''], 'b' : [''], 'c' : [''] } },
+    { input: 'a=b&c=d', data: { 'a' : ['b'], 'c' : ['d'] } },
+    { input: 'a=b&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+    { input: '&&&a=b&&&&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
+    { input: 'a=a&a=b&a=c', data: { 'a' : ['a', 'b', 'c'] } },
+    { input: 'a==a', data: { 'a' : ['=a'] } },
+    { input: 'a=a+b+c+d', data: { 'a' : ['a b c d'] } },
+    { input: '%=a', data: { '%' : ['a'] } },
+    { input: '%a=a', data: { '%a' : ['a'] } },
+    { input: '%a_=a', data: { '%a_' : ['a'] } },
+    { input: '%61=a', data: { 'a' : ['a'] } },
+    { input: '%=a', data: { '%' : ['a'] } },
+    { input: '%a=a', data: { '%a' : ['a'] } },
+    { input: '%a_=a', data: { '%a_' : ['a'] } },
+    { input: '%61=a', data: { 'a' : ['a'] } },
+    { input: '%61+%4d%4D=', data: { 'a MM' : [''] } },
+    { input: '?a=1', data: { 'a' : ['1'] } },
+    { input: '?', data: {} },
+    { input: '?=b', data: { '' : ['b'] } },
+  ];
 
-  function testCopyURLSearchParams() {
-    var u = new URLSearchParams();
-    ok(u, "URLSearchParams created");
-    u.append('foo', 'bar');
+  for (var i = 0; i < checks.length; ++i) {
+    var u = new URLSearchParams(checks[i].input);
+
+    var count = 0;
+    for (var key in checks[i].data) {
+      ++count;
+      ok(u.has(key), "key " + key + " found");
 
-    var uu = new URLSearchParams(u);
-    is(uu.get('foo'), 'bar', 'uu.get()');
+      var all = u.getAll(key);
+      is(all.length, checks[i].data[key].length, "same number of elements");
 
-    u.append('foo', 'bar2');
-    is(u.getAll('foo').length, 2, "u.getAll()");
-    is(uu.getAll('foo').length, 1, "uu.getAll()");
-
-    runTest();
+      for (var k = 0; k < all.length; ++k) {
+        is(all[k], checks[i].data[key][k], "value matches");
+      }
+    }
   }
 
-  function testURL() {
-    var url = new URL('http://www.example.net?a=b&c=d');
-    ok(url.searchParams, "URL searchParams exists!");
-    ok(url.searchParams.has('a'), "URL.searchParams.has('a')");
-    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
-    ok(url.searchParams.has('c'), "URL.searchParams.has('c')");
-    is(url.searchParams.get('c'), 'd', "URL.searchParams.get('c')");
-
-    url.searchParams.set('e', 'f');
-    ok(url.href.indexOf('e=f') != 1, 'URL right');
-
-    runTest();
-  }
-  function testParserURLSearchParams() {
-    var checks = [
-      { input: '', data: {} },
-      { input: 'a', data: { 'a' : [''] } },
-      { input: 'a=b', data: { 'a' : ['b'] } },
-      { input: 'a=', data: { 'a' : [''] } },
-      { input: '=b', data: { '' : ['b'] } },
-      { input: '&', data: {} },
-      { input: '&a', data: { 'a' : [''] } },
-      { input: 'a&', data: { 'a' : [''] } },
-      { input: 'a&a', data: { 'a' : ['', ''] } },
-      { input: 'a&b&c', data: { 'a' : [''], 'b' : [''], 'c' : [''] } },
-      { input: 'a=b&c=d', data: { 'a' : ['b'], 'c' : ['d'] } },
-      { input: 'a=b&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
-      { input: '&&&a=b&&&&c=d&', data: { 'a' : ['b'], 'c' : ['d'] } },
-      { input: 'a=a&a=b&a=c', data: { 'a' : ['a', 'b', 'c'] } },
-      { input: 'a==a', data: { 'a' : ['=a'] } },
-      { input: 'a=a+b+c+d', data: { 'a' : ['a b c d'] } },
-      { input: '%=a', data: { '%' : ['a'] } },
-      { input: '%a=a', data: { '%a' : ['a'] } },
-      { input: '%a_=a', data: { '%a_' : ['a'] } },
-      { input: '%61=a', data: { 'a' : ['a'] } },
-      { input: '%=a', data: { '%' : ['a'] } },
-      { input: '%a=a', data: { '%a' : ['a'] } },
-      { input: '%a_=a', data: { '%a_' : ['a'] } },
-      { input: '%61=a', data: { 'a' : ['a'] } },
-      { input: '%61+%4d%4D=', data: { 'a MM' : [''] } },
-      { input: '?a=1', data: { 'a' : ['1'] } },
-      { input: '?', data: {} },
-      { input: '?=b', data: { '' : ['b'] } },
-    ];
-
-    for (var i = 0; i < checks.length; ++i) {
-      var u = new URLSearchParams(checks[i].input);
-
-      var count = 0;
-      for (var key in checks[i].data) {
-        ++count;
-        ok(u.has(key), "key " + key + " found");
+  runTest();
+}
 
-        var all = u.getAll(key);
-        is(all.length, checks[i].data[key].length, "same number of elements");
-
-        for (var k = 0; k < all.length; ++k) {
-          is(all[k], checks[i].data[key][k], "value matches");
-        }
-      }
-    }
-
-    runTest();
-  }
-
-  function testEncoding() {
-    var encoding = [ [ '1', '1' ],
-                     [ 'a b', 'a+b' ],
-                     [ '<>', '%3C%3E' ],
-                     [ '\u0541', '%D5%81'] ];
-
-    for (var i = 0; i < encoding.length; ++i) {
-      var url = new URL('http://www.example.net');
-      url.searchParams.set('a', encoding[i][0]);
-      is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
-
-      var url2 = new URL(url.href);
-      is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
-    }
+function testEncoding() {
+  var encoding = [ [ '1', '1' ],
+                   [ 'a b', 'a+b' ],
+                   [ '<>', '%3C%3E' ],
+                   [ '\u0541', '%D5%81'] ];
 
-    runTest();
-  }
-
-  function testCopyConstructor() {
-    var url = new URL("http://example.com/");
-    var p = url.searchParams;
-    var q = new URLSearchParams(p);
-    q.set("a", "b");
-    is(url.href, "http://example.com/",
-       "Messing with copy of URLSearchParams should not affect URL");
-    p.set("c", "d");
-    is(url.href, "http://example.com/?c=d",
-       "Messing with URLSearchParams should affect URL");
+  for (var i = 0; i < encoding.length; ++i) {
+    var url = new URL('http://www.example.net');
+    url.searchParams.set('a', encoding[i][0]);
+    is(url.href, 'http://www.example.net/?a=' + encoding[i][1]);
 
-    runTest();
-  }
-
-  function testOrdering() {
-    var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
-    is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
-    is(a.getAll('a').length, 3, "Correct length of getAll()");
-
-    var b = new URLSearchParams();
-    b.append('a', '1');
-    b.append('b', '2');
-    b.append('a', '3');
-    is(b.toString(), "a=1&b=2&a=3", "Order is correct");
-    is(b.getAll('a').length, 2, "Correct length of getAll()");
-
-    runTest();
+    var url2 = new URL(url.href);
+    is(url2.searchParams.get('a'), encoding[i][0], 'a is still there');
   }
 
-  function testDelete() {
-    var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
-    is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
-    is(a.getAll('a').length, 3, "Correct length of getAll()");
+  runTest();
+}
 
-    a.delete('a');
-    is(a.getAll('a').length, 0, "Correct length of getAll()");
-    is(a.toString(), "b=3&c=4&c=5", "Order is correct");
+function testCopyConstructor() {
+  var url = new URL("http://example.com/");
+  var p = url.searchParams;
+  var q = new URLSearchParams(p);
+  q.set("a", "b");
+  is(url.href, "http://example.com/",
+     "Messing with copy of URLSearchParams should not affect URL");
+  p.set("c", "d");
+  is(url.href, "http://example.com/?c=d",
+     "Messing with URLSearchParams should affect URL");
 
-    runTest();
-  }
+  runTest();
+}
+
+function testOrdering() {
+  var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
+  is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
+  is(a.getAll('a').length, 3, "Correct length of getAll()");
 
-  function testGetNULL() {
-    var u = new URLSearchParams();
-    is(typeof u.get(''), "object", "typeof URL.searchParams.get('')");
-    is(u.get(''), null, "URL.searchParams.get('') should be null");
+  var b = new URLSearchParams();
+  b.append('a', '1');
+  b.append('b', '2');
+  b.append('a', '3');
+  is(b.toString(), "a=1&b=2&a=3", "Order is correct");
+  is(b.getAll('a').length, 2, "Correct length of getAll()");
+
+  runTest();
+}
+
+function testDelete() {
+  var a = new URLSearchParams("a=1&a=2&b=3&c=4&c=5&a=6");
+  is(a.toString(), "a=1&a=2&b=3&c=4&c=5&a=6", "Order is correct");
+  is(a.getAll('a').length, 3, "Correct length of getAll()");
 
-    var url = new URL('http://www.example.net?a=b');
-    is(url.searchParams.get('b'), null, "URL.searchParams.get('b') should be null");
-    is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+  a.delete('a');
+  is(a.getAll('a').length, 0, "Correct length of getAll()");
+  is(a.toString(), "b=3&c=4&c=5", "Order is correct");
+
+  runTest();
+}
 
-    runTest();
-  }
+function testGetNULL() {
+  var u = new URLSearchParams();
+  is(typeof u.get(''), "object", "typeof URL.searchParams.get('')");
+  is(u.get(''), null, "URL.searchParams.get('') should be null");
+
+  var url = new URL('http://www.example.net?a=b');
+  is(url.searchParams.get('b'), null, "URL.searchParams.get('b') should be null");
+  is(url.searchParams.get('a'), 'b', "URL.searchParams.get('a')");
+
+  runTest();
+}
 
-  function testSet() {
-    var u = new URLSearchParams();
-    u.set('a','b');
-    u.set('e','c');
-    u.set('i','d');
-    u.set('o','f');
-    u.set('u','g');
+function testSet() {
+  var u = new URLSearchParams();
+  u.set('a','b');
+  u.set('e','c');
+  u.set('i','d');
+  u.set('o','f');
+  u.set('u','g');
+
+  is(u.get('a'), 'b', "URL.searchParams.get('a') should return b");
+  is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
 
-    is(u.get('a'), 'b', "URL.searchParams.get('a') should return b");
-    is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
+  u.set('a','h1');
+  u.set('a','h2');
+  u.set('a','h3');
+  u.set('a','h4');
+  is(u.get('a'), 'h4', "URL.searchParams.get('a') should return h4");
+  is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
 
-    u.set('a','h1');
-    u.set('a','h2');
-    u.set('a','h3');
-    u.set('a','h4');
-    is(u.get('a'), 'h4', "URL.searchParams.get('a') should return h4");
-    is(u.getAll('a').length, 1, "URLSearchParams.getAll('a').length should be 1");
+  is(u.get('e'), 'c', "URL.searchParams.get('e') should return c");
+  is(u.get('i'), 'd', "URL.searchParams.get('i') should return d");
+  is(u.get('o'), 'f', "URL.searchParams.get('o') should return f");
+  is(u.get('u'), 'g', "URL.searchParams.get('u') should return g");
+
+  is(u.getAll('e').length, 1, "URLSearchParams.getAll('e').length should be 1");
+  is(u.getAll('i').length, 1, "URLSearchParams.getAll('i').length should be 1");
+  is(u.getAll('o').length, 1, "URLSearchParams.getAll('o').length should be 1");
+  is(u.getAll('u').length, 1, "URLSearchParams.getAll('u').length should be 1");
 
-    is(u.get('e'), 'c', "URL.searchParams.get('e') should return c");
-    is(u.get('i'), 'd', "URL.searchParams.get('i') should return d");
-    is(u.get('o'), 'f', "URL.searchParams.get('o') should return f");
-    is(u.get('u'), 'g', "URL.searchParams.get('u') should return g");
+  u = new URLSearchParams("name1=value1&name1=value2&name1=value3");
+  is(u.get('name1'), 'value1', "URL.searchParams.get('name1') should return value1");
+  is(u.getAll('name1').length, 3, "URLSearchParams.getAll('name1').length should be 3");
+  u.set('name1','firstPair');
+  is(u.get('name1'), 'firstPair', "URL.searchParams.get('name1') should return firstPair");
+  is(u.getAll('name1').length, 1, "URLSearchParams.getAll('name1').length should be 1");
+
+  runTest();
+}
 
-    is(u.getAll('e').length, 1, "URLSearchParams.getAll('e').length should be 1");
-    is(u.getAll('i').length, 1, "URLSearchParams.getAll('i').length should be 1");
-    is(u.getAll('o').length, 1, "URLSearchParams.getAll('o').length should be 1");
-    is(u.getAll('u').length, 1, "URLSearchParams.getAll('u').length should be 1");
+function testIterable() {
+  var u = new URLSearchParams();
+  u.set('1','2');
+  u.set('2','4');
+  u.set('3','6');
+  u.set('4','8');
+  u.set('5','10');
 
-    u = new URLSearchParams("name1=value1&name1=value2&name1=value3");
-    is(u.get('name1'), 'value1', "URL.searchParams.get('name1') should return value1");
-    is(u.getAll('name1').length, 3, "URLSearchParams.getAll('name1').length should be 3");
-    u.set('name1','firstPair');
-    is(u.get('name1'), 'firstPair', "URL.searchParams.get('name1') should return firstPair");
-    is(u.getAll('name1').length, 1, "URLSearchParams.getAll('name1').length should be 1");
-
-    runTest();
+  var key_iter = u.keys();
+  var value_iter = u.values();
+  var entries_iter = u.entries();
+  for (var i = 0; i < 5; ++i) {
+    var v = i + 1;
+    var key = key_iter.next();
+    var value = value_iter.next();
+    var entry = entries_iter.next();
+    is(key.value, v.toString(), "Correct Key iterator: " + v.toString());
+    ok(!key.done, "Key.done is false");
+    is(value.value, (v * 2).toString(), "Correct Value iterator: " + (v * 2).toString());
+    ok(!value.done, "Value.done is false");
+    is(entry.value[0], v.toString(), "Correct Entry 0 iterator: " + v.toString());
+    is(entry.value[1], (v * 2).toString(), "Correct Entry 1 iterator: " + (v * 2).toString());
+    ok(!entry.done, "Entry.done is false");
   }
 
-  function testIterable() {
-    var u = new URLSearchParams();
-    u.set('1','2');
-    u.set('2','4');
-    u.set('3','6');
-    u.set('4','8');
-    u.set('5','10');
+  var last = key_iter.next();
+  ok(last.done, "Nothing more to read.");
+  is(last.value, undefined, "Undefined is the last key");
+
+  last = value_iter.next();
+  ok(last.done, "Nothing more to read.");
+  is(last.value, undefined, "Undefined is the last value");
+
+  last = entries_iter.next();
+  ok(last.done, "Nothing more to read.");
 
-    var key_iter = u.keys();
-    var value_iter = u.values();
-    var entries_iter = u.entries();
-    for (var i = 0; i < 5; ++i) {
-      var v = i + 1;
-      var key = key_iter.next();
-      var value = value_iter.next();
-      var entry = entries_iter.next();
-      is(key.value, v.toString(), "Correct Key iterator: " + v.toString());
-      ok(!key.done, "Key.done is false");
-      is(value.value, (v * 2).toString(), "Correct Value iterator: " + (v * 2).toString());
-      ok(!value.done, "Value.done is false");
-      is(entry.value[0], v.toString(), "Correct Entry 0 iterator: " + v.toString());
-      is(entry.value[1], (v * 2).toString(), "Correct Entry 1 iterator: " + (v * 2).toString());
-      ok(!entry.done, "Entry.done is false");
-    }
+  key_iter = u.keys();
+  key_iter.next();
+  key_iter.next();
+  u.delete('1');
+  u.delete('2');
+  u.delete('3');
+  u.delete('4');
+  u.delete('5');
 
-    var last = key_iter.next();
-    ok(last.done, "Nothing more to read.");
-    is(last.value, undefined, "Undefined is the last key");
+  last = key_iter.next();
+  ok(last.done, "Nothing more to read.");
+  is(last.value, undefined, "Undefined is the last key");
 
-    last = value_iter.next();
-    ok(last.done, "Nothing more to read.");
-    is(last.value, undefined, "Undefined is the last value");
-
-    last = entries_iter.next();
-    ok(last.done, "Nothing more to read.");
+  runTest();
+}
+function testZeroHandling() {
+  var u = new URLSearchParams;
+  u.set("a", "b\0c");
+  u.set("d\0e", "f");
+  u.set("g\0h", "i\0j");
+  is(u.toString(), "a=b%00c&d%00e=f&g%00h=i%00j",
+     "Should encode U+0000 as %00");
 
-    key_iter = u.keys();
-    key_iter.next();
-    key_iter.next();
-    u.delete('1');
-    u.delete('2');
-    u.delete('3');
-    u.delete('4');
-    u.delete('5');
+  runTest();
+}
 
-    last = key_iter.next();
-    ok(last.done, "Nothing more to read.");
-    is(last.value, undefined, "Undefined is the last key");
+function testCTORs() {
+  var a = new URLSearchParams("a=b");
+  is(a.get("a"), "b", "CTOR with string");
+
+  var b = new URLSearchParams([['a', 'b'], ['c', 'd']]);
+  is(b.get("a"), "b", "CTOR with sequence");
+  is(b.get("c"), "d", "CTOR with sequence");
 
-    runTest();
-  }
-  function testZeroHandling() {
-    var u = new URLSearchParams;
-    u.set("a", "b\0c");
-    u.set("d\0e", "f");
-    u.set("g\0h", "i\0j");
-    is(u.toString(), "a=b%00c&d%00e=f&g%00h=i%00j",
-       "Should encode U+0000 as %00");
+  ok(new URLSearchParams([]), "CTOR with empty sequence");
 
-    runTest();
+  let result;
+  try {
+    result = new URLSearchParams([[1]]);
+  } catch(e) {
+    result = 42;
   }
 
-  function testCTORs() {
-    var a = new URLSearchParams("a=b");
-    is(a.get("a"), "b", "CTOR with string");
+  is(result, 42, "CTOR throws if the sequence doesn't contain exactly 2 elements");
 
-    var b = new URLSearchParams([['a', 'b'], ['c', 'd']]);
-    is(b.get("a"), "b", "CTOR with sequence");
-    is(b.get("c"), "d", "CTOR with sequence");
-
-    ok(new URLSearchParams([]), "CTOR with empty sequence");
+  try {
+    result = new URLSearchParams([[1,2,3]]);
+  } catch(e) {
+    result = 43;
+  }
+  is(result, 43, "CTOR throws if the sequence doesn't contain exactly 2 elements");
 
-    let result;
-    try {
-      result = new URLSearchParams([[1]]);
-    } catch(e) {
-      result = 42;
-    }
-
-    is(result, 42, "CTOR throws if the sequence doesn't contain exactly 2 elements");
+  var c = new URLSearchParams({ a: 'b', c: 42, d: null, e: [1,2,3], f: {a:42} });
+  is(c.get('a'), 'b', "CTOR with record<>");
+  is(c.get('c'), '42', "CTOR with record<>");
+  is(c.get('d'), 'null', "CTOR with record<>");
+  is(c.get('e'), [1,2,3].toString(), "CTOR with record<>");
+  is(c.get('f'), {a:42}.toString(), "CTOR with record<>");
 
-    try {
-      result = new URLSearchParams([[1,2,3]]);
-    } catch(e) {
-      result = 43;
-    }
-    is(result, 43, "CTOR throws if the sequence doesn't contain exactly 2 elements");
-
-    runTest();
-  }
-
+  runTest();
+}
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -22,17 +22,16 @@ interface KeyboardEvent : UIEvent
   const unsigned long DOM_KEY_LOCATION_RIGHT    = 0x02;
   const unsigned long DOM_KEY_LOCATION_NUMPAD   = 0x03;
 
   readonly attribute unsigned long location;
   readonly attribute boolean       repeat;
   readonly attribute boolean       isComposing;
 
   readonly attribute DOMString key;
-  [Pref="dom.keyboardevent.code.enabled"]
   readonly attribute DOMString code;
 
   // This returns the initialized dictionary for generating a
   // same-type keyboard event
   [Cached, ChromeOnly, Constant]
   readonly attribute KeyboardEventInit initDict;
 };
 
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -160,33 +160,31 @@ CreateVariationDictionaryOrNull(CGFontRe
 }
 
 gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
                        bool aNeedsBold)
     : gfxFont(aFontEntry, aFontStyle),
       mCGFont(nullptr),
       mCTFont(nullptr),
       mFontFace(nullptr),
-      mVariationFont(false)
+      mVariationFont(aFontEntry->HasVariations())
 {
     mApplySyntheticBold = aNeedsBold;
 
-    auto varCount = aFontStyle->variationSettings.Length();
-    if (varCount > 0) {
+    if (mVariationFont && aFontStyle->variationSettings.Length() > 0) {
         CGFontRef baseFont = aFontEntry->GetFontRef();
         if (!baseFont) {
             mIsValid = false;
             return;
         }
         CFDictionaryRef variations =
             CreateVariationDictionaryOrNull(baseFont, aFontStyle->variationSettings);
         if (variations) {
             mCGFont = ::CGFontCreateCopyWithVariations(baseFont, variations);
             ::CFRelease(variations);
-            mVariationFont = true;
         } else {
             ::CFRetain(baseFont);
             mCGFont = baseFont;
         }
     } else {
         mCGFont = aFontEntry->GetFontRef();
         if (!mCGFont) {
             mIsValid = false;
--- a/gfx/thebes/gfxMacFont.h
+++ b/gfx/thebes/gfxMacFont.h
@@ -101,12 +101,12 @@ protected:
 
     cairo_font_face_t    *mFontFace;
 
     mozilla::UniquePtr<gfxFontShaper> mCoreTextShaper;
 
     Metrics               mMetrics;
     uint32_t              mSpaceGlyph;
 
-    bool                  mVariationFont; // true if variations are in effect
+    bool                  mVariationFont; // true if font has OpenType variations
 };
 
 #endif /* GFX_MACFONT_H */
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -49,16 +49,17 @@ public:
 
     void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                 FontListSizes* aSizes) const override;
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
 
     bool RequiresAATLayout() const { return mRequiresAAT; }
 
+    bool HasVariations();
     bool IsCFF();
 
 protected:
     gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle,
                                 bool aNeedsBold) override;
 
     bool HasFontTable(uint32_t aTableTag) override;
 
@@ -67,16 +68,18 @@ protected:
     CGFontRef mFontRef; // owning reference to the CGFont, released on destruction
 
     double mSizeHint;
 
     bool mFontRefInitialized;
     bool mRequiresAAT;
     bool mIsCFF;
     bool mIsCFFInitialized;
+    bool mHasVariations;
+    bool mHasVariationsInitialized;
     nsTHashtable<nsUint32HashKey> mAvailableTables;
 };
 
 class gfxMacPlatformFontList : public gfxPlatformFontList {
 public:
     static gfxMacPlatformFontList* PlatformFontList() {
         return static_cast<gfxMacPlatformFontList*>(sPlatformFontList);
     }
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -243,16 +243,27 @@ MacOSFontEntry::ReadCMAP(FontInfoData *a
 
 gfxFont*
 MacOSFontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
     return new gfxMacFont(this, aFontStyle, aNeedsBold);
 }
 
 bool
+MacOSFontEntry::HasVariations()
+{
+    if (!mHasVariationsInitialized) {
+        mHasVariationsInitialized = true;
+        mHasVariations = HasFontTable(TRUETYPE_TAG('f','v','a','r'));
+    }
+
+    return mHasVariations;
+}
+
+bool
 MacOSFontEntry::IsCFF()
 {
     if (!mIsCFFInitialized) {
         mIsCFFInitialized = true;
         mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
     }
 
     return mIsCFF;
@@ -263,34 +274,38 @@ MacOSFontEntry::MacOSFontEntry(const nsA
                                bool aIsStandardFace,
                                double aSizeHint)
     : gfxFontEntry(aPostscriptName, aIsStandardFace),
       mFontRef(NULL),
       mSizeHint(aSizeHint),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
-      mIsCFFInitialized(false)
+      mIsCFFInitialized(false),
+      mHasVariations(false),
+      mHasVariationsInitialized(false)
 {
     mWeight = aWeight;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                CGFontRef aFontRef,
                                uint16_t aWeight, uint16_t aStretch,
                                uint8_t aStyle,
                                bool aIsDataUserFont,
                                bool aIsLocalUserFont)
     : gfxFontEntry(aPostscriptName, false),
       mFontRef(NULL),
       mSizeHint(0.0),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
-      mIsCFFInitialized(false)
+      mIsCFFInitialized(false),
+      mHasVariations(false),
+      mHasVariationsInitialized(false)
 {
     mFontRef = aFontRef;
     mFontRefInitialized = true;
     ::CFRetain(mFontRef);
 
     mWeight = aWeight;
     mStretch = aStretch;
     mFixedPitch = false; // xxx - do we need this for downloaded fonts?
--- a/ipc/app/Makefile.in
+++ b/ipc/app/Makefile.in
@@ -20,17 +20,21 @@ include $(topsrcdir)/config/rules.mk
 
 ifneq ($(MOZ_WIDGET_TOOLKIT),android)
 #LIBS += ../contentproc/$(LIB_PREFIX)plugin-container.$(LIB_SUFFIX)
 endif
 
 ifeq ($(OS_ARCH),WINNT) #{
 # Note the manifest file exists in the tree, so we use the explicit filename
 # here.
-EXTRA_DEPS += plugin-container.exe.manifest
+ifdef HAVE_64BIT_BUILD
+EXTRA_DEPS += plugin-container.exe.64.manifest
+else
+EXTRA_DEPS += plugin-container.exe.32.manifest
+endif
 endif #}
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) #{
 
 libs::
 	$(NSINSTALL) -D $(DIST)/bin/$(PROGRAM).app
 	rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app 
 	sed -e 's/%PROGRAM%/$(MOZ_CHILD_PROCESS_NAME)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/bin/$(MOZ_CHILD_PROCESS_NAME).app/Contents/Info.plist
--- a/ipc/app/moz.build
+++ b/ipc/app/moz.build
@@ -48,16 +48,18 @@ if CONFIG['OS_ARCH'] == 'WINNT':
             'winmm.dll',
             'user32.dll',
         ]
 
     DELAYLOAD_DLLS += [
         'xul.dll',
     ]
 
+    RCINCLUDE = 'plugin-container.rc'
+
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
     # For sandbox includes and the include dependencies those have
     LOCAL_INCLUDES += [
         '/security/sandbox/chromium',
         '/security/sandbox/chromium-shim',
     ]
     USE_LIBS += [
         'mozsandbox',
rename from ipc/app/plugin-container.exe.manifest
rename to ipc/app/plugin-container.exe.32.manifest
--- a/ipc/app/plugin-container.exe.manifest
+++ b/ipc/app/plugin-container.exe.32.manifest
@@ -14,16 +14,22 @@
                         name="Microsoft.Windows.Common-Controls"
                         version="6.0.0.0"
                         processorArchitecture="*"
                         publicKeyToken="6595b64144ccf1df"
                         language="*"
                 />
         </dependentAssembly>
 </dependency>
+<comInterfaceExternalProxyStub
+    iid="{618736E0-3C3D-11CF-810C-00AA00389B71}"
+    proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"
+    name="IAccessible"
+    tlbid="{1EA4DBF0-3C3B-11CF-810C-00AA00389B71}"
+/>
   <ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
     <ms_asmv3:security>
       <ms_asmv3:requestedPrivileges>
         <ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
       </ms_asmv3:requestedPrivileges>
     </ms_asmv3:security>
   </ms_asmv3:trustInfo>
   <ms_asmv3:application xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
copy from ipc/app/plugin-container.exe.manifest
copy to ipc/app/plugin-container.exe.64.manifest
new file mode 100644
--- /dev/null
+++ b/ipc/app/plugin-container.rc
@@ -0,0 +1,14 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <windows.h>
+#include "mozilla-config.h"
+
+#if defined(HAVE_64BIT_BUILD)
+1 RT_MANIFEST "plugin-container.exe.64.manifest"
+#else
+1 RT_MANIFEST "plugin-container.exe.32.manifest"
+#endif
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/1349308-1.html
@@ -0,0 +1,2 @@
+<!-- bold font should *not* have the same glyph widths as regular face -->
+<span style="font:36px -apple-system;font-weight:bold;padding:2px;background:black">abcdefghijklmnopqrstuvwxyz
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/1349308-notref.html
@@ -0,0 +1,1 @@
+<span style="font:36px -apple-system;font-weight:normal;padding:2px;background:black">abcdefghijklmnopqrstuvwxyz
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -182,16 +182,17 @@ HTTP(..) != fallback-mark-stacking-1.htm
 == 726392-2.html 726392-2-ref.html
 == 726392-3.html 726392-3-ref.html
 == 745555-1.html 745555-1-ref.html
 == 745555-2.html 745555-2-ref.html
 == 820255.html 820255-ref.html
 HTTP(..) != 1170688.html 1170688-ref.html
 fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 1320665-cmap-format-13.html 1320665-cmap-format-13-ref.html # see bug 1320665 comments 8-9
 HTTP(..) == 1331339-script-extensions-shaping-1.html 1331339-script-extensions-shaping-1-ref.html
+skip-if(!cocoaWidget) != 1349308-1.html 1349308-notref.html # macOS-specific test for -apple-system glyph metrics
 
 # ensure emoji chars don't render blank (bug 715798, bug 779042);
 # should at least render hexboxes if there's no font support
 != emoji-01.html emoji-01-notref.html
 != emoji-02.html emoji-02-notref.html
 
 # Bug 727276: tests with variation selectors 15 and 16 to control emoji rendering style
 == emoji-03.html emoji-03-ref.html
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -395,30 +395,33 @@ nsXULPopupManager::GetSubmenuWidgetChain
   // this method is used by the widget code to determine the list of popups
   // that are open. If a mouse click occurs outside one of these popups, the
   // panels will roll up. If the click is inside a popup, they will not roll up
   uint32_t count = 0, sameTypeCount = 0;
 
   NS_ASSERTION(aWidgetChain, "null parameter");
   nsMenuChainItem* item = GetTopVisibleMenu();
   while (item) {
-    nsCOMPtr<nsIWidget> widget = item->Frame()->GetWidget();
-    NS_ASSERTION(widget, "open popup has no widget");
-    aWidgetChain->AppendElement(widget.get());
-    // In the case when a menulist inside a panel is open, clicking in the
-    // panel should still roll up the menu, so if a different type is found,
-    // stop scanning.
     nsMenuChainItem* parent = item->GetParent();
-    if (!sameTypeCount) {
-      count++;
-      if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
-                     item->IsContextMenu() != parent->IsContextMenu()) {
-        sameTypeCount = count;
+    if (!item->IsNoAutoHide()) {
+      nsCOMPtr<nsIWidget> widget = item->Frame()->GetWidget();
+      NS_ASSERTION(widget, "open popup has no widget");
+      aWidgetChain->AppendElement(widget.get());
+      // In the case when a menulist inside a panel is open, clicking in the
+      // panel should still roll up the menu, so if a different type is found,
+      // stop scanning.
+      if (!sameTypeCount) {
+        count++;
+        if (!parent || item->Frame()->PopupType() != parent->Frame()->PopupType() ||
+                       item->IsContextMenu() != parent->IsContextMenu()) {
+          sameTypeCount = count;
+        }
       }
     }
+
     item = parent;
   }
 
   return sameTypeCount;
 }
 
 nsIWidget*
 nsXULPopupManager::GetRollupWidget()
@@ -1043,17 +1046,23 @@ nsXULPopupManager::HidePopup(nsIContent*
       deselectMenu = aDeselectMenu;
       popupToHide = topMenu->Content();
       popupFrame = topMenu->Frame();
 
       // Close up another popup if there is one, and we are either hiding the
       // entire chain or the item to hide isn't the topmost popup.
       nsMenuChainItem* parent = topMenu->GetParent();
       if (parent && (aHideChain || topMenu != foundPopup)) {
-        nextPopup = parent->Content();
+        while (parent && parent->IsNoAutoHide()) {
+          parent = parent->GetParent();
+        }
+
+        if (parent) {
+          nextPopup = parent->Content();
+        }
       }
 
       lastPopup = aLastPopup ? aLastPopup : (aHideChain ? nullptr : aPopup);
     }
   } else if (popupFrame->PopupState() == ePopupPositioning) {
     // When the popup is in the popuppositioning state, it will not be in the
     // mPopups list. We need another way to find it and make sure it does not
     // continue the popup showing process.
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -44,21 +44,28 @@ import org.mozilla.gecko.util.GeckoBundl
 public class WebAppActivity extends GeckoApp {
 
     public static final String INTENT_KEY = "IS_A_WEBAPP";
     public static final String MANIFEST_PATH = "MANIFEST_PATH";
 
     private static final String LOGTAG = "WebAppActivity";
 
     private TextView mUrlView;
+    private String mManifestPath;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        loadManifest(getIntent());
+
+        if (savedInstanceState != null) {
+            mManifestPath = savedInstanceState.getString(WebAppActivity.MANIFEST_PATH, null);
+        } else {
+            mManifestPath = getIntent().getStringExtra(WebAppActivity.MANIFEST_PATH);
+        }
+        loadManifest(mManifestPath);
 
         final Toolbar toolbar = (Toolbar) findViewById(R.id.actionbar);
         setSupportActionBar(toolbar);
 
         final ProgressBar progressBar = (ProgressBar) findViewById(R.id.page_progress);
         progressBar.setVisibility(View.GONE);
 
         final ActionBar actionBar = getSupportActionBar();
@@ -104,16 +111,23 @@ public class WebAppActivity extends Geck
         }
 
         if (msg == Tabs.TabEvents.LOCATION_CHANGE) {
             mUrlView.setText(tab.getURL());
         }
     }
 
     @Override
+    protected void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+
+        outState.putString(WebAppActivity.MANIFEST_PATH, mManifestPath);
+    }
+
+    @Override
     public void onDestroy() {
         super.onDestroy();
         EventDispatcher.getInstance().unregisterUiThreadListener(this,
               "Website:AppEntered",
               "Website:AppLeft",
               null);
         Tabs.unregisterOnTabsChangedListener(this);
     }
@@ -135,33 +149,32 @@ public class WebAppActivity extends Geck
 
         final SafeIntent intent = new SafeIntent(externalIntent);
         final String launchUrl = intent.getDataString();
         final String currentUrl = Tabs.getInstance().getSelectedTab().getURL();
         final boolean isSameDomain = Uri.parse(currentUrl).getHost()
             .equals(Uri.parse(launchUrl).getHost());
 
         if (!isSameDomain) {
-            loadManifest(externalIntent);
+            mManifestPath = externalIntent.getStringExtra(WebAppActivity.MANIFEST_PATH);
+            loadManifest(mManifestPath);
             Tabs.getInstance().loadUrl(launchUrl);
         }
     }
 
-    private void loadManifest(Intent intent) {
-        String manifestPath = intent.getStringExtra(WebAppActivity.MANIFEST_PATH);
-        if (manifestPath != null) {
-            updateFromManifest(manifestPath);
+    private void loadManifest(String manifestPath) {
+        if (manifestPath == null) {
+            Log.e(LOGTAG, "Missing manifest");
+            return;
         }
-    }
 
-    private void updateFromManifest(String manifestPath) {
         try {
             final File manifestFile = new File(manifestPath);
             final JSONObject manifest = FileUtils.readJSONObjectFromFile(manifestFile);
-            final JSONObject manifestField = (JSONObject) manifest.get("manifest");
+            final JSONObject manifestField = manifest.getJSONObject("manifest");
             final Integer color = readColorFromManifest(manifestField);
             final String name = readNameFromManifest(manifestField);
             final Bitmap icon = readIconFromManifest(manifest);
             final ActivityManager.TaskDescription taskDescription = (color == null)
                 ? new ActivityManager.TaskDescription(name, icon)
                 : new ActivityManager.TaskDescription(name, icon, color);
 
             updateStatusBarColor(color);
@@ -175,37 +188,37 @@ public class WebAppActivity extends Geck
     private void updateStatusBarColor(final Integer themeColor) {
         if (themeColor != null && !AppConstants.Versions.preLollipop) {
             final Window window = getWindow();
             window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
             window.setStatusBarColor(ColorUtil.darken(themeColor, 0.25));
         }
     }
 
-    private Integer readColorFromManifest(JSONObject manifest) throws JSONException {
-        final String colorStr = (String) manifest.get("theme_color");
+    private Integer readColorFromManifest(JSONObject manifest) {
+        final String colorStr = manifest.optString("theme_color", null);
         if (colorStr != null) {
             return ColorUtil.parseStringColor(colorStr);
         }
         return null;
     }
 
-    private String readNameFromManifest(JSONObject manifest) throws JSONException {
-        String name = (String) manifest.get("name");
+    private String readNameFromManifest(JSONObject manifest) {
+        String name = manifest.optString("name", null);
         if (name == null) {
-            name = (String) manifest.get("short_name");
+            name = manifest.optString("short_name", null);
         }
         if (name == null) {
-            name = (String) manifest.get("start_url");
+            name = manifest.optString("start_url", null);
         }
         return name;
     }
 
-    private Bitmap readIconFromManifest(JSONObject manifest) throws JSONException {
-        final String iconStr = (String) manifest.get("cached_icon");
+    private Bitmap readIconFromManifest(JSONObject manifest) {
+        final String iconStr = manifest.optString("cached_icon", null);
         if (iconStr != null) {
             return FaviconDecoder
                 .decodeDataURI(getContext(), iconStr)
                 .getBestBitmap(GeckoAppShell.getPreferredIconSize());
         }
         return null;
     }
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -193,19 +193,16 @@ pref("dom.gamepad.enabled", true);
 pref("dom.gamepad.test.enabled", false);
 #ifdef RELEASE_OR_BETA
 pref("dom.gamepad.non_standard_events.enabled", false);
 #else
 pref("dom.gamepad.non_standard_events.enabled", true);
 #endif
 pref("dom.gamepad.extensions.enabled", false);
 
-// Whether the KeyboardEvent.code is enabled
-pref("dom.keyboardevent.code.enabled", true);
-
 // If this is true, TextEventDispatcher dispatches keydown and keyup events
 // even during composition (keypress events are never fired during composition
 // even if this is true).
 pref("dom.keyboardevent.dispatch_during_composition", false);
 
 // Whether to run add-on code in different compartments from browser code. This
 // causes a separate compartment for each (addon, global) combination, which may
 // significantly increase the number of compartments in the system.
@@ -3076,16 +3073,19 @@ pref("dom.ipc.plugins.forcedirect.enable
 #endif
 
 #ifdef NIGHTLY_BUILD
 pref("dom.ipc.processCount", 4);
 #else
 pref("dom.ipc.processCount", 1);
 #endif
 
+// Default to allow only one file:// URL content process.
+pref("dom.ipc.processCount.file", 1);
+
 // WebExtensions only support a single extension process.
 pref("dom.ipc.processCount.extension", 1);
 
 // Disable support for SVG
 pref("svg.disabled", false);
 
 // Override default dom.ipc.processCount for some remote content process types.
 pref("dom.ipc.processCount.webLargeAllocation", 10);
--- a/parser/html/nsHtml5AtomTable.cpp
+++ b/parser/html/nsHtml5AtomTable.cpp
@@ -19,16 +19,17 @@ nsHtml5AtomEntry::nsHtml5AtomEntry(const
   NS_NOTREACHED("nsHtml5AtomTable is broken and tried to copy an entry");
 }
 
 nsHtml5AtomEntry::~nsHtml5AtomEntry()
 {
 }
 
 nsHtml5AtomTable::nsHtml5AtomTable()
+  : mRecentlyUsedParserAtoms{}
 {
 #ifdef DEBUG
   NS_GetMainThread(getter_AddRefs(mPermittedLookupThread));
 #endif
 }
 
 nsHtml5AtomTable::~nsHtml5AtomTable()
 {
@@ -39,18 +40,28 @@ nsHtml5AtomTable::GetAtom(const nsAStrin
 {
 #ifdef DEBUG
   {
     nsCOMPtr<nsIThread> currentThread;
     NS_GetCurrentThread(getter_AddRefs(currentThread));
     NS_ASSERTION(mPermittedLookupThread == currentThread, "Wrong thread!");
   }
 #endif
+
+  uint32_t index = mozilla::HashString(aKey) % RECENTLY_USED_PARSER_ATOMS_SIZE;
+  nsIAtom* cachedAtom = mRecentlyUsedParserAtoms[index];
+  if (cachedAtom && cachedAtom->Equals(aKey)) {
+    return cachedAtom;
+  }
+
   nsIAtom* atom = NS_GetStaticAtom(aKey);
   if (atom) {
+    mRecentlyUsedParserAtoms[index] = atom;
     return atom;
   }
   nsHtml5AtomEntry* entry = mTable.PutEntry(aKey);
   if (!entry) {
     return nullptr;
   }
+
+  mRecentlyUsedParserAtoms[index] = entry->GetAtom();
   return entry->GetAtom();
 }
--- a/parser/html/nsHtml5AtomTable.h
+++ b/parser/html/nsHtml5AtomTable.h
@@ -6,16 +6,18 @@
 #define nsHtml5AtomTable_h
 
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "nsAutoPtr.h"
 #include "nsIAtom.h"
 #include "nsIThread.h"
 
+#define RECENTLY_USED_PARSER_ATOMS_SIZE 31
+
 class nsHtml5Atom;
 
 class nsHtml5AtomEntry : public nsStringHashKey
 {
   public:
     explicit nsHtml5AtomEntry(KeyTypePointer aStr);
     nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther);
     ~nsHtml5AtomEntry();
@@ -82,26 +84,30 @@ class nsHtml5AtomTable
      */
     nsIAtom* GetAtom(const nsAString& aKey);
     
     /**
      * Empties the table.
      */
     void Clear()
     {
+      for (uint32_t i = 0; i < RECENTLY_USED_PARSER_ATOMS_SIZE; ++i) {
+        mRecentlyUsedParserAtoms[i] = nullptr;
+      }
       mTable.Clear();
     }
     
 #ifdef DEBUG
     void SetPermittedLookupThread(nsIThread* aThread)
     {
       mPermittedLookupThread = aThread;
     }
 #endif  
   
   private:
     nsTHashtable<nsHtml5AtomEntry> mTable;
+    nsIAtom* mRecentlyUsedParserAtoms[RECENTLY_USED_PARSER_ATOMS_SIZE];
 #ifdef DEBUG
     nsCOMPtr<nsIThread>            mPermittedLookupThread;
 #endif
 };
 
 #endif // nsHtml5AtomTable_h
--- a/testing/mozharness/scripts/release/publish_balrog.py
+++ b/testing/mozharness/scripts/release/publish_balrog.py
@@ -48,17 +48,17 @@ class PublishBalrog(MercurialScript, Bui
         # taskcluster properties
         self.read_buildbot_config()
         if not self.buildbot_config:
             self.warning("Skipping buildbot properties overrides")
             return
         # TODO: version and appVersion should come from repo
         props = self.buildbot_config["properties"]
         for prop in ['product', 'version', 'build_number', 'channels',
-                     'balrog_api_root']:
+                     'balrog_api_root', 'schedule_at', 'background_rate']:
             if props.get(prop):
                 self.info("Overriding %s with %s" % (prop, props[prop]))
                 self.config[prop] = props.get(prop)
 
     def query_abs_dirs(self):
         if self.abs_dirs:
             return self.abs_dirs
         self.abs_dirs = super(PublishBalrog, self).query_abs_dirs()
@@ -102,14 +102,18 @@ class PublishBalrog(MercurialScript, Bui
             "--username", self.config["balrog_username"],
             "--version", self.config["version"],
             "--product", self.config["product"],
             "--build-number", str(self.config["build_number"]),
             "--verbose",
         ])
         for r in channel_config["publish_rules"]:
             cmd.extend(["--rules", r])
+        if self.config.get("schedule_at"):
+            cmd.extend(["--schedule-at", self.config["schedule_at"]])
+        if self.config.get("background_rate"):
+            cmd.extend(["--background-rate", str(self.config["background_rate"])])
 
         self.retry(lambda: self.run_command(cmd, halt_on_failure=True))
 
 # __main__ {{{1
 if __name__ == '__main__':
     PublishBalrog().run_and_exit()