Merge mozilla-central to inbound. r=merge a=merge on a CLOSED TREE
authorBogdan Tara <btara@mozilla.com>
Mon, 04 Dec 2017 22:47:53 +0200
changeset 394958 b580f65bdb2108b44a18219dc8f4a78467c7e0c0
parent 394957 5c2c9bbf9fccd746f3e61afc8da3acc8d0b8e98a (current diff)
parent 394896 5be384bcf00191f97d32b4ac3ecd1b85ec7b18e1 (diff)
child 394959 7324518f32644f799a1d89dfc2c8e2a2d165802f
push id33026
push usershindli@mozilla.com
push dateTue, 05 Dec 2017 09:59:24 +0000
treeherdermozilla-central@b4cef8d1dff0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to inbound. r=merge a=merge on a CLOSED TREE
browser/base/content/test/general/browser_bug580638.js
browser/base/content/test/general/browser_pinnedTabs.js
devtools/client/shared/vendor/REACT_REDUX_UPGRADING
devtools/client/shared/vendor/REACT_VIRTUALIZED_UPGRADING
devtools/client/shared/vendor/react-addons-shallow-compare.js
devtools/client/shared/vendor/react-virtualized.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_deactivateHUDForContext_unfocused_window.js
devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_history_nav.js
devtools/client/webconsole/new-console-output/test/mochitest/test-bug-597136-external-script-errors.html
devtools/client/webconsole/new-console-output/test/mochitest/test-bug-597136-external-script-errors.js
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
intl/locale/android/nsAndroidCharset.cpp
intl/locale/mac/nsMacCharset.cpp
intl/locale/nsIPlatformCharset.h
intl/locale/nsPlatformCharset.h
intl/locale/unix/moz.build
intl/locale/unix/nsUNIXCharset.cpp
intl/locale/unix/unixcharset.properties
intl/locale/windows/nsWinCharset.cpp
intl/locale/windows/wincharset.properties
layout/generic/TextDrawTarget.h
layout/reftests/bugs/1364335-ref.html
layout/reftests/bugs/1364335.html
mobile/android/app/src/photon/res/drawable-hdpi/flat_icon.png
mobile/android/app/src/photon/res/drawable-xhdpi/flat_icon.png
mobile/android/app/src/photon/res/drawable-xxhdpi/flat_icon.png
mobile/android/app/src/photon/res/drawable-xxxhdpi/flat_icon.png
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/FullScreenState.java
mobile/android/geckoview/src/main/java/org/mozilla/gecko/gfx/LayerView.java
modules/libpref/init/all.js
services/common/modules-testing/storageserver.js
services/common/tests/mach_commands.py
services/common/tests/run_storage_server.js
services/common/tests/unit/test_storage_server.js
testing/web-platform/meta/css/css-shapes/shape-outside/values/shape-image-threshold-000.html.ini
testing/web-platform/meta/webvtt/parsing/file-parsing/tests/settings-position.html.ini
third_party/python/fluent/PKG-INFO
third_party/python/fluent/setup.cfg
third_party/python/fluent/setup.py
third_party/rust/byteorder-1.0.0/.cargo-checksum.json
third_party/rust/byteorder-1.0.0/.travis.yml
third_party/rust/byteorder-1.0.0/COPYING
third_party/rust/byteorder-1.0.0/Cargo.toml
third_party/rust/byteorder-1.0.0/LICENSE-MIT
third_party/rust/byteorder-1.0.0/README.md
third_party/rust/byteorder-1.0.0/UNLICENSE
third_party/rust/byteorder-1.0.0/benches/bench.rs
third_party/rust/byteorder-1.0.0/src/lib.rs
third_party/rust/byteorder-1.0.0/src/new.rs
toolkit/themes/linux/mozapps/plugins/pluginGeneric-16.png
toolkit/themes/linux/mozapps/plugins/pluginGeneric.png
toolkit/themes/osx/mozapps/plugins/pluginGeneric-16.png
toolkit/themes/osx/mozapps/plugins/pluginGeneric.png
toolkit/themes/windows/mozapps/plugins/pluginGeneric-16.png
toolkit/themes/windows/mozapps/plugins/pluginGeneric.png
toolkit/themes/windows/mozapps/plugins/pluginInstallerWizard.css
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -1476,16 +1476,23 @@ AccessibleWrap::GetIAccessibleFor(const 
   }
 
   // If the MSAA ID is not a chrome id then we already know that we won't
   // find it here and should look remotely instead. This handles the case when
   // accessible is part of the chrome process and is part of the xul browser
   // window and the child id points in the content documents. Thus we need to
   // make sure that it is never called on proxies.
   if (XRE_IsParentProcess() && !IsProxy() && !sIDGen.IsChromeID(varChild.lVal)) {
+    if (!IsRoot()) {
+      // Bug 1422201: accChild with a remote id is only valid on the root accessible.
+      // Otherwise, we might return remote accessibles which aren't descendants
+      // of this accessible. This would confuse clients which use accChild to
+      // check whether something is a descendant of a document.
+      return nullptr;
+    }
     return GetRemoteIAccessibleFor(varChild);
   }
 
   if (varChild.lVal > 0) {
     // Gecko child indices are 0-based in contrast to indices used in MSAA.
     MOZ_ASSERT(!IsProxy());
     Accessible* xpAcc = GetChildAt(varChild.lVal - 1);
     if (!xpAcc) {
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -73,31 +73,31 @@ LPROJ := Contents/Resources/$(LPROJ_ROOT
 clean clobber repackage::
 	$(RM) -r $(dist_dest)
 
 MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h)
 
 .PHONY: repackage
 tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME) features
 	rm -rf $(dist_dest)
-	$(MKDIR) -p $(dist_dest)/Contents/MacOS
-	$(MKDIR) -p $(dist_dest)/$(LPROJ)
-	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
-	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/$(LPROJ)
-	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' -e 's|%MOZ_DEVELOPER_REPO_PATH%|$(topsrcdir)|' -e 's|%MOZ_DEVELOPER_OBJ_PATH%|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
-	sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > $(dist_dest)/$(LPROJ)/InfoPlist.strings
-	rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ $(dist_dest)/Contents/Resources
-	rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ $(dist_dest)/Contents/MacOS
-	$(RM) $(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)
-	rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) $(dist_dest)/Contents/MacOS
-	cp -RL $(DIST)/branding/firefox.icns $(dist_dest)/Contents/Resources/firefox.icns
-	cp -RL $(DIST)/branding/document.icns $(dist_dest)/Contents/Resources/document.icns
-	$(MKDIR) -p $(dist_dest)/Contents/Library/LaunchServices
+	$(MKDIR) -p '$(dist_dest)/Contents/MacOS'
+	$(MKDIR) -p '$(dist_dest)/$(LPROJ)'
+	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents '$(dist_dest)' --exclude English.lproj
+	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ '$(dist_dest)/$(LPROJ)'
+	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' -e 's|%MOZ_DEVELOPER_REPO_PATH%|$(topsrcdir)|' -e 's|%MOZ_DEVELOPER_OBJ_PATH%|$(topobjdir)|' $(srcdir)/macbuild/Contents/Info.plist.in > '$(dist_dest)/Contents/Info.plist'
+	sed -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | iconv -f UTF-8 -t UTF-16 > '$(dist_dest)/$(LPROJ)/InfoPlist.strings'
+	rsync -a --exclude-from='$(srcdir)/macbuild/Contents/MacOS-files.in' $(DIST)/bin/ '$(dist_dest)/Contents/Resources'
+	rsync -a --include-from='$(srcdir)/macbuild/Contents/MacOS-files.in' --exclude '*' $(DIST)/bin/ '$(dist_dest)/Contents/MacOS'
+	$(RM) '$(dist_dest)/Contents/MacOS/$(MOZ_APP_NAME)'
+	rsync -aL $(DIST)/bin/$(MOZ_APP_NAME) '$(dist_dest)/Contents/MacOS'
+	cp -RL $(DIST)/branding/firefox.icns '$(dist_dest)/Contents/Resources/firefox.icns'
+	cp -RL $(DIST)/branding/document.icns '$(dist_dest)/Contents/Resources/document.icns'
+	$(MKDIR) -p '$(dist_dest)/Contents/Library/LaunchServices'
 ifdef MOZ_UPDATER
-	mv -f $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater $(dist_dest)/Contents/Library/LaunchServices
-	ln -s ../../../../Library/LaunchServices/org.mozilla.updater $(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater
+	mv -f '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater' '$(dist_dest)/Contents/Library/LaunchServices'
+	ln -s ../../../../Library/LaunchServices/org.mozilla.updater '$(dist_dest)/Contents/MacOS/updater.app/Contents/MacOS/org.mozilla.updater'
 endif
-	printf APPLMOZB > $(dist_dest)/Contents/PkgInfo
+	printf APPLMOZB > '$(dist_dest)/Contents/PkgInfo'
 endif
 
 .PHONY: features
 tools features::
 	$(PYTHON) -c 'import os, json; listing = {"system": sorted(os.listdir("$(DIST)/bin/browser/features"))}; print json.dumps(listing)' > $(DIST)/bin/browser/chrome/browser/content/browser/built_in_addons.json
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -577,16 +577,22 @@ pref("privacy.sanitize.sanitizeOnShutdow
 
 pref("privacy.sanitize.migrateFx3Prefs",    false);
 
 pref("privacy.panicButton.enabled",         true);
 
 // Time until temporary permissions expire, in ms
 pref("privacy.temporary_permission_expire_time_ms",  3600000);
 
+// If Accept-Language should be spoofed by en-US
+// 0 - will prompt
+// 1 - don't spoof
+// 2 - spoof
+pref("privacy.spoof_english", 0);
+
 pref("network.proxy.share_proxy_settings",  false); // use the same proxy settings for all protocols
 
 // simple gestures support
 pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
 pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
 pref("browser.gesture.swipe.up", "cmd_scrollTop");
 pref("browser.gesture.swipe.down", "cmd_scrollBottom");
 #ifdef XP_MACOSX
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -26,17 +26,17 @@
     <command id="Browser:SavePage" oncommand="saveBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="Browser:SendLink"
              oncommand="MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);"/>
 
     <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
     <command id="cmd_print" oncommand="PrintUtils.printWindow(window.gBrowser.selectedBrowser.outerWindowID, window.gBrowser.selectedBrowser);"/>
     <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
-    <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
+    <command id="cmd_close" oncommand="BrowserCloseTabOrWindow(event);"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
     <command id="cmd_toggleMute" oncommand="gBrowser.selectedTab.toggleMuteAudio()"/>
     <command id="cmd_CustomizeToolbars" oncommand="gCustomizeMode.enter()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
 
 
     <commandset id="editMenuCommands"/>
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -29,16 +29,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   ContentSearch: "resource:///modules/ContentSearch.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
   Deprecated: "resource://gre/modules/Deprecated.jsm",
   DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   E10SUtils: "resource:///modules/E10SUtils.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
+  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   Log: "resource://gre/modules/Log.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
@@ -1816,16 +1817,18 @@ var gBrowserInit = {
     CaptivePortalWatcher.uninit();
 
     SidebarUI.uninit();
 
     DownloadsButton.uninit();
 
     gAccessibilityServiceIndicator.uninit();
 
+    LanguagePrompt.uninit();
+
     // Now either cancel delayedStartup, or clean up the services initialized from
     // it.
     if (this._boundDelayedStartup) {
       this._cancelDelayedStartup();
     } else {
       if (Win7Features)
         Win7Features.onCloseWindow();
 
@@ -2279,43 +2282,16 @@ function BrowserOpenTab(event) {
         where = "tab";
         break;
     }
   }
 
   openUILinkIn(BROWSER_NEW_TAB_URL, where, { relatedToCurrent });
 }
 
-/* Called from the openLocation dialog. This allows that dialog to instruct
-   its opener to open a new window and then step completely out of the way.
-   Anything less byzantine is causing horrible crashes, rather believably,
-   though oddly only on Linux. */
-function delayedOpenWindow(chrome, flags, href, postData) {
-  // The other way to use setTimeout,
-  // setTimeout(openDialog, 10, chrome, "_blank", flags, url),
-  // doesn't work here.  The extra "magic" extra argument setTimeout adds to
-  // the callback function would confuse gBrowserInit.onLoad() by making
-  // window.arguments[1] be an integer instead of null.
-  setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10);
-}
-
-/* Required because the tab needs time to set up its content viewers and get the load of
-   the URI kicked off before becoming the active content area. */
-function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup) {
-  gBrowser.loadOneTab(aUrl, {
-    referrerURI: aReferrer,
-    charset: aCharset,
-    postData: aPostData,
-    inBackground: false,
-    allowThirdPartyFixup: aAllowThirdPartyFixup,
-    // Bug 1367168: only use systemPrincipal till we can remove that function
-    triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
-  });
-}
-
 var gLastOpenDirectory = {
   _lastDir: null,
   get path() {
     if (!this._lastDir || !this._lastDir.exists()) {
       try {
         this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
                                                      Ci.nsIFile);
         if (!this._lastDir.exists())
@@ -2367,23 +2343,34 @@ function BrowserOpenFileWindow() {
                      nsIFilePicker.filterImages | nsIFilePicker.filterXML |
                      nsIFilePicker.filterHTML);
     fp.displayDirectory = gLastOpenDirectory.path;
     fp.open(fpCallback);
   } catch (ex) {
   }
 }
 
-function BrowserCloseTabOrWindow() {
-  // If we're not a browser window, just close the window
+function BrowserCloseTabOrWindow(event) {
+  // If we're not a browser window, just close the window.
   if (window.location.href != getBrowserURL()) {
     closeWindow(true);
     return;
   }
 
+  // Keyboard shortcuts that would close a tab that is pinned select the first
+  // unpinned tab instead.
+  if (event &&
+      (event.ctrlKey || event.metaKey || event.altKey) &&
+      gBrowser.selectedTab.pinned) {
+    if (gBrowser.visibleTabs.length > gBrowser._numPinnedTabs) {
+      gBrowser.tabContainer.selectedIndex = gBrowser._numPinnedTabs;
+    }
+    return;
+  }
+
   // If the current tab is the last one, this will close the window.
   gBrowser.removeCurrentTab({animate: true});
 }
 
 function BrowserTryToCloseWindow() {
   if (WindowIsClosing())
     window.close(); // WindowIsClosing does all the necessary checks
 }
@@ -2910,32 +2897,30 @@ const PREF_SSL_IMPACT_ROOTS = ["security
  */
 var BrowserOnClick = {
   init() {
     let mm = window.messageManager;
     mm.addMessageListener("Browser:CertExceptionError", this);
     mm.addMessageListener("Browser:OpenCaptivePortalPage", this);
     mm.addMessageListener("Browser:SiteBlockedError", this);
     mm.addMessageListener("Browser:EnableOnlineMode", this);
-    mm.addMessageListener("Browser:SendSSLErrorReport", this);
     mm.addMessageListener("Browser:SetSSLErrorReportAuto", this);
     mm.addMessageListener("Browser:ResetSSLPreferences", this);
     mm.addMessageListener("Browser:SSLErrorReportTelemetry", this);
     mm.addMessageListener("Browser:SSLErrorGoBack", this);
 
     Services.obs.addObserver(this, "captive-portal-login-abort");
     Services.obs.addObserver(this, "captive-portal-login-success");
   },
 
   uninit() {
     let mm = window.messageManager;
     mm.removeMessageListener("Browser:CertExceptionError", this);
     mm.removeMessageListener("Browser:SiteBlockedError", this);
     mm.removeMessageListener("Browser:EnableOnlineMode", this);
-    mm.removeMessageListener("Browser:SendSSLErrorReport", this);
     mm.removeMessageListener("Browser:SetSSLErrorReportAuto", this);
     mm.removeMessageListener("Browser:ResetSSLPreferences", this);
     mm.removeMessageListener("Browser:SSLErrorReportTelemetry", this);
     mm.removeMessageListener("Browser:SSLErrorGoBack", this);
 
     Services.obs.removeObserver(this, "captive-portal-login-abort");
     Services.obs.removeObserver(this, "captive-portal-login-success");
   },
@@ -2968,21 +2953,16 @@ var BrowserOnClick = {
       break;
       case "Browser:EnableOnlineMode":
         if (Services.io.offline) {
           // Reset network state and refresh the page.
           Services.io.offline = false;
           msg.target.reload();
         }
       break;
-      case "Browser:SendSSLErrorReport":
-        this.onSSLErrorReport(msg.target,
-                              msg.data.uri,
-                              msg.data.securityInfo);
-      break;
       case "Browser:ResetSSLPreferences":
         let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
                 return prefs.concat(Services.prefs.getChildList(root));
         }, []);
         for (let prefName of prefSSLImpact) {
           Services.prefs.clearUserPref(prefName);
         }
         msg.target.reload();
@@ -3001,33 +2981,16 @@ var BrowserOnClick = {
           .add(reportStatus);
       break;
       case "Browser:SSLErrorGoBack":
         goBackFromErrorPage();
       break;
     }
   },
 
-  onSSLErrorReport(browser, uri, securityInfo) {
-    if (!Services.prefs.getBoolPref("security.ssl.errorReporting.enabled")) {
-      Cu.reportError("User requested certificate error report sending, but certificate error reporting is disabled");
-      return;
-    }
-
-    let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
-                           .getService(Ci.nsISerializationHelper);
-    let transportSecurityInfo = serhelper.deserializeObject(securityInfo);
-    transportSecurityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-
-    let errorReporter = Cc["@mozilla.org/securityreporter;1"]
-                          .getService(Ci.nsISecurityReporter);
-    errorReporter.reportTLSError(transportSecurityInfo,
-                                 uri.host, uri.port);
-  },
-
   onCertError(browser, elementId, isTopFrame, location, securityInfoAsString) {
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
     let securityInfo;
 
     switch (elementId) {
       case "exceptionDialogButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
@@ -3521,25 +3484,16 @@ var PrintPreviewListener = {
       SidebarUI.show(this._sidebarCommand);
   },
 
   activateBrowser(browser) {
     gBrowser.activateBrowserForPrintPreview(browser);
   },
 };
 
-function getMarkupDocumentViewer() {
-  return gBrowser.markupDocumentViewer;
-}
-
-// This function is obsolete. Newer code should use <tooltip page="true"/> instead.
-function FillInHTMLTooltip(tipElement) {
-  document.getElementById("aHTMLTooltip").fillInPageTooltip(tipElement);
-}
-
 var browserDragAndDrop = {
   canDropLink: aEvent => Services.droppedLinkHandler.canDropLink(aEvent, true),
 
   dragOver(aEvent) {
     if (this.canDropLink(aEvent)) {
       aEvent.preventDefault();
     }
   },
@@ -6313,30 +6267,16 @@ var gPageStyleMenu = {
   },
 
   disableStyle() {
     let mm = gBrowser.selectedBrowser.messageManager;
     mm.sendAsyncMessage("PageStyle:Disable");
   },
 };
 
-/* Legacy global page-style functions */
-var stylesheetFillPopup = gPageStyleMenu.fillPopup.bind(gPageStyleMenu);
-function stylesheetSwitchAll(contentWindow, title) {
-  // We ignore the contentWindow param. Add-ons don't appear to use
-  // it, and it's difficult to support in e10s (where it will be a
-  // CPOW).
-  gPageStyleMenu.switchStyleSheet(title);
-}
-function setStyleDisabled(disabled) {
-  if (disabled)
-    gPageStyleMenu.disableStyle();
-}
-
-
 var LanguageDetectionListener = {
   init() {
     window.messageManager.addMessageListener("Translation:DocumentState", msg => {
       Translation.documentStateReceived(msg.target, msg.data);
     });
   }
 };
 
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -437,24 +437,30 @@ var AboutNetAndCertErrorListener = {
     sendAsyncMessage("Browser:ResetSSLPreferences");
   },
 
   onSetAutomatic(evt) {
     sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
       automatic: evt.detail
     });
 
-    // if we're enabling reports, send a report for this failure
+    // If we're enabling reports, send a report for this failure.
     if (evt.detail) {
-      let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
-      sendAsyncMessage("Browser:SendSSLErrorReport", {
-        uri: { host, port },
-        securityInfo: getSerializedSecurityInfo(docShell),
-      });
+      let win = evt.originalTarget.ownerGlobal;
+      let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell);
 
+      let {securityInfo} = docShell.failedChannel;
+      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+      let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
+
+      let errorReporter = Cc["@mozilla.org/securityreporter;1"]
+                            .getService(Ci.nsISecurityReporter);
+      errorReporter.reportTLSError(securityInfo, host, port);
     }
   },
 };
 
 AboutNetAndCertErrorListener.init(this);
 AboutBlockedSiteListener.init(this);
 
 var ClickEventHandler = {
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -206,52 +206,16 @@
           if (this._statusPanel) {
             let browser = this.selectedBrowser;
             let browserContainer = this.getBrowserContainer(browser);
             browserContainer.insertBefore(this._statusPanel, browser.parentNode.nextSibling);
           }
         ]]></body>
       </method>
 
-      <method name="_setCloseKeyState">
-        <parameter name="aEnabled"/>
-        <body><![CDATA[
-          let keyClose = document.getElementById("key_close");
-          let closeKeyEnabled = keyClose.getAttribute("disabled") != "true";
-          if (closeKeyEnabled == aEnabled)
-            return;
-
-          if (aEnabled)
-            keyClose.removeAttribute("disabled");
-          else
-            keyClose.setAttribute("disabled", "true");
-
-          // We also want to remove the keyboard shortcut from the file menu
-          // when the shortcut is disabled, and bring it back when it's
-          // renabled.
-          //
-          // Fixing bug 630826 could make that happen automatically.
-          // Fixing bug 630830 could avoid the ugly hack below.
-
-          let closeMenuItem = document.getElementById("menu_close");
-          let parentPopup = closeMenuItem.parentNode;
-          let nextItem = closeMenuItem.nextSibling;
-          let clonedItem = closeMenuItem.cloneNode(true);
-
-          parentPopup.removeChild(closeMenuItem);
-
-          if (aEnabled)
-            clonedItem.setAttribute("key", "key_close");
-          else
-            clonedItem.removeAttribute("key");
-
-          parentPopup.insertBefore(clonedItem, nextItem);
-        ]]></body>
-      </method>
-
       <method name="pinTab">
         <parameter name="aTab"/>
         <body><![CDATA[
           if (aTab.pinned)
             return;
 
           if (aTab.hidden)
             this.showTab(aTab);
@@ -259,19 +223,16 @@
           this.moveTabTo(aTab, this._numPinnedTabs);
           aTab.setAttribute("pinned", "true");
           this.tabContainer._unlockTabSizing();
           this.tabContainer._positionPinnedTabs();
           this.tabContainer._updateCloseButtons();
 
           this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: true });
 
-          if (aTab.selected)
-            this._setCloseKeyState(false);
-
           let event = document.createEvent("Events");
           event.initEvent("TabPinned", true, false);
           aTab.dispatchEvent(event);
         ]]></body>
       </method>
 
       <method name="unpinTab">
         <parameter name="aTab"/>
@@ -283,19 +244,16 @@
           aTab.removeAttribute("pinned");
           aTab.style.marginInlineStart = "";
           this.tabContainer._unlockTabSizing();
           this.tabContainer._positionPinnedTabs();
           this.tabContainer._updateCloseButtons();
 
           this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: false });
 
-          if (aTab.selected)
-            this._setCloseKeyState(true);
-
           let event = document.createEvent("Events");
           event.initEvent("TabUnpinned", true, false);
           aTab.dispatchEvent(event);
         ]]></body>
       </method>
 
       <method name="previewTab">
         <parameter name="aTab"/>
@@ -1366,18 +1324,16 @@
               this.mIsBusy = false;
               this._callProgressListeners(null, "onStateChange",
                                           [webProgress, null,
                                            nsIWebProgressListener.STATE_STOP |
                                            nsIWebProgressListener.STATE_IS_NETWORK, 0],
                                           true, false);
             }
 
-            this._setCloseKeyState(!this.mCurrentTab.pinned);
-
             // TabSelect events are suppressed during preview mode to avoid confusing extensions and other bits of code
             // that might rely upon the other changes suppressed.
             // Focus is suppressed in the event that the main browser window is minimized - focusing a tab would restore the window
             if (!this._previewMode) {
               // We've selected the new tab, so go ahead and notify listeners.
               let event = new CustomEvent("TabSelect", {
                 bubbles: true,
                 cancelable: false,
@@ -4422,26 +4378,36 @@
 
             setTabState(tab, state) {
               this.setTabStateNoAction(tab, state);
 
               let browser = tab.linkedBrowser;
               let {tabParent} = browser.frameLoader;
               if (state == this.STATE_LOADING) {
                 this.assert(!this.minimizedOrFullyOccluded);
-                browser.docShellIsActive = true;
-                if (!tabParent) {
+
+                if (!this.tabbrowser.tabWarmingEnabled) {
+                  browser.docShellIsActive = true;
+                }
+
+                if (tabParent) {
+                  browser.renderLayers = true;
+                } else {
                   this.onLayersReady(browser);
                 }
               } else if (state == this.STATE_UNLOADING) {
                 this.unwarmTab(tab);
+                // Setting the docShell to be inactive will also cause it
+                // to stop rendering layers.
                 browser.docShellIsActive = false;
                 if (!tabParent) {
                   this.onLayersCleared(browser);
                 }
+              } else if (state == this.STATE_LOADED) {
+                this.maybeActivateDocShell(tab);
               }
 
               if (!tab.linkedBrowser.isRemoteBrowser) {
                 // setTabState is potentially re-entrant in the non-remote case,
                 // so we must re-get the state for this assertion.
                 let nonRemoteState = this.getTabState(tab);
                 // Non-remote tabs can never stay in the STATE_LOADING
                 // or STATE_UNLOADING states. By the time this function
@@ -4456,40 +4422,49 @@
             get minimizedOrFullyOccluded() {
               return window.windowState == window.STATE_MINIMIZED ||
                      window.isFullyOccluded;
             },
 
             init() {
               this.log("START");
 
-              // If we minimized the window before the switcher was activated,
-              // we might have set  the preserveLayers flag for the current
-              // browser. Let's clear it.
-              this.tabbrowser.mCurrentBrowser.preserveLayers(false);
-
               window.addEventListener("MozAfterPaint", this);
               window.addEventListener("MozLayerTreeReady", this);
               window.addEventListener("MozLayerTreeCleared", this);
               window.addEventListener("TabRemotenessChange", this);
               window.addEventListener("sizemodechange", this);
               window.addEventListener("occlusionstatechange", this);
               window.addEventListener("SwapDocShells", this, true);
               window.addEventListener("EndSwapDocShells", this, true);
 
-              let tab = this.requestedTab;
-              let browser = tab.linkedBrowser;
-              let tabIsLoaded = !browser.isRemoteBrowser ||
-                                browser.frameLoader.tabParent.hasPresented;
-
-              if (!this.minimizedOrFullyOccluded) {
-                this.log("Initial tab is loaded?: " + tabIsLoaded);
-                this.setTabState(tab, tabIsLoaded ? this.STATE_LOADED
-                                                  : this.STATE_LOADING);
-              }
+              let initialTab = this.requestedTab;
+
+              for (let tab of this.tabbrowser.tabs) {
+                if (tab == initialTab && this.minimizedOrFullyOccluded) {
+                  continue;
+                }
+
+                if (tab.linkedPanel) {
+                  let b = tab.linkedBrowser;
+                  let state;
+                  if (b.renderLayers && b.hasLayers) {
+                    state = this.STATE_LOADED;
+                  } else if (b.renderLayers && !b.hasLayers) {
+                    state = this.STATE_LOADING;
+                  } else if (!b.renderLayers && b.hasLayers) {
+                    state = this.STATE_UNLOADING;
+                  } else {
+                    state = this.STATE_UNLOADED;
+                  }
+
+                  this.setTabState(tab, state);
+                }
+              }
+              this.logState("Tab states initialized");
             },
 
             destroy() {
               if (this.unloadTimer) {
                 this.clearTimer(this.unloadTimer);
                 this.unloadTimer = null;
               }
               if (this.loadTimer) {
@@ -4658,16 +4633,18 @@
                        * the tab switch.
                        */
                       this.tabbrowser.addEventListener("select", () => {
                         this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
                       }, {once: true});
                     } else {
                       this.tabbrowser._adjustFocusAfterTabSwitch(showTab);
                     }
+
+                    this.maybeActivateDocShell(this.requestedTab);
                   }
                 }
 
                 // This doesn't necessarily exist if we're a new window and haven't switched tabs yet
                 if (this.lastVisibleTab)
                   this.lastVisibleTab._visuallySelected = false;
 
                 this.visibleTab._visuallySelected = true;
@@ -4696,16 +4673,39 @@
               // In that case we just overwrite it with a different tab; it's had its chance.
               this.loadingTab = this.requestedTab;
               this.log("Loading tab " + this.tinfo(this.loadingTab));
 
               this.loadTimer = this.setTimer(() => this.onLoadTimeout(), this.TAB_SWITCH_TIMEOUT);
               this.setTabState(this.requestedTab, this.STATE_LOADING);
             },
 
+            maybeActivateDocShell(tab) {
+              // If we've reached the point where the requested tab has entered
+              // the loaded state, but the DocShell is still not yet active, we
+              // should activate it.
+              let browser = tab.linkedBrowser;
+              let state = this.getTabState(tab);
+              let canCheckDocShellState = !browser.mDestroyed &&
+                                          (browser.docShell ||
+                                           browser.frameLoader.tabParent);
+              if (tab == this.requestedTab &&
+                  canCheckDocShellState &&
+                  state == this.STATE_LOADED &&
+                  !browser.docShellIsActive &&
+                  !this.minimizedOrFullyOccluded) {
+                browser.docShellIsActive = true;
+                this.logState("Set requested tab docshell to active and preserveLayers to false");
+                // If we minimized the window before the switcher was activated,
+                // we might have set the preserveLayers flag for the current
+                // browser. Let's clear it.
+                browser.preserveLayers(false);
+              }
+            },
+
             // This function runs before every event. It fixes up the state
             // to account for closed tabs.
             preActions() {
               this.assert(this.tabbrowser._switcher);
               this.assert(this.tabbrowser._switcher === this);
 
               for (let [tab, ] of this.tabState) {
                 if (!tab.linkedBrowser) {
@@ -4864,16 +4864,23 @@
               this.loadTimer = null;
               this.loadingTab = null;
               this.postActions();
             },
 
             // Fires when the layers become available for a tab.
             onLayersReady(browser) {
               let tab = this.tabbrowser.getTabForBrowser(browser);
+              if (!tab) {
+                // We probably got a layer update from a tab that got before
+                // the switcher was created, or for browser that's not being
+                // tracked by the async tab switcher (like the preloaded about:newtab).
+                return;
+              }
+
               this.logState(`onLayersReady(${tab._tPos}, ${browser.isRemoteBrowser})`);
 
               this.assert(this.getTabState(tab) == this.STATE_LOADING ||
                           this.getTabState(tab) == this.STATE_LOADED);
               this.setTabState(tab, this.STATE_LOADED);
 
               if (this.loadingTab === tab) {
                 this.clearTimer(this.loadTimer);
@@ -5009,17 +5016,22 @@
             shouldActivateDocShell(browser) {
               let tab = this.tabbrowser.getTabForBrowser(browser);
               let state = this.getTabState(tab);
               return state == this.STATE_LOADING || state == this.STATE_LOADED;
             },
 
             activateBrowserForPrintPreview(browser) {
               let tab = this.tabbrowser.getTabForBrowser(browser);
-              this.setTabState(tab, this.STATE_LOADING);
+              let state = this.getTabState(tab);
+              if (state != this.STATE_LOADING &&
+                  state != this.STATE_LOADED) {
+                this.setTabState(tab, this.STATE_LOADING);
+                this.logState("Activated browser " + this.tinfo(tab) + " for print preview");
+              }
             },
 
             canWarmTab(tab) {
               if (!this.tabbrowser.tabWarmingEnabled) {
                 return false;
               }
 
               // If the tab is not yet inserted, closing, not remote,
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -211,18 +211,16 @@ skip-if = toolkit != "cocoa" # Because o
 [browser_bug575830.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug577121.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug578534.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug579872.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_bug580638.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug580956.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug581242.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug581253.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_bug585785.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
@@ -377,18 +375,16 @@ support-files = feed_discovery.html
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_gZipOfflineChild.js]
 support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_page_style_menu.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_page_style_menu_update.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
-[browser_pinnedTabs.js]
-# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_plainTextLinks.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_printpreview.js]
 skip-if = os == 'win' # Bug 1384127
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_private_browsing_window.js]
 # DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
 [browser_private_no_prompt.js]
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -15,16 +15,19 @@ var gExceptionPaths = [
   "chrome://browser/locale/searchplugins/",
   "resource://app/defaults/blocklists/",
   "resource://app/defaults/pinning/",
   "resource://app/defaults/preferences/",
   "resource://gre/modules/commonjs/",
   "resource://gre/defaults/pref/",
   "resource://shield-recipe-client/node_modules/jexl/lib/",
 
+  // These resources are referenced using relative paths from html files.
+  "resource://payments/",
+
   // https://github.com/mozilla/normandy/issues/577
   "resource://shield-recipe-client/test/",
 
   // https://github.com/mozilla/activity-stream/issues/3053
   "resource://activity-stream/data/content/tippytop/images/",
   // https://github.com/mozilla/activity-stream/issues/3758
   "resource://activity-stream/prerendered/",
 
--- a/browser/base/content/test/tabs/browser.ini
+++ b/browser/base/content/test/tabs/browser.ini
@@ -15,14 +15,16 @@ skip-if = !e10s # Tab spinner is e10s on
 skip-if = os == 'mac'
 [browser_navigatePinnedTab.js]
 [browser_new_file_whitelisted_http_tab.js]
 skip-if = !e10s # Test only relevant for e10s.
 [browser_new_web_tab_in_file_process_pref.js]
 skip-if = !e10s # Pref and test only relevant for e10s.
 [browser_opened_file_tab_navigated_to_web.js]
 [browser_overflowScroll.js]
+[browser_pinnedTabs.js]
+[browser_pinnedTabs_closeByKeyboard.js]
 [browser_positional_attributes.js]
 [browser_preloadedBrowser_zoom.js]
 [browser_reload_deleted_file.js]
 [browser_tabswitch_updatecommands.js]
 [browser_viewsource_of_data_URI_in_file_process.js]
 [browser_open_newtab_start_observer_notification.js]
rename from browser/base/content/test/general/browser_pinnedTabs.js
rename to browser/base/content/test/tabs/browser_pinnedTabs.js
rename from browser/base/content/test/general/browser_bug580638.js
rename to browser/base/content/test/tabs/browser_pinnedTabs_closeByKeyboard.js
--- a/browser/base/content/test/general/browser_bug580638.js
+++ b/browser/base/content/test/tabs/browser_pinnedTabs_closeByKeyboard.js
@@ -5,55 +5,58 @@
 function test() {
   waitForExplicitFinish();
 
   function testState(aPinned) {
     function elemAttr(id, attr) {
       return document.getElementById(id).getAttribute(attr);
     }
 
-    if (aPinned) {
-      is(elemAttr("key_close", "disabled"), "true",
-         "key_close should be disabled when a pinned-tab is selected");
-      is(elemAttr("menu_close", "key"), "",
-         "menu_close shouldn't have a key set when a pinned is selected");
-    } else {
-      is(elemAttr("key_close", "disabled"), "",
-         "key_closed shouldn't have disabled state set when a non-pinned tab is selected");
-      is(elemAttr("menu_close", "key"), "key_close",
-         "menu_close should have key_close set as its key when a non-pinned tab is selected");
-    }
+    is(elemAttr("key_close", "disabled"), "",
+       "key_closed should always be enabled");
+    is(elemAttr("menu_close", "key"), "key_close",
+       "menu_close should always have key_close set");
   }
 
-  let lastSelectedTab = gBrowser.selectedTab;
-  ok(!lastSelectedTab.pinned, "We should have started with a regular tab selected");
+  let unpinnedTab = gBrowser.selectedTab;
+  ok(!unpinnedTab.pinned, "We should have started with a regular tab selected");
 
   testState(false);
 
-  let pinnedTab = BrowserTestUtils.addTab(gBrowser, "about:blank");
+  let pinnedTab = gBrowser.addTab();
   gBrowser.pinTab(pinnedTab);
 
   // Just pinning the tab shouldn't change the key state.
   testState(false);
 
-  // Test updating key state after selecting a tab.
+  // Test key state after selecting a tab.
   gBrowser.selectedTab = pinnedTab;
   testState(true);
 
-  gBrowser.selectedTab = lastSelectedTab;
+  gBrowser.selectedTab = unpinnedTab;
   testState(false);
 
   gBrowser.selectedTab = pinnedTab;
   testState(true);
 
-  // Test updating the key state after un/pinning the tab.
+  // Test the key state after un/pinning the tab.
   gBrowser.unpinTab(pinnedTab);
   testState(false);
 
   gBrowser.pinTab(pinnedTab);
   testState(true);
 
-  // Test updating the key state after removing the tab.
+  // Test that accel+w in a pinned tab selects the next tab.
+  let pinnedTab2 = gBrowser.addTab();
+  gBrowser.pinTab(pinnedTab2);
+  gBrowser.selectedTab = pinnedTab;
+
+  EventUtils.synthesizeKey("w", { accelKey: true });
+  is(gBrowser.tabs.length, 3, "accel+w in a pinned tab didn't close it");
+  is(gBrowser.selectedTab, unpinnedTab, "accel+w in a pinned tab selected the first unpinned tab");
+
+  // Test the key state after removing the tab.
   gBrowser.removeTab(pinnedTab);
+  gBrowser.removeTab(pinnedTab2);
   testState(false);
 
   finish();
 }
--- a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
@@ -1,14 +1,17 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 /* global gBrowser SessionStore */
 "use strict";
 
-let lazyTabState = {entries: [{url: "http://example.com/", title: "Example Domain"}]};
+const {Utils} = Cu.import("resource://gre/modules/sessionstore/Utils.jsm", {});
+const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
+
+let lazyTabState = {entries: [{url: "http://example.com/", triggeringPrincipal_base64, title: "Example Domain"}]};
 
 add_task(async function test_discarded() {
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
       "permissions": ["tabs"],
     },
 
     background: async function() {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
@@ -1,15 +1,18 @@
 "use strict";
 
+const {Utils} = Cu.import("resource://gre/modules/sessionstore/Utils.jsm", {});
+const triggeringPrincipal_base64 = Utils.SERIALIZED_SYSTEMPRINCIPAL;
+
 const SESSION = {
   windows: [{
     tabs: [
-      {entries: [{url: "about:blank"}]},
-      {entries: [{url: "https://example.com/"}]},
+      {entries: [{url: "about:blank", triggeringPrincipal_base64}]},
+      {entries: [{url: "https://example.com/", triggeringPrincipal_base64}]},
     ],
   }],
 };
 
 add_task(async function() {
   SessionStore.setBrowserState(JSON.stringify(SESSION));
   const tab = gBrowser.tabs[1];
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -36,16 +36,17 @@ XPCOMUtils.defineLazyModuleGetters(this,
   DirectoryLinksProvider: "resource:///modules/DirectoryLinksProvider.jsm",
   ExtensionsUI: "resource:///modules/ExtensionsUI.jsm",
   Feeds: "resource:///modules/Feeds.jsm",
   FileUtils: "resource://gre/modules/FileUtils.jsm",
   FileSource: "resource://gre/modules/L10nRegistry.jsm",
   FormValidationHandler: "resource:///modules/FormValidationHandler.jsm",
   Integration: "resource://gre/modules/Integration.jsm",
   L10nRegistry: "resource://gre/modules/L10nRegistry.jsm",
+  LanguagePrompt: "resource://gre/modules/LanguagePrompt.jsm",
   LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
   LoginHelper: "resource://gre/modules/LoginHelper.jsm",
   LoginManagerParent: "resource://gre/modules/LoginManagerParent.jsm",
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   NewTabUtils: "resource://gre/modules/NewTabUtils.jsm",
   OS: "resource://gre/modules/osfile.jsm",
   PageActions: "resource:///modules/PageActions.jsm",
   PageThumbs: "resource://gre/modules/PageThumbs.jsm",
@@ -1161,16 +1162,20 @@ BrowserGlue.prototype = {
       handlerService.asyncInit();
     });
 
     if (AppConstants.platform == "win") {
       Services.tm.idleDispatchToMainThread(() => {
         JawsScreenReaderVersionCheck.onWindowsRestored();
       });
     }
+
+    Services.tm.idleDispatchToMainThread(() => {
+      LanguagePrompt.init();
+    });
   },
 
   /**
    * Use this function as an entry point to schedule tasks that need
    * to run once per session, at any arbitrary point in time.
    * This function will be called from an idle observer. Check the value of
    * LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
    * observer.
--- a/browser/components/preferences/in-content/preferences.js
+++ b/browser/components/preferences/in-content/preferences.js
@@ -22,16 +22,18 @@ var Cr = Components.results;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSettingsStore",
                                   "resource://gre/modules/ExtensionSettingsStore.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "formAutofillParent",
+                                  "resource://formautofill/FormAutofillParent.jsm");
 
 var gLastHash = "";
 
 var gCategoryInits = new Map();
 function init_category_if_required(category) {
   let categoryInfo = gCategoryInits.get(category);
   if (!categoryInfo) {
     throw "Unknown in-content prefs category! Can't init " + category;
@@ -169,46 +171,41 @@ function gotoPref(aCategory) {
   // the categories 'select' event will re-enter the gotoPref codepath.
   gLastHash = category;
   if (item) {
     categories.selectedItem = item;
   } else {
     categories.clearSelection();
   }
   window.history.replaceState(category, document.title);
-  search(category, "data-category", subcategory, "data-subcategory");
+  search(category, "data-category");
 
   let mainContent = document.querySelector(".main-content");
   mainContent.scrollTop = 0;
 
+  spotlight(subcategory);
+
   Services.telemetry
           .getHistogramById("FX_PREFERENCES_CATEGORY_OPENED_V2")
           .add(telemetryBucketForCategory(friendlyName));
 }
 
-function search(aQuery, aAttribute, aSubquery, aSubAttribute) {
+function search(aQuery, aAttribute) {
   let mainPrefPane = document.getElementById("mainPrefPane");
   let elements = mainPrefPane.children;
   for (let element of elements) {
     // If the "data-hidden-from-search" is "true", the
     // element will not get considered during search. This
     // should only be used when an element is still under
     // development and should not be shown for any reason.
     if (element.getAttribute("data-hidden-from-search") != "true" ||
         element.getAttribute("data-subpanel") == "true") {
       let attributeValue = element.getAttribute(aAttribute);
       if (attributeValue == aQuery) {
-        if (!element.classList.contains("header") &&
-            element.localName !== "preferences" &&
-            aSubquery && aSubAttribute) {
-          let subAttributeValue = element.getAttribute(aSubAttribute);
-          element.hidden = subAttributeValue != aSubquery;
-        } else {
-          element.hidden = false;
-        }
+        element.hidden = false;
       } else {
         element.hidden = true;
       }
     }
     element.classList.remove("visually-hidden");
   }
 
   let keysets = mainPrefPane.getElementsByTagName("keyset");
@@ -216,16 +213,116 @@ function search(aQuery, aAttribute, aSub
     let attributeValue = element.getAttribute(aAttribute);
     if (attributeValue == aQuery)
       element.removeAttribute("disabled");
     else
       element.setAttribute("disabled", true);
   }
 }
 
+async function spotlight(subcategory) {
+  let highlightedElements = document.querySelectorAll(".spotlight");
+  if (highlightedElements.length) {
+    for (let element of highlightedElements) {
+      element.classList.remove("spotlight");
+    }
+  }
+  if (subcategory) {
+    if (!gSearchResultsPane.categoriesInitialized) {
+      await waitForSystemAddonInjectionsFinished([{
+        isGoingToInject: formAutofillParent.initialized,
+        elementId: "formAutofillGroup",
+      }]);
+    }
+    scrollAndHighlight(subcategory);
+  }
+
+  /**
+   * Wait for system addons finished their dom injections.
+   * @param {Array} addons - The system addon information array.
+   * For example, the element is looked like
+   * { isGoingToInject: true, elementId: "formAutofillGroup" }.
+   * The `isGoingToInject` means the system addon will be visible or not,
+   * and the `elementId` means the id of the element will be injected into the dom
+   * if the `isGoingToInject` is true.
+   * @returns {Promise} Will resolve once all injections are finished.
+   */
+  function waitForSystemAddonInjectionsFinished(addons) {
+    return new Promise(resolve => {
+      let elementIdSet = new Set();
+      for (let addon of addons) {
+        if (addon.isGoingToInject) {
+          elementIdSet.add(addon.elementId);
+        }
+      }
+      if (elementIdSet.size) {
+        let observer = new MutationObserver(mutations => {
+          for (let mutation of mutations) {
+            for (let node of mutation.addedNodes) {
+              elementIdSet.delete(node.id);
+              if (elementIdSet.size === 0) {
+                observer.disconnect();
+                resolve();
+              }
+            }
+          }
+        });
+        let mainContent = document.querySelector(".main-content");
+        observer.observe(mainContent, {childList: true, subtree: true});
+        // Disconnect the mutation observer once there is any user input.
+        mainContent.addEventListener("scroll", disconnectMutationObserver);
+        window.addEventListener("mousedown", disconnectMutationObserver);
+        window.addEventListener("keydown", disconnectMutationObserver);
+        function disconnectMutationObserver() {
+          mainContent.removeEventListener("scroll", disconnectMutationObserver);
+          window.removeEventListener("mousedown", disconnectMutationObserver);
+          window.removeEventListener("keydown", disconnectMutationObserver);
+          observer.disconnect();
+        }
+      } else {
+        resolve();
+      }
+    });
+  }
+}
+
+function scrollAndHighlight(subcategory) {
+  let element = document.querySelector(`[data-subcategory="${subcategory}"]`);
+  if (element) {
+    let header = getClosestDisplayedHeader(element);
+    scrollContentTo(header);
+    element.classList.add("spotlight");
+  }
+}
+
+/**
+ * If there is no visible second level header it will return first level header,
+ * otherwise return second level header.
+ * @returns {Element} - The closest displayed header.
+ */
+function getClosestDisplayedHeader(element) {
+  let header = element.closest("groupbox");
+  let searchHeader = header.querySelector("caption.search-header");
+  if (searchHeader && searchHeader.hidden &&
+      header.previousSibling.classList.contains("subcategory")) {
+    header = header.previousSibling;
+  }
+  return header;
+}
+
+function scrollContentTo(element) {
+  const SEARCH_CONTAINER_HEIGHT = document.querySelector(".search-container").clientHeight;
+  let mainContent = document.querySelector(".main-content");
+  let top = element.getBoundingClientRect().top - SEARCH_CONTAINER_HEIGHT;
+  mainContent.scroll({
+    top,
+    behavior: "smooth",
+  });
+}
+
 function helpButtonCommand() {
   let pane = history.state;
   let categories = document.getElementById("categories");
   let helpTopic = categories.querySelector(".category[value=" + pane + "]")
                             .getAttribute("helpTopic");
   openHelpLink(helpTopic);
 }
 
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -702,52 +702,52 @@
               value="&a11yPrivacy.learnmore.label;"></label>
     </hbox>
   </vbox>
 </groupbox>
 
 <hbox id="dataCollectionCategory"
       class="subcategory"
       hidden="true"
-      data-category="panePrivacy"
-      data-subcategory="reports">
+      data-category="panePrivacy">
   <label class="header-name" flex="1">&dataCollection.label;</label>
 </hbox>
 
 <!-- Firefox Data Collection and Use -->
 #ifdef MOZ_DATA_REPORTING
-<groupbox id="dataCollectionGroup" data-category="panePrivacy" data-subcategory="reports" hidden="true">
+<groupbox id="dataCollectionGroup" data-category="panePrivacy" hidden="true">
   <caption class="search-header" hidden="true"><label>&dataCollection.label;</label></caption>
 
-  <vbox>
-    <description>
-      <label class="tail-with-learn-more">&dataCollectionDesc.label;</label><label id="dataCollectionPrivacyNotice" class="learnMore text-link">&dataCollectionPrivacyNotice.label;</label>
-    </description>
+  <description>
+    <label class="tail-with-learn-more">&dataCollectionDesc.label;</label><label id="dataCollectionPrivacyNotice" class="learnMore text-link">&dataCollectionPrivacyNotice.label;</label>
+  </description>
+  <vbox data-subcategory="reports">
     <description flex="1">
       <checkbox id="submitHealthReportBox" label="&enableHealthReport2.label;"
                 class="tail-with-learn-more"
                 accesskey="&enableHealthReport2.accesskey;"/>
       <label id="FHRLearnMore"
              class="learnMore text-link">&healthReportLearnMore.label;</label>
     </description>
 #ifndef MOZ_TELEMETRY_REPORTING
     <description id="TelemetryDisabledDesc" class="indent tip-caption" control="telemetryGroup">&healthReportingDisabled.label;</description>
 #endif
-  </vbox>
+
 #ifdef MOZ_CRASHREPORTER
-  <hbox align="center">
-    <checkbox id="automaticallySubmitCrashesBox"
-              class="tail-with-learn-more"
-              preference="browser.crashReports.unsubmittedCheck.autoSubmit"
-              label="&alwaysSubmitCrashReports1.label;"
-              accesskey="&alwaysSubmitCrashReports1.accesskey;"/>
-    <label id="crashReporterLearnMore"
-           class="learnMore text-link">&crashReporterLearnMore.label;</label>
-  </hbox>
+    <hbox align="center">
+      <checkbox id="automaticallySubmitCrashesBox"
+                class="tail-with-learn-more"
+                preference="browser.crashReports.unsubmittedCheck.autoSubmit"
+                label="&alwaysSubmitCrashReports1.label;"
+                accesskey="&alwaysSubmitCrashReports1.accesskey;"/>
+      <label id="crashReporterLearnMore"
+             class="learnMore text-link">&crashReporterLearnMore.label;</label>
+    </hbox>
 #endif
+  </vbox>
 </groupbox>
 #endif
 
 <hbox id="securityCategory"
       class="subcategory"
       hidden="true"
       data-category="panePrivacy">
   <label class="header-name" flex="1">&security.label;</label>
--- a/browser/components/preferences/in-content/tests/browser.ini
+++ b/browser/components/preferences/in-content/tests/browser.ini
@@ -68,16 +68,17 @@ skip-if = e10s
 [browser_privacypane_8.js]
 [browser_sanitizeOnShutdown_prefLocked.js]
 [browser_searchsuggestions.js]
 [browser_security-1.js]
 [browser_security-2.js]
 [browser_siteData.js]
 [browser_siteData2.js]
 [browser_siteData3.js]
+[browser_spotlight.js]
 [browser_site_login_exceptions.js]
 [browser_permissions_dialog.js]
 [browser_cookies_dialog.js]
 [browser_subdialogs.js]
 support-files =
   subdialog.xul
   subdialog2.xul
 [browser_telemetry.js]
--- a/browser/components/preferences/in-content/tests/browser_advanced_update.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_update.js
@@ -51,33 +51,30 @@ const mockUpdateManager = {
     return this._updates[index];
   },
 
   _updates: [
     {
       name: "Firefox Developer Edition 49.0a2",
       statusText: "The Update was successfully installed",
       buildID: "20160728004010",
-      type: "minor",
       installDate: 1469763105156,
       detailsURL: "https://www.mozilla.org/firefox/aurora/"
     },
     {
       name: "Firefox Developer Edition 43.0a2",
       statusText: "The Update was successfully installed",
       buildID: "20150929004011",
-      type: "minor",
       installDate: 1443585886224,
       detailsURL: "https://www.mozilla.org/firefox/aurora/"
     },
     {
       name: "Firefox Developer Edition 42.0a2",
       statusText: "The Update was successfully installed",
       buildID: "20150920004018",
-      type: "major",
       installDate: 1442818147544,
       detailsURL: "https://www.mozilla.org/firefox/aurora/"
     }
   ]
 };
 
 function resetPreferences() {
   Services.prefs.clearUserPref("browser.search.update");
@@ -141,17 +138,16 @@ add_task(async function() {
   // Test the updates are displayed correctly
   let update = null;
   let updateData = null;
   for (let i = 0; i < updates.length; ++i) {
     update = updates[i];
     updateData = mockUpdateManager.getUpdateAt(i);
 
     is(update.name, updateData.name + " (" + updateData.buildID + ")", "Wrong update name");
-    is(update.type, updateData.type == "major" ? "New Version" : "Security Update", "Wrong update type");
     is(update.installDate, formatInstallDate(updateData.installDate), "Wrong update installDate");
     is(update.detailsURL, updateData.detailsURL, "Wrong update detailsURL");
     is(update.status, updateData.statusText, "Wrong update status");
   }
 
   // Test the dialog window closes
   let closeBtn = dialogOverlay.querySelector(".dialogClose");
   closeBtn.doCommand();
--- a/browser/components/preferences/in-content/tests/browser_bug1020245_openPreferences_to_paneContent.js
+++ b/browser/components/preferences/in-content/tests/browser_bug1020245_openPreferences_to_paneContent.js
@@ -20,17 +20,18 @@ add_task(async function() {
   prefs = await openPreferencesViaHash("nonexistant-category");
   is(prefs.selectedPane, "paneGeneral", "General pane is selected when hash is a nonexistant-category");
   prefs = await openPreferencesViaHash();
   is(prefs.selectedPane, "paneGeneral", "General pane is selected by default");
   prefs = await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
   is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
   let doc = gBrowser.contentDocument;
   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
-  ok(doc.querySelector("#locationBarGroup").hidden, "Location Bar prefs should be hidden when only Reports are requested");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"), "Wait for the reports section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "reports", "The reports section is spotlighted.");
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 // Test opening Preferences with subcategory on an existing Preferences tab. See bug 1358475.
 add_task(async function() {
   let prefs = await openPreferencesViaOpenPreferencesAPI("general", {leaveOpen: true});
   is(prefs.selectedPane, "paneGeneral", "General pane is selected by default");
   let doc = gBrowser.contentDocument;
@@ -38,17 +39,18 @@ add_task(async function() {
   // The reasons that here just call the `openPreferences` API without the helping function are
   //   - already opened one about:preferences tab up there and
   //   - the goal is to test on the existing tab and
   //   - using `openPreferencesViaOpenPreferencesAPI` would introduce more handling of additional about:blank and unneccessary event
   openPreferences("privacy-reports");
   let selectedPane = gBrowser.contentWindow.history.state;
   is(selectedPane, "panePrivacy", "Privacy pane should be selected");
   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
-  ok(doc.querySelector("#locationBarGroup").hidden, "Location Bar prefs should be hidden when only Reports are requested");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"), "Wait for the reports section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "reports", "The reports section is spotlighted.");
   await BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 // Test opening to a subcategory displays the correct values for preferences
 add_task(async function() {
   // Skip if crash reporting isn't enabled since the checkbox will be missing.
   if (!AppConstants.MOZ_CRASHREPORTER) {
     return;
new file mode 100644
--- /dev/null
+++ b/browser/components/preferences/in-content/tests/browser_spotlight.js
@@ -0,0 +1,37 @@
+/* eslint-disable mozilla/no-cpows-in-tests */
+
+add_task(async function test_reports_section() {
+  let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
+  is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
+  let doc = gBrowser.contentDocument;
+  is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
+    "Wait for the reports section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "reports",
+    "The reports section is spotlighted.");
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(async function test_address_autofill_section() {
+  let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-address-autofill", {leaveOpen: true});
+  is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
+  let doc = gBrowser.contentDocument;
+  is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
+    "Wait for the ddress-autofill section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "address-autofill",
+    "The ddress-autofill section is spotlighted.");
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(async function test_credit_card_autofill_section() {
+  let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-credit-card-autofill", {leaveOpen: true});
+  is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
+  let doc = gBrowser.contentDocument;
+  is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
+    "Wait for the credit-card-autofill section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "credit-card-autofill",
+    "The credit-card-autofill section is spotlighted.");
+  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/browser/components/preferences/languages.js
+++ b/browser/components/preferences/languages.js
@@ -1,13 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /* 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/. */
 
+Components.utils.import("resource://gre/modules/Services.jsm");
+
 var gLanguagesDialog = {
 
   _availableLanguagesList: [],
   _acceptLanguages: { },
 
   _selectedItemID: null,
 
   init() {
@@ -144,26 +146,32 @@ var gLanguagesDialog = {
       this._acceptLanguages[languages[i]] = true;
     }
 
     if (this._activeLanguages.childNodes.length > 0) {
       this._activeLanguages.ensureIndexIsVisible(selectedIndex);
       this._activeLanguages.selectedIndex = selectedIndex;
     }
 
+    // Update states of accept-language list and buttons according to
+    // privacy.resistFingerprinting and privacy.spoof_english.
+    this.readSpoofEnglish();
+
     return undefined;
   },
 
   writeAcceptLanguages() {
     return undefined;
   },
 
   onAvailableLanguageSelect() {
+    var availableLanguages = this._availableLanguages;
     var addButton = document.getElementById("addButton");
-    addButton.disabled = false;
+    addButton.disabled = availableLanguages.disabled ||
+                         availableLanguages.selectedIndex < 0;
 
     this._availableLanguages.removeAttribute("accesskey");
   },
 
   addLanguage() {
     var selectedID = this._availableLanguages.selectedItem.id;
     var preference = document.getElementById("intl.accept_languages");
     var arrayOfPrefs = preference.value.toLowerCase().split(/\s*,\s*/);
@@ -286,11 +294,45 @@ var gLanguagesDialog = {
       downButton.disabled = this._activeLanguages.selectedIndex == this._activeLanguages.childNodes.length - 1;
       removeButton.disabled = false;
       break;
     default:
       upButton.disabled = true;
       downButton.disabled = true;
       removeButton.disabled = false;
     }
+  },
+
+  readSpoofEnglish() {
+    var checkbox = document.getElementById("spoofEnglish");
+    var resistFingerprinting = Services.prefs.getBoolPref("privacy.resistFingerprinting");
+    if (!resistFingerprinting) {
+      checkbox.hidden = true;
+      return false;
+    }
+
+    var spoofEnglish = document.getElementById("privacy.spoof_english").value;
+    var activeLanguages = this._activeLanguages;
+    var availableLanguages = this._availableLanguages;
+    checkbox.hidden = false;
+    switch (spoofEnglish) {
+    case 1: // don't spoof intl.accept_lanauges
+      activeLanguages.disabled = false;
+      activeLanguages.selectItem(activeLanguages.firstChild);
+      availableLanguages.disabled = false;
+      this.onAvailableLanguageSelect();
+      return false;
+    case 2: // spoof intl.accept_lanauges
+      activeLanguages.clearSelection();
+      activeLanguages.disabled = true;
+      availableLanguages.disabled = true;
+      this.onAvailableLanguageSelect();
+      return true;
+    default: // will prompt for spoofing intl.accept_lanauges if resisting fingerprinting
+      return false;
+    }
+  },
+
+  writeSpoofEnglish() {
+    return document.getElementById("spoofEnglish").checked ? 2 : 1;
   }
 };
 
--- a/browser/components/preferences/languages.xul
+++ b/browser/components/preferences/languages.xul
@@ -35,28 +35,36 @@
                   name="pref.browser.language.disable_button.up"
                   type="bool"/>
       <preference id="pref.browser.language.disable_button.down"
                   name="pref.browser.language.disable_button.down"
                   type="bool"/>
       <preference id="pref.browser.language.disable_button.remove"
                   name="pref.browser.language.disable_button.remove"
                   type="bool"/>
+      <preference id="privacy.spoof_english"
+                  name="privacy.spoof_english"
+                  type="int"/>
     </preferences>
 
     <script type="application/javascript" src="chrome://browser/content/preferences/languages.js"/>
 
     <stringbundleset id="languageSet">
       <stringbundle id="bundleRegions"      src="chrome://global/locale/regionNames.properties"/>
       <stringbundle id="bundleLanguages"    src="chrome://global/locale/languageNames.properties"/>
       <stringbundle id="bundlePreferences"  src="chrome://browser/locale/preferences/preferences.properties"/>
       <stringbundle id="bundleAccepted"     src="resource://gre/res/language.properties"/>
     </stringbundleset>
 
     <description>&languages.customize2.description;</description>
+    <checkbox id="spoofEnglish"
+              label="&languages.customize.spoofEnglish;"
+              preference="privacy.spoof_english"
+              onsyncfrompreference="return gLanguagesDialog.readSpoofEnglish();"
+              onsynctopreference="return gLanguagesDialog.writeSpoofEnglish();"/>
     <grid flex="1">
       <columns>
         <column flex="1"/>
         <column/>
       </columns>
       <rows>
         <row flex="1">
           <listbox id="activeLanguages" flex="1" rows="6"
--- a/browser/components/sessionstore/test/browser_906076_lazy_tabs.js
+++ b/browser/components/sessionstore/test/browser_906076_lazy_tabs.js
@@ -1,32 +1,32 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const TEST_STATE = {
   windows: [{
     tabs: [
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
-      { entries: [{ url: "http://example.com" }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
+      { entries: [{ url: "http://example.com", triggeringPrincipal_base64 }] },
     ]
   }]
 };
 
 const TEST_STATE_2 = {
   windows: [{
     tabs: [
-      { entries: [{ url: "about:robots" }]
+      { entries: [{ url: "about:robots", triggeringPrincipal_base64 }]
       },
       { entries: [],
         userTypedValue: "http://example.com",
         userTypedClear: 1
       }
     ]
   }]
 };
--- a/browser/components/sessionstore/test/browser_tab_label_during_restore.js
+++ b/browser/components/sessionstore/test/browser_tab_label_during_restore.js
@@ -35,20 +35,20 @@ add_task(async function() {
     };
   }
 
   info("setting test browser state");
   let browserLoadedPromise = BrowserTestUtils.firstBrowserLoaded(window, false);
   await promiseBrowserState({
     windows: [{
       tabs: [
-        { entries: [{ url: REMOTE_URL }] },
-        { entries: [{ url: ABOUT_ROBOTS_URI }] },
-        { entries: [{ url: REMOTE_URL }] },
-        { entries: [{ url: NO_TITLE_URL }] },
+        { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+        { entries: [{ url: ABOUT_ROBOTS_URI, triggeringPrincipal_base64 }] },
+        { entries: [{ url: REMOTE_URL, triggeringPrincipal_base64 }] },
+        { entries: [{ url: NO_TITLE_URL, triggeringPrincipal_base64 }] },
       ]
     }]
   });
   let [tab1, tab2, tab3, tab4] = gBrowser.tabs;
   is(gBrowser.selectedTab, tab1, "first tab is selected");
 
   await browserLoadedPromise;
   const REMOTE_TITLE = tab1.linkedBrowser.contentTitle;
--- a/browser/components/uitour/test/browser_openPreferences.js
+++ b/browser/components/uitour/test/browser_openPreferences.js
@@ -40,13 +40,14 @@ add_UITour_task(async function test_open
       !(AppConstants.MOZ_DATA_REPORTING && AppConstants.MOZ_CRASHREPORTER)) {
     return;
   }
   let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences#privacy-reports");
   await gContentAPI.openPreferences("privacy-reports");
   let tab = await promiseTabOpened;
   await BrowserTestUtils.waitForEvent(gBrowser.selectedBrowser, "Initialized");
   let doc = gBrowser.selectedBrowser.contentDocument;
-  let reports = doc.querySelector("groupbox[data-subcategory='reports']");
   is(doc.location.hash, "#privacy", "Should not display the reports subcategory in the location hash.");
-  is(reports.hidden, false, "Should open to the reports subcategory in the privacy pane in the new Preferences.");
+  await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
+    "Wait for the reports section is spotlighted.");
+  is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "reports", "The reports section is spotlighted.");
   await BrowserTestUtils.removeTab(tab);
 });
--- a/browser/extensions/asan-reporter/bootstrap.js
+++ b/browser/extensions/asan-reporter/bootstrap.js
@@ -26,22 +26,47 @@ const PREF_LOG_LEVEL = "asanreporter.log
 
 // Setup logging
 const LOGGER_NAME = "extensions.asanreporter";
 let logger = Log.repository.getLogger(LOGGER_NAME);
 logger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
 logger.addAppender(new Log.DumpAppender(new Log.BasicFormatter()));
 logger.level = Preferences.get(PREF_LOG_LEVEL, Log.Level.Info);
 
+this.TabCrashObserver = {
+  init() {
+    if (this.initialized)
+      return;
+    this.initialized = true;
+
+    Services.obs.addObserver(this, "ipc:content-shutdown");
+  },
+
+  observe(aSubject, aTopic, aData) {
+    if (aTopic == "ipc:content-shutdown") {
+        aSubject.QueryInterface(Ci.nsIPropertyBag2);
+        if (!aSubject.get("abnormal")) {
+          return;
+        }
+        processDirectory("/tmp");
+    }
+  },
+};
+
 function install(aData, aReason) {}
 
 function uninstall(aData, aReason) {}
 
 function startup(aData, aReason) {
   logger.info("Starting up...");
+
+  // Install a handler to observe tab crashes, so we can report those right
+  // after they happen instead of relying on the user to restart the browser.
+  TabCrashObserver.init();
+
   // We could use OS.Constants.Path.tmpDir here, but unfortunately there is
   // no way in C++ to get the same value *prior* to xpcom initialization.
   // Since ASan needs its options, including the "log_path" option already
   // at early startup, there is no way to pass this on to ASan.
   //
   // Instead, we hardcode the /tmp directory here, which should be fine in
   // most cases, as long as we are on Linux and Mac (the main targets for
   // this addon at the time of writing).
--- a/browser/extensions/asan-reporter/clone_asan_reporter.sh
+++ b/browser/extensions/asan-reporter/clone_asan_reporter.sh
@@ -1,11 +1,11 @@
 #!/bin/sh
 
 mkdir tmp/
 git clone --no-checkout --depth 1 https://github.com/choller/firefox-asan-reporter tmp/
-(cd tmp && git reset --hard e1a5d7dc5a2706af72bac0f11eab34b3afdb48ba)
+(cd tmp && git reset --hard d508c6e3f5df752a9a7a2d6f1e4e7261ec2290e7)
 
 # Copy only whitelisted files
 cp tmp/bootstrap.js tmp/install.rdf.in tmp/moz.build tmp/README.md tmp/LICENSE .
 
 # Remove the temporary directory
 rm -Rf tmp/
--- a/browser/extensions/formautofill/FormAutofillContent.jsm
+++ b/browser/extensions/formautofill/FormAutofillContent.jsm
@@ -411,17 +411,17 @@ var FormAutofillContent = {
 
     let handler = this._formsDetails.get(formElement);
     if (!handler) {
       this.log.debug("Form element could not map to an existing handler");
       return true;
     }
 
     let records = handler.createRecords();
-    if (!Object.keys(records).length) {
+    if (!Object.values(records).some(typeRecords => typeRecords.length)) {
       return true;
     }
 
     this._onFormSubmit(records, domWin, handler.timeStartedFillingMS);
     return true;
   },
 
   receiveMessage({name, data}) {
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -981,17 +981,32 @@ class FormAutofillHandler {
           }
           input.removeEventListener("input", this);
         }
         this.timeStartedFillingMS = Date.now();
         break;
     }
   }
 
+  /**
+   * Collect the filled sections within submitted form and convert all the valid
+   * field data into multiple records.
+   *
+   * @returns {Object} records
+   *          {Array.<Object>} records.address
+   *          {Array.<Object>} records.creditCard
+   */
   createRecords() {
-    // TODO [Bug 1415073] `FormAutofillHandler.createRecords` should traverse
-    // all sections and aggregate the records into one result.
-    if (this.sections.length > 0) {
-      return this.sections[0].createRecords();
+    const records = {
+      address: [],
+      creditCard: [],
+    };
+
+    for (const section of this.sections) {
+      const secRecords = section.createRecords();
+      for (const [type, record] of Object.entries(secRecords)) {
+        records[type].push(record);
+      }
     }
-    return null;
+    log.debug("Create records:", records);
+    return records;
   }
 }
--- a/browser/extensions/formautofill/FormAutofillHeuristics.jsm
+++ b/browser/extensions/formautofill/FormAutofillHeuristics.jsm
@@ -16,35 +16,38 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
 this.log = null;
 FormAutofillUtils.defineLazyLogGetter(this, this.EXPORTED_SYMBOLS[0]);
 
 const PREF_HEURISTICS_ENABLED = "extensions.formautofill.heuristics.enabled";
 const PREF_SECTION_ENABLED = "extensions.formautofill.section.enabled";
+const DEFAULT_SECTION_NAME = "-moz-section-default";
 
 /**
  * A scanner for traversing all elements in a form and retrieving the field
  * detail with FormAutofillHeuristics.getInfo function. It also provides a
  * cursor (parsingIndex) to indicate which element is waiting for parsing.
  */
 class FieldScanner {
   /**
    * Create a FieldScanner based on form elements with the existing
    * fieldDetails.
    *
    * @param {Array.DOMElement} elements
    *        The elements from a form for each parser.
    */
-  constructor(elements) {
+  constructor(elements, {allowDuplicates = false, sectionEnabled = true}) {
     this._elementsWeakRef = Cu.getWeakReference(elements);
     this.fieldDetails = [];
     this._parsingIndex = 0;
     this._sections = [];
+    this._allowDuplicates = allowDuplicates;
+    this._sectionEnabled = sectionEnabled;
   }
 
   get _elements() {
     return this._elementsWeakRef.get();
   }
 
   /**
    * This cursor means the index of the element which is waiting for parsing.
@@ -107,32 +110,71 @@ class FieldScanner {
       }
     }
     this._sections.push({
       name,
       fieldDetails: [fieldDetail],
     });
   }
 
-  getSectionFieldDetails(allowDuplicates) {
-    // TODO: [Bug 1416664] If there is only one section which is not defined by
-    // `autocomplete` attribute, the sections should be classified by the
-    // heuristics.
-    return this._sections.map(section => {
-      if (allowDuplicates) {
-        return section.fieldDetails;
+  _classifySections() {
+    let fieldDetails = this._sections[0].fieldDetails;
+    this._sections = [];
+    let seenTypes = new Set();
+    let previousType;
+    let sectionCount = 0;
+
+    for (let fieldDetail of fieldDetails) {
+      if (!fieldDetail.fieldName) {
+        continue;
+      }
+      if (seenTypes.has(fieldDetail.fieldName) &&
+          previousType != fieldDetail.fieldName) {
+        seenTypes.clear();
+        sectionCount++;
       }
-      return this._trimFieldDetails(section.fieldDetails);
-    });
+      previousType = fieldDetail.fieldName;
+      seenTypes.add(fieldDetail.fieldName);
+      this._pushToSection(DEFAULT_SECTION_NAME + "-" + sectionCount, fieldDetail);
+    }
+  }
+
+  /**
+   * The result is an array contains the sections with its belonging field
+   * details. If `this._sections` contains one section only with the default
+   * section name (DEFAULT_SECTION_NAME), `this._classifySections` should be
+   * able to identify all sections in the heuristic way.
+   *
+   * @returns {Array<Object>}
+   *          The array with the sections, and the belonging fieldDetails are in
+   *          each section.
+   */
+  getSectionFieldDetails() {
+    // When the section feature is disabled, `getSectionFieldDetails` should
+    // provide a single section result.
+    if (!this._sectionEnabled) {
+      return [this._getFinalDetails(this.fieldDetails)];
+    }
+    if (this._sections.length == 0) {
+      return [];
+    }
+    if (this._sections.length == 1 && this._sections[0].name == DEFAULT_SECTION_NAME) {
+      this._classifySections();
+    }
+
+    return this._sections.map(section =>
+      this._getFinalDetails(section.fieldDetails)
+    );
   }
 
   /**
    * This function will prepare an autocomplete info object with getInfo
-   * function and push the detail to fieldDetails property. Any duplicated
-   * detail will be marked as _duplicated = true for the parser.
+   * function and push the detail to fieldDetails property.
+   * Any field will be pushed into `this._sections` based on the section name
+   * in `autocomplete` attribute.
    *
    * Any element without the related detail will be used for adding the detail
    * to the end of field details.
    */
   pushDetail() {
     let elementIndex = this.fieldDetails.length;
     if (elementIndex >= this._elements.length) {
       throw new Error("Try to push the non-existing element info.");
@@ -149,81 +191,79 @@ class FieldScanner {
       fieldName: info.fieldName,
       elementWeakRef: Cu.getWeakReference(element),
     };
 
     if (info._reason) {
       fieldInfo._reason = info._reason;
     }
 
-    // Store the association between the field metadata and the element.
-    if (this.findSameField(info) != -1) {
-      // A field with the same identifier already exists.
-      log.debug("Not collecting a field matching another with the same info:", info);
-      fieldInfo._duplicated = true;
-    }
-
     this.fieldDetails.push(fieldInfo);
     this._pushToSection(this._getSectionName(fieldInfo), fieldInfo);
   }
 
   _getSectionName(info) {
     let names = [];
     if (info.section) {
       names.push(info.section);
     }
     if (info.addressType) {
       names.push(info.addressType);
     }
-    return names.length ? names.join(" ") : "-moz-section-default";
+    return names.length ? names.join(" ") : DEFAULT_SECTION_NAME;
   }
 
   /**
    * When a field detail should be changed its fieldName after parsing, use
    * this function to update the fieldName which is at a specific index.
    *
    * @param {number} index
    *        The index indicates a field detail to be updated.
    * @param {string} fieldName
    *        The new fieldName
    */
   updateFieldName(index, fieldName) {
     if (index >= this.fieldDetails.length) {
       throw new Error("Try to update the non-existing field detail.");
     }
     this.fieldDetails[index].fieldName = fieldName;
-
-    delete this.fieldDetails[index]._duplicated;
-    let indexSame = this.findSameField(this.fieldDetails[index]);
-    if (indexSame != index && indexSame != -1) {
-      this.fieldDetails[index]._duplicated = true;
-    }
   }
 
-  findSameField(info) {
-    return this.fieldDetails.findIndex(f => f.section == info.section &&
-                                       f.addressType == info.addressType &&
-                                       f.fieldName == info.fieldName);
+  _isSameField(field1, field2) {
+    return field1.section == field2.section &&
+           field1.addressType == field2.addressType &&
+           field1.fieldName == field2.fieldName;
   }
 
   /**
-   * Provide the field details without invalid field name and duplicated fields.
+   * Provide the final field details without invalid field name, and the
+   * duplicated fields will be removed as well. For the debugging purpose,
+   * the final `fieldDetails` will include the duplicated fields if
+   * `_allowDuplicates` is true.
    *
    * @param   {Array<Object>} fieldDetails
    *          The field details for trimming.
    * @returns {Array<Object>}
    *          The array with the field details without invalid field name and
    *          duplicated fields.
    */
-  _trimFieldDetails(fieldDetails) {
-    return fieldDetails.filter(f => f.fieldName && !f._duplicated);
-  }
+  _getFinalDetails(fieldDetails) {
+    if (this._allowDuplicates) {
+      return fieldDetails.filter(f => f.fieldName);
+    }
 
-  getFieldDetails(allowDuplicates) {
-    return allowDuplicates ? this.fieldDetails : this._trimFieldDetails(this.fieldDetails);
+    let dedupedFieldDetails = [];
+    for (let fieldDetail of fieldDetails) {
+      if (fieldDetail.fieldName && !dedupedFieldDetails.find(f => this._isSameField(fieldDetail, f))) {
+        dedupedFieldDetails.push(fieldDetail);
+      } else {
+        log.debug("Not collecting an invalid field or matching another with the same info:", fieldDetail);
+      }
+    }
+    return dedupedFieldDetails;
   }
 
   elementExisting(index) {
     return index < this._elements.length;
   }
 }
 
 this.LabelUtils = {
@@ -646,38 +686,33 @@ this.FormAutofillHeuristics = {
   getFormInfo(form, allowDuplicates = false) {
     const eligibleFields = Array.from(form.elements)
       .filter(elem => FormAutofillUtils.isFieldEligibleForAutofill(elem));
 
     if (eligibleFields.length <= 0) {
       return [];
     }
 
-    let fieldScanner = new FieldScanner(eligibleFields);
+    let fieldScanner = new FieldScanner(eligibleFields,
+      {allowDuplicates, sectionEnabled: this._sectionEnabled});
     while (!fieldScanner.parsingFinished) {
       let parsedPhoneFields = this._parsePhoneFields(fieldScanner);
       let parsedAddressFields = this._parseAddressFields(fieldScanner);
       let parsedExpirationDateFields = this._parseCreditCardExpirationDateFields(fieldScanner);
 
       // If there is no any field parsed, the parsing cursor can be moved
       // forward to the next one.
       if (!parsedPhoneFields && !parsedAddressFields && !parsedExpirationDateFields) {
         fieldScanner.parsingIndex++;
       }
     }
 
     LabelUtils.clearLabelMap();
 
-    if (!this._sectionEnabled) {
-      // When the section feature is disabled, `getFormInfo` should provide a
-      // single section result.
-      return [fieldScanner.getFieldDetails(allowDuplicates)];
-    }
-
-    return fieldScanner.getSectionFieldDetails(allowDuplicates);
+    return fieldScanner.getSectionFieldDetails();
   },
 
   _regExpTableHashValue(...signBits) {
     return signBits.reduce((p, c, i) => p | !!c << i, 0);
   },
 
   _setRegExpListCache(regexps, b0, b1, b2) {
     if (!this._regexpList) {
--- a/browser/extensions/formautofill/FormAutofillParent.jsm
+++ b/browser/extensions/formautofill/FormAutofillParent.jsm
@@ -22,17 +22,19 @@
  *   {
  *     // ...
  *   }
  * ]
  */
 
 "use strict";
 
-this.EXPORTED_SYMBOLS = ["FormAutofillParent"];
+// We expose a singleton from this module. Some tests may import the
+// constructor via a backstage pass.
+this.EXPORTED_SYMBOLS = ["formAutofillParent"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 Cu.import("resource://formautofill/FormAutofillUtils.jsm");
 
@@ -74,19 +76,39 @@ FormAutofillParent.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
 
   /**
    * Cache of the Form Autofill status (considering preferences and storage).
    */
   _active: null,
 
   /**
+   * The status of Form Autofill's initialization.
+   */
+  _initialized: false,
+
+  /**
+   * Exposes the status of Form Autofill's initialization. It can be used to
+   * determine whether Form Autofill is available for current users.
+   *
+   * @returns {boolean} Whether FormAutofillParent is initialized.
+   */
+  get initialized() {
+    return this._initialized;
+  },
+
+  /**
    * Initializes ProfileStorage and registers the message handler.
    */
   async init() {
+    if (this._initialized) {
+      return;
+    }
+    this._initialized = true;
+
     Services.obs.addObserver(this, "sync-pane-loaded");
     Services.ppmm.addMessageListener("FormAutofill:InitStorage", this);
     Services.ppmm.addMessageListener("FormAutofill:GetRecords", this);
     Services.ppmm.addMessageListener("FormAutofill:SaveAddress", this);
     Services.ppmm.addMessageListener("FormAutofill:RemoveAddresses", this);
     Services.ppmm.addMessageListener("FormAutofill:OpenPreferences", this);
     Services.mm.addMessageListener("FormAutofill:OnFormSubmit", this);
 
@@ -356,29 +378,31 @@ FormAutofillParent.prototype = {
     });
 
     Services.ppmm.broadcastAsyncMessage("FormAutofill:savedFieldNames",
                                         Services.ppmm.initialProcessData.autofillSavedFieldNames);
     this._updateStatus();
   },
 
   _onAddressSubmit(address, target, timeStartedFillingMS) {
+    let showDoorhanger = null;
     if (address.guid) {
       // Avoid updating the fields that users don't modify.
       let originalAddress = this.profileStorage.addresses.get(address.guid);
       for (let field in address.record) {
         if (address.untouchedFields.includes(field) && originalAddress[field]) {
           address.record[field] = originalAddress[field];
         }
       }
 
       if (!this.profileStorage.addresses.mergeIfPossible(address.guid, address.record, true)) {
         this._recordFormFillingTime("address", "autofill-update", timeStartedFillingMS);
 
-        FormAutofillDoorhanger.show(target, "updateAddress").then((state) => {
+        showDoorhanger = async () => {
+          const state = await FormAutofillDoorhanger.show(target, "updateAddress");
           let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record, true);
           switch (state) {
             case "create":
               if (!changedGUIDs.length) {
                 changedGUIDs.push(this.profileStorage.addresses.add(address.record));
               }
               break;
             case "update":
@@ -386,52 +410,54 @@ FormAutofillParent.prototype = {
                 this.profileStorage.addresses.update(address.guid, address.record, true);
                 changedGUIDs.push(address.guid);
               } else {
                 this.profileStorage.addresses.remove(address.guid);
               }
               break;
           }
           changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
-        });
+        };
         // Address should be updated
         Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill_update", 1);
-        return;
+      } else {
+        this._recordFormFillingTime("address", "autofill", timeStartedFillingMS);
+        this.profileStorage.addresses.notifyUsed(address.guid);
+        // Address is merged successfully
+        Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1);
       }
-      this._recordFormFillingTime("address", "autofill", timeStartedFillingMS);
-      this.profileStorage.addresses.notifyUsed(address.guid);
-      // Address is merged successfully
-      Services.telemetry.scalarAdd("formautofill.addresses.fill_type_autofill", 1);
     } else {
       let changedGUIDs = this.profileStorage.addresses.mergeToStorage(address.record);
       if (!changedGUIDs.length) {
         changedGUIDs.push(this.profileStorage.addresses.add(address.record));
       }
       changedGUIDs.forEach(guid => this.profileStorage.addresses.notifyUsed(guid));
       this._recordFormFillingTime("address", "manual", timeStartedFillingMS);
 
       // Show first time use doorhanger
       if (FormAutofillUtils.isAutofillAddressesFirstTimeUse) {
         Services.prefs.setBoolPref(FormAutofillUtils.ADDRESSES_FIRST_TIME_USE_PREF, false);
-        FormAutofillDoorhanger.show(target, "firstTimeUse").then((state) => {
+        showDoorhanger = async () => {
+          const state = await FormAutofillDoorhanger.show(target, "firstTimeUse");
           if (state !== "open-pref") {
             return;
           }
 
           target.ownerGlobal.openPreferences("panePrivacy",
                                              {origin: "autofillDoorhanger"});
-        });
+        };
       } else {
         // We want to exclude the first time form filling.
         Services.telemetry.scalarAdd("formautofill.addresses.fill_type_manual", 1);
       }
     }
+    return showDoorhanger;
   },
 
-  async _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) {
+  _onCreditCardSubmit(creditCard, target, timeStartedFillingMS) {
     // Updates the used status for shield/heartbeat to recognize users who have
     // used Credit Card Autofill.
     let setUsedStatus = status => {
       if (FormAutofillUtils.AutofillCreditCardsUsedStatus < status) {
         Services.prefs.setIntPref(FormAutofillUtils.CREDITCARDS_USED_STATUS_PREF, status);
       }
     };
 
@@ -458,17 +484,17 @@ FormAutofillParent.prototype = {
         recordUnchanged &= untouched;
       }
 
       if (recordUnchanged) {
         this.profileStorage.creditCards.notifyUsed(creditCard.guid);
         // Add probe to record credit card autofill(without modification).
         Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill", 1);
         this._recordFormFillingTime("creditCard", "autofill", timeStartedFillingMS);
-        return;
+        return false;
       }
       // Add the probe to record credit card autofill with modification.
       Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_autofill_modified", 1);
       this._recordFormFillingTime("creditCard", "autofill-update", timeStartedFillingMS);
     } else {
       // Indicate that the user neither sees the doorhanger nor uses Autofill
       // but somehow has a duplicate record in the storage. Will be reset to 2
       // if the doorhanger actually shows below.
@@ -478,65 +504,89 @@ FormAutofillParent.prototype = {
       Services.telemetry.scalarAdd("formautofill.creditCards.fill_type_manual", 1);
       this._recordFormFillingTime("creditCard", "manual", timeStartedFillingMS);
     }
 
     // Early return if it's a duplicate data
     let dupGuid = this.profileStorage.creditCards.getDuplicateGuid(creditCard.record);
     if (dupGuid) {
       this.profileStorage.creditCards.notifyUsed(dupGuid);
-      return;
+      return false;
     }
 
     // Indicate that the user has seen the doorhanger.
     setUsedStatus(2);
 
-    let state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard");
-    if (state == "cancel") {
-      return;
-    }
+    return async () => {
+      // Suppress the pending doorhanger from showing up if user disabled credit card in previous doorhanger.
+      if (!FormAutofillUtils.isAutofillCreditCardsEnabled) {
+        return;
+      }
+
+      const state = await FormAutofillDoorhanger.show(target, creditCard.guid ? "updateCreditCard" : "addCreditCard");
+      if (state == "cancel") {
+        return;
+      }
+
+      if (state == "disable") {
+        Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
+        return;
+      }
+
+      // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
+      // APIs are refactored to be async functions (bug 1399367).
+      if (!await MasterPassword.ensureLoggedIn()) {
+        log.warn("User canceled master password entry");
+        return;
+      }
 
-    if (state == "disable") {
-      Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
-      return;
-    }
+      let changedGUIDs = [];
+      if (creditCard.guid) {
+        if (state == "update") {
+          this.profileStorage.creditCards.update(creditCard.guid, creditCard.record, true);
+          changedGUIDs.push(creditCard.guid);
+        } else if ("create") {
+          changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
+        }
+      } else {
+        changedGUIDs.push(...this.profileStorage.creditCards.mergeToStorage(creditCard.record));
+        if (!changedGUIDs.length) {
+          changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
+        }
+      }
+      changedGUIDs.forEach(guid => this.profileStorage.creditCards.notifyUsed(guid));
+    };
+  },
 
-    // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
-    // APIs are refactored to be async functions (bug 1399367).
-    if (!await MasterPassword.ensureLoggedIn()) {
-      log.warn("User canceled master password entry");
-      return;
+  async _onFormSubmit(data, target) {
+    let {profile: {address, creditCard}, timeStartedFillingMS} = data;
+
+    // Don't record filling time if any type of records has more than one section being
+    // populated. We've been recording the filling time, so the other cases that aren't
+    // recorded on the same basis should be out of the data samples. E.g. Filling time of
+    // populating one profile is different from populating two sections, therefore, we
+    // shouldn't record the later to regress the representation of existing statistics.
+    if (address.length > 1 || creditCard.length > 1) {
+      timeStartedFillingMS = null;
     }
 
-    let changedGUIDs = [];
-    if (creditCard.guid) {
-      if (state == "update") {
-        this.profileStorage.creditCards.update(creditCard.guid, creditCard.record, true);
-        changedGUIDs.push(creditCard.guid);
-      } else if ("create") {
-        changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
-      }
-    } else {
-      changedGUIDs.push(...this.profileStorage.creditCards.mergeToStorage(creditCard.record));
-      if (!changedGUIDs.length) {
-        changedGUIDs.push(this.profileStorage.creditCards.add(creditCard.record));
+    // Transmit the telemetry immediately in the meantime form submitted, and handle these pending
+    // doorhangers at a later.
+    await Promise.all([
+      address.map(addrRecord => this._onAddressSubmit(addrRecord, target, timeStartedFillingMS)),
+      creditCard.map(ccRecord => this._onCreditCardSubmit(ccRecord, target, timeStartedFillingMS)),
+    ].map(pendingDoorhangers => {
+      return pendingDoorhangers.filter(pendingDoorhanger => !!pendingDoorhanger &&
+                                                            typeof pendingDoorhanger == "function");
+    }).map(pendingDoorhangers => new Promise(async resolve => {
+      for (const showDoorhanger of pendingDoorhangers) {
+        await showDoorhanger();
       }
-    }
-    changedGUIDs.forEach(guid => this.profileStorage.creditCards.notifyUsed(guid));
-  },
-
-  _onFormSubmit(data, target) {
-    let {profile: {address, creditCard}, timeStartedFillingMS} = data;
-
-    if (address) {
-      this._onAddressSubmit(address, target, timeStartedFillingMS);
-    }
-    if (creditCard) {
-      this._onCreditCardSubmit(creditCard, target, timeStartedFillingMS);
-    }
+      resolve();
+    })));
   },
   /**
    * Set the probes for the filling time with specific filling type and form type.
    *
    * @private
    * @param  {string} formType
    *         3 type of form (address/creditcard/address-creditcard).
    * @param  {string} fillingType
@@ -547,8 +597,10 @@ FormAutofillParent.prototype = {
   _recordFormFillingTime(formType, fillingType, startedFillingMS) {
     if (!startedFillingMS) {
       return;
     }
     let histogram = Services.telemetry.getKeyedHistogramById("FORM_FILLING_REQUIRED_TIME_MS");
     histogram.add(`${formType}-${fillingType}`, Date.now() - startedFillingMS);
   },
 };
+
+this.formAutofillParent = new FormAutofillParent();
--- a/browser/extensions/formautofill/FormAutofillPreferences.jsm
+++ b/browser/extensions/formautofill/FormAutofillPreferences.jsm
@@ -82,16 +82,17 @@ FormAutofillPreferences.prototype = {
     savedAddressesBtn.className = "accessory-button";
     addressAutofillCheckbox.className = "tail-with-learn-more";
     addressAutofillLearnMore.className = "learnMore text-link";
 
     formAutofillGroup.id = "formAutofillGroup";
     addressAutofill.id = "addressAutofill";
     addressAutofillLearnMore.id = "addressAutofillLearnMore";
 
+    addressAutofill.setAttribute("data-subcategory", "address-autofill");
     addressAutofillLearnMore.setAttribute("value", this.bundle.GetStringFromName("learnMoreLabel"));
     addressAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("autofillAddressesCheckbox"));
     savedAddressesBtn.setAttribute("label", this.bundle.GetStringFromName("savedAddressesBtnLabel"));
 
     addressAutofillLearnMore.setAttribute("href", learnMoreURL);
 
     // Add preferences search support
     savedAddressesBtn.setAttribute("searchkeywords", MANAGE_ADDRESSES_KEYWORDS.concat(EDIT_ADDRESS_KEYWORDS)
@@ -125,16 +126,17 @@ FormAutofillPreferences.prototype = {
       let savedCreditCardsBtn = document.createElementNS(XUL_NS, "button");
       savedCreditCardsBtn.className = "accessory-button";
       creditCardAutofillCheckbox.className = "tail-with-learn-more";
       creditCardAutofillLearnMore.className = "learnMore text-link";
 
       creditCardAutofill.id = "creditCardAutofill";
       creditCardAutofillLearnMore.id = "creditCardAutofillLearnMore";
 
+      creditCardAutofill.setAttribute("data-subcategory", "credit-card-autofill");
       creditCardAutofillLearnMore.setAttribute("value", this.bundle.GetStringFromName("learnMoreLabel"));
       creditCardAutofillCheckbox.setAttribute("label", this.bundle.GetStringFromName("autofillCreditCardsCheckbox"));
       savedCreditCardsBtn.setAttribute("label", this.bundle.GetStringFromName("savedCreditCardsBtnLabel"));
 
       creditCardAutofillLearnMore.setAttribute("href", learnMoreURL);
 
       // Add preferences search support
       savedCreditCardsBtn.setAttribute("searchkeywords", MANAGE_CREDITCARDS_KEYWORDS.concat(EDIT_CREDITCARD_KEYWORDS)
--- a/browser/extensions/formautofill/bootstrap.js
+++ b/browser/extensions/formautofill/bootstrap.js
@@ -11,17 +11,17 @@ const STYLESHEET_URI = "chrome://formaut
 const CACHED_STYLESHEETS = new WeakMap();
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
                                   "resource://gre/modules/AddonManager.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillParent",
+XPCOMUtils.defineLazyModuleGetter(this, "formAutofillParent",
                                   "resource://formautofill/FormAutofillParent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormAutofillUtils",
                                   "resource://formautofill/FormAutofillUtils.jsm");
 
 function insertStyleSheet(domWindow, url) {
   let doc = domWindow.document;
   let styleSheetAttr = `href="${url}" type="text/css"`;
   let styleSheet = doc.createProcessingInstruction("xml-stylesheet", styleSheetAttr);
@@ -105,18 +105,17 @@ function startup(data) {
     Services.prefs.setBoolPref("services.sync.engine.creditcards.available", true);
   } else {
     Services.prefs.clearUserPref("services.sync.engine.creditcards.available");
   }
 
   // Listen for the autocomplete popup message to lazily append our stylesheet related to the popup.
   Services.mm.addMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
 
-  let parent = new FormAutofillParent();
-  parent.init().catch(Cu.reportError);
+  formAutofillParent.init().catch(Cu.reportError);
   Services.ppmm.loadProcessScript("data:,new " + function() {
     Components.utils.import("resource://formautofill/FormAutofillContent.jsm");
   }, true);
   Services.mm.loadFrameScript("chrome://formautofill/content/FormAutofillFrameScript.js", true);
 }
 
 function shutdown() {
   Services.mm.removeMessageListener("FormAutoComplete:MaybeOpenPopup", onMaybeOpenPopup);
--- a/browser/extensions/formautofill/content/manageDialog.css
+++ b/browser/extensions/formautofill/content/manageDialog.css
@@ -1,16 +1,26 @@
 /* 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/. */
 
+html {
+  /* Prevent unnecessary horizontal scroll bar from showing */
+  overflow-x: hidden;
+}
+
 div {
   display: flex;
 }
 
+button {
+  padding-right: 10px;
+  padding-left: 10px;
+}
+
 fieldset {
   margin: 0;
   padding: 0;
   border: none;
 }
 
 fieldset > legend {
   box-sizing: border-box;
--- a/browser/extensions/formautofill/content/manageDialog.js
+++ b/browser/extensions/formautofill/content/manageDialog.js
@@ -44,17 +44,17 @@ class ManageRecords {
 
   uninit() {
     log.debug("uninit");
     this.detachEventListeners();
     this._elements = null;
   }
 
   localizeDocument() {
-    document.documentElement.style.width = FormAutofillUtils.stringBundle.GetStringFromName("manageDialogsWidth");
+    document.documentElement.style.minWidth = FormAutofillUtils.stringBundle.GetStringFromName("manageDialogsWidth");
     FormAutofillUtils.localizeMarkup(AUTOFILL_BUNDLE_URI, document);
   }
 
   /**
    * Get the selected options on the addresses element.
    *
    * @returns {array<DOMElement>}
    */
--- a/browser/extensions/formautofill/skin/shared/editDialog.css
+++ b/browser/extensions/formautofill/skin/shared/editDialog.css
@@ -32,16 +32,21 @@ div > span {
 option {
   padding: 0.3em 0.5em;
 }
 
 textarea {
   resize: none;
 }
 
+button {
+  padding-right: 10px;
+  padding-left: 10px;
+}
+
 input,
 select {
   box-sizing: border-box;
   flex: 1 0 auto;
 }
 
 #controls-container {
   flex: 0 1 100%;
--- a/browser/extensions/formautofill/test/fixtures/multiple_section.html
+++ b/browser/extensions/formautofill/test/fixtures/multiple_section.html
@@ -6,42 +6,79 @@
 </head>
 <body>
   <h1>Form Autofill Demo Page</h1>
   <form>
     <label>Name: <input id="name" autocomplete="name"></label><br/>
     <label>Organization: <input id="organization" autocomplete="organization"></label><br/>
 
     <br/>
-    <label>Street Address: <input id="street-address" autocomplete="shipping street-address"></label><br/>
-    <label>Address Level 2: <input id="address-level2" autocomplete="shipping address-level2"></label><br/>
-    <label>Address Level 1: <input id="address-level1" autocomplete="shipping address-level1"></label><br/>
-    <label>Postal Code: <input id="postal-code" autocomplete="shipping postal-code"></label><br/>
-    <label>Country: <input id="country" autocomplete="shipping country"></label><br/>
+    <label>Street Address: <input id="street-address-a" autocomplete="shipping street-address"></label><br/>
+    <label>Address Level 2: <input id="address-level2-a" autocomplete="shipping address-level2"></label><br/>
+    <label>Address Level 1: <input id="address-level1-a" autocomplete="shipping address-level1"></label><br/>
+    <label>Postal Code: <input id="postal-code-a" autocomplete="shipping postal-code"></label><br/>
+    <label>Country: <input id="country-a" autocomplete="shipping country"></label><br/>
+
+    <br/>
+    <label>Street Address: <input id="street-address-b" autocomplete="billing street-address"></label><br/>
+    <label>Address Level 2: <input id="address-level2-b" autocomplete="billing address-level2"></label><br/>
+    <label>Address Level 1: <input id="address-level1-b" autocomplete="billing address-level1"></label><br/>
+    <label>Postal Code: <input id="postal-code-b" autocomplete="billing postal-code"></label><br/>
+    <label>Country: <input id="country-b" autocomplete="billing country"></label><br/>
+
+    <br/>
+    <label>Street Address: <input id="street-address-c" autocomplete="section-my street-address"></label><br/>
+    <label>Address Level 2: <input id="address-level2-c" autocomplete="section-my address-level2"></label><br/>
+    <label>Address Level 1: <input id="address-level1-c" autocomplete="section-my address-level1"></label><br/>
+    <label>Postal Code: <input id="postal-code-c" autocomplete="section-my postal-code"></label><br/>
+    <label>Country: <input id="country-c" autocomplete="section-my country"></label><br/>
 
     <br/>
-    <label>Street Address: <input id="street-address" autocomplete="billing street-address"></label><br/>
-    <label>Address Level 2: <input id="address-level2" autocomplete="billing address-level2"></label><br/>
-    <label>Address Level 1: <input id="address-level1" autocomplete="billing address-level1"></label><br/>
-    <label>Postal Code: <input id="postal-code" autocomplete="billing postal-code"></label><br/>
-    <label>Country: <input id="country" autocomplete="billing country"></label><br/>
+    <label>Telephone: <input id="tel-a" autocomplete="work tel"></label><br/>
+    <label>Email: <input id="email-a" autocomplete="work email"></label><br/>
+    <br/>
+    <label>Telephone: <input id="tel-b" autocomplete="home tel"></label><br/>
+    <label>Email: <input id="email-b" autocomplete="home email"></label><br/>
+    <p>
+      <input type="submit" value="Submit">
+      <button type="reset">Reset</button>
+    </p>
+  </form>
+
+  <form>
+    <label>Name: <input autocomplete="name"></label><br/>
+    <label>Organization: <input autocomplete="organization"></label><br/>
 
     <br/>
-    <label>Street Address: <input id="street-address" autocomplete="section-my street-address"></label><br/>
-    <label>Address Level 2: <input id="address-level2" autocomplete="section-my address-level2"></label><br/>
-    <label>Address Level 1: <input id="address-level1" autocomplete="section-my address-level1"></label><br/>
-    <label>Postal Code: <input id="postal-code" autocomplete="section-my postal-code"></label><br/>
-    <label>Country: <input id="country" autocomplete="section-my country"></label><br/>
+    <label>Street Address: <input autocomplete="street-address"></label><br/>
+    <label>Address Level 2: <input autocomplete="address-level2"></label><br/>
+    <label>Address Level 1: <input autocomplete="address-level1"></label><br/>
+    <label>Postal Code: <input autocomplete="postal-code"></label><br/>
+    <label>Country: <input autocomplete="country"></label><br/>
 
     <br/>
-    <label>Telephone: <input id="tel" autocomplete="work tel"></label><br/>
-    <label>Email: <input id="email" autocomplete="work email"></label><br/>
+    <label>Street Address: <input autocomplete="street-address"></label><br/>
+    <label>Address Level 2: <input autocomplete="address-level2"></label><br/>
+    <label>Address Level 1: <input autocomplete="address-level1"></label><br/>
+    <label>Postal Code: <input autocomplete="postal-code"></label><br/>
+    <label>Country: <input autocomplete="country"></label><br/>
+
     <br/>
-    <label>Telephone: <input id="tel" autocomplete="home tel"></label><br/>
-    <label>Email: <input id="email" autocomplete="home email"></label><br/>
+    <label>Street Address: <input autocomplete="street-address"></label><br/>
+    <label>Address Level 2: <input autocomplete="address-level2"></label><br/>
+    <label>Address Level 1: <input autocomplete="address-level1"></label><br/>
+    <label>Postal Code: <input autocomplete="postal-code"></label><br/>
+    <label>Country: <input autocomplete="country"></label><br/>
+
+    <br/>
+    <label>Telephone: <input autocomplete="work tel"></label><br/>
+    <label>Email: <input autocomplete="work email"></label><br/>
+    <br/>
+    <label>Telephone: <input autocomplete="home tel"></label><br/>
+    <label>Email: <input autocomplete="home email"></label><br/>
     <p>
       <input type="submit" value="Submit">
       <button type="reset">Reset</button>
     </p>
   </form>
 
 </body>
 </html>
--- a/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/test_multiple_section.js
@@ -36,12 +36,43 @@ runHeuristicsTest([
         [
           {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "street-address"},
           {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "address-level2"},
           {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "address-level1"},
           {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "postal-code"},
           {"section": "section-my", "addressType": "", "contactType": "", "fieldName": "country"},
         ],
       ],
+      [
+        [
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "name"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "organization"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
+        ],
+        [
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
+        ],
+        [
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "street-address"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
+          {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
+          {"section": "", "addressType": "", "contactType": "work", "fieldName": "tel"},
+          {"section": "", "addressType": "", "contactType": "work", "fieldName": "email"},
+        ],
+        [
+          {"section": "", "addressType": "", "contactType": "home", "fieldName": "tel"},
+          {"section": "", "addressType": "", "contactType": "home", "fieldName": "email"},
+        ],
+      ],
     ],
   },
 ], "../../fixtures/");
 
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_BestBuy.js
@@ -1,17 +1,17 @@
 /* global runHeuristicsTest */
 
 "use strict";
 
 runHeuristicsTest([
   {
     fixturePath: "Checkout_ShippingAddress.html",
     expectedResult: [
-      [[]], // Search form
+      [], // Search form
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, // city
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, // state
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
       ]],
@@ -21,17 +21,17 @@ runHeuristicsTest([
       [[ // unknown
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
       ]],
     ],
   }, {
     fixturePath: "Checkout_Payment.html",
     expectedResult: [
-      [[]], // Search form
+      [], // Search form
       [[ // Sign up
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, // city
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CDW.js
@@ -15,19 +15,20 @@ runHeuristicsTest([
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, // city
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, // state
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
 
         // FIXME: bug 1392932 - misdetect ZIP ext string
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
+      ], [
         // The below "tel-extension" is correct and removed due to the
         // duplicated field above.
-//      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
       ]],
       [],
     ],
   }, {
     fixturePath: "Checkout_BillingPaymentInfo.html",
     expectedResult: [
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
@@ -42,21 +43,23 @@ runHeuristicsTest([
         // FIXME: bug 1392932 - misdetect ZIP ext string
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
 
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
+      ], [
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
       ]],
       [],
     ],
   }, {
     fixturePath: "Checkout_Logon.html",
     expectedResult: [
       [],
-      [[]],
+      [],
       [],
     ],
   },
 ], "../../../fixtures/third_party/CDW/");
 
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_CostCo.js
@@ -45,25 +45,24 @@ runHeuristicsTest([
   }, {
     fixturePath: "Payment.html",
     expectedResult: [
       [[
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
-
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"}, // ac-off
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
       ]],
       [],
-      [[]],
+      [],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "additional-name"}, // middle-name initial
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "organization"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
@@ -91,17 +90,17 @@ runHeuristicsTest([
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
       [],
     ],
   }, {
     fixturePath: "SignIn.html",
     expectedResult: [
-      [[]],
+      [],
       [],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
       [],
       [[ // Forgot password
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Macys.js
@@ -11,20 +11,17 @@ runHeuristicsTest([
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, // city
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, // state
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
       ]],
-      [
-/*
-*/
-      ],
+      [],
     ],
   }, {
     fixturePath: "Checkout_Payment.html",
     expectedResult: [
       [[
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"}, // ac-off
@@ -51,15 +48,15 @@ runHeuristicsTest([
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "password"},
       ]],
       [[ // Forgot password
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
-      [[]],
+      [],
       [],
       [],
     ],
   },
 ], "../../../fixtures/third_party/Macys/");
 
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_NewEgg.js
@@ -21,16 +21,19 @@ runHeuristicsTest([
       [],
     ],
   }, {
     fixturePath: "BillingInfo.html",
     expectedResult: [
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
+      ], [
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"},
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "country"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, // state
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_OfficeDepot.js
@@ -50,15 +50,15 @@ runHeuristicsTest([
         // FIXME: bug 1392950 - the membership number should not be detected
         // as cc-number.
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
       ]],
     ],
   }, {
     fixturePath: "SignIn.html",
     expectedResult: [
-      [[ // ac-off
+      [ // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
-      ]],
+      ],
     ],
   },
 ], "../../../fixtures/third_party/OfficeDepot/");
 
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_QVC.js
@@ -8,50 +8,50 @@ runHeuristicsTest([
     expectedResult: [
       [[
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-month"}, // select
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-day"}, // select
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-year"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"},
-
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"},
-
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
+      ], [
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
     ],
   }, {
     fixturePath: "PaymentMethod.html",
     expectedResult: [
       [[
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-month"}, // select
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-day"}, // select
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "bday-year"}, // select
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-type"}, // select
-
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp"},
-
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
+      ], [
+        {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"}, // ac-off
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
     ],
   }, {
     fixturePath: "SignIn.html",
     expectedResult: [
-      [[]],
+      [],
       [[ // Sign in
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
       ]],
       [],
     ],
   },
 ], "../../../fixtures/third_party/QVC/");
 
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Sears.js
@@ -2,61 +2,61 @@
 
 "use strict";
 
 runHeuristicsTest([
   {
     fixturePath: "ShippingAddress.html",
     expectedResult: [
       [],
-      [[]], // search form, ac-off
-      [[ // ac-off
+      [], // search form, ac-off
+      [ // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
-      ]],
-      [[ // check-out, ac-off
+      ],
+      [ // check-out, ac-off
 /*
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
 */
-      ]],
+      ],
       [ // ac-off
 /*
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "new-password"},
 */
       ],
-      [[ // ac-off
+      [ // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
-      ]],
-      [[ // ac-off
+      ],
+      [ // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
-      ]],
+      ],
     ],
   }, {
     fixturePath: "PaymentOptions.html",
     expectedResult: [
       [],
-      [[]], // search
+      [], // search
       [[ // credit card
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-number"},
         // FIXME: bug 1392958 - Cardholder name field should be detected as cc-name
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
       ]],
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Staples.js
@@ -1,25 +1,25 @@
 /* global runHeuristicsTest */
 
 "use strict";
 
 runHeuristicsTest([
   {
     fixturePath: "Basic.html",
     expectedResult: [
-      [[ // ac-off
+      [ // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "organization"},
-      ]],
+      ],
     ],
   }, {
     fixturePath: "Basic_ac_on.html",
     expectedResult: [
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
--- a/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js
+++ b/browser/extensions/formautofill/test/unit/heuristics/third_party/test_Walmart.js
@@ -4,33 +4,33 @@
 
 runHeuristicsTest([
   {
     fixturePath: "Checkout.html",
     expectedResult: [
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
       ]],
-      [[]],
+      [],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "email"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "password"}, // ac-off
       ]],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "email"}, // ac-off
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "password"},
 //      {"section": "", "addressType": "", "contactType": "", "fieldName": "password"}, // ac-off
       ]],
     ],
   }, {
     fixturePath: "Payment.html",
     expectedResult: [
-      [[]],
+      [],
       [
         [
           {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "given-name"},
           {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "family-name"},
           {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "cc-number"},
           {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
           {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
 //        {"section": "section-payment", "addressType": "", "contactType": "", "fieldName": "cc-csc"},
@@ -45,17 +45,17 @@ runHeuristicsTest([
       ],
     ],
   }, {
     fixturePath: "Shipping.html",
     expectedResult: [
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "postal-code"},
       ]],
-      [[]],
+      [],
       [[
         {"section": "", "addressType": "", "contactType": "", "fieldName": "given-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "family-name"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line1"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-line2"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level2"}, // city
         {"section": "", "addressType": "", "contactType": "", "fieldName": "address-level1"}, // state
--- a/browser/extensions/formautofill/test/unit/test_activeStatus.js
+++ b/browser/extensions/formautofill/test/unit/test_activeStatus.js
@@ -1,15 +1,15 @@
 /*
  * Test for status handling in Form Autofill Parent.
  */
 
 "use strict";
 
-Cu.import("resource://formautofill/FormAutofillParent.jsm");
+let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {});
 Cu.import("resource://formautofill/ProfileStorage.jsm");
 
 add_task(async function test_activeStatus_init() {
   let formAutofillParent = new FormAutofillParent();
   sinon.spy(formAutofillParent, "_updateStatus");
 
   // Default status is null before initialization
   do_check_eq(formAutofillParent._active, null);
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -287,20 +287,32 @@ const TESTCASES = [
                </form>`,
     allowDuplicates: true,
     sections: [{
       addressFieldDetails: [
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-extension"},
+      ],
+      creditCardFieldDetails: [],
+    }, {
+      addressFieldDetails: [
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
+
+        // TODO Bug 1421181 - "tel-country-code" field should belong to the next
+        // section. There should be a way to group the related fields during the
+        // parsing stage.
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-country-code"},
+      ],
+      creditCardFieldDetails: [],
+    }, {
+      addressFieldDetails: [
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-prefix"},
         {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-local-suffix"},
       ],
       creditCardFieldDetails: [],
     }],
     validFieldDetails: [
       {"section": "", "addressType": "", "contactType": "", "fieldName": "tel-area-code"},
@@ -456,23 +468,20 @@ for (let tc of TESTCASES) {
         handlerDetails.forEach((detail, index) => {
           Assert.equal(detail.fieldName, testCaseDetails[index].fieldName, "fieldName");
           Assert.equal(detail.section, testCaseDetails[index].section, "section");
           Assert.equal(detail.addressType, testCaseDetails[index].addressType, "addressType");
           Assert.equal(detail.contactType, testCaseDetails[index].contactType, "contactType");
           Assert.equal(detail.elementWeakRef.get(), testCaseDetails[index].elementWeakRef.get(), "DOM reference");
         });
       }
-      for (let i = 0; i < testcase.sections.length; i++) {
-        let section = testcase.sections[i];
-        [
-          section.addressFieldDetails,
-          section.creditCardFieldDetails,
-        ].forEach(details => setElementWeakRef(details));
-      }
+      setElementWeakRef(testcase.sections.reduce((fieldDetails, section) => {
+        fieldDetails.push(...section.addressFieldDetails, ...section.creditCardFieldDetails);
+        return fieldDetails;
+      }, []));
       setElementWeakRef(testcase.validFieldDetails);
 
       let handler = new FormAutofillHandler(formLike);
       let validFieldDetails = handler.collectFormFields(testcase.allowDuplicates);
 
       Assert.equal(handler.sections.length, testcase.sections.length);
       for (let i = 0; i < handler.sections.length; i++) {
         let section = handler.sections[i];
--- a/browser/extensions/formautofill/test/unit/test_createRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_createRecords.js
@@ -21,81 +21,84 @@ const TESTCASES = [
       "given-name": "John",
       "organization": "*".repeat(200),
       "address-level1": "*".repeat(201),
       "country": "US",
       "cc-number": "1111222233334444",
       "cc-name": "*".repeat(201),
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "*".repeat(200),
         "address-level1": "",
         "country": "US",
-      },
-      creditCard: {
+      }],
+      creditCard: [{
         "cc-number": "1111222233334444",
         "cc-name": "",
-      },
+      }],
     },
   },
   {
     description: "Don't create address record if filled data is less than 3",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" autocomplete="country">
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
     },
     expectedRecord: {
-      address: undefined,
+      address: [],
+      creditCard: [],
     },
   },
   {
     description: "\"country\" using @autocomplete shouldn't be identified aggressively",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" autocomplete="country">
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "country": "United States",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "country": "United States",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "\"country\" using heuristics should be identified aggressively",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" name="country">
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "country": "United States",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "country": "US",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "\"tel\" related fields should be concatenated",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="tel-country-code" autocomplete="tel-country-code">
@@ -103,21 +106,22 @@ const TESTCASES = [
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "tel-country-code": "+1",
       "tel-national": "1234567890",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "tel": "+11234567890",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "\"tel\" should be removed if it's too short",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" autocomplete="country">
@@ -125,22 +129,23 @@ const TESTCASES = [
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "country": "United States",
       "tel": "1234",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "country": "United States",
         "tel": "",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "\"tel\" should be removed if it's too long",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" autocomplete="country">
@@ -148,22 +153,23 @@ const TESTCASES = [
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "country": "United States",
       "tel": "1234567890123456",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "country": "United States",
         "tel": "",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "\"tel\" should be removed if it contains invalid characters",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="organization" autocomplete="organization">
                 <input id="country" autocomplete="country">
@@ -171,38 +177,40 @@ const TESTCASES = [
                </form>`,
     formValue: {
       "given-name": "John",
       "organization": "Mozilla",
       "country": "United States",
       "tel": "12345###!!!",
     },
     expectedRecord: {
-      address: {
+      address: [{
         "given-name": "John",
         "organization": "Mozilla",
         "country": "United States",
         "tel": "",
-      },
+      }],
+      creditCard: [],
     },
   },
   {
     description: "All name related fields should be counted as 1 field only.",
     document: `<form>
                 <input id="given-name" autocomplete="given-name">
                 <input id="family-name" autocomplete="family-name">
                 <input id="organization" autocomplete="organization">
                </form>`,
     formValue: {
       "given-name": "John",
       "family-name": "Doe",
       "organization": "Mozilla",
     },
     expectedRecord: {
-      address: undefined,
+      address: [],
+      creditCard: [],
     },
   },
   {
     description: "All telephone related fields should be counted as 1 field only.",
     document: `<form>
                 <input id="tel-country-code" autocomplete="tel-country-code">
                 <input id="tel-area-code" autocomplete="tel-area-code">
                 <input id="tel-local" autocomplete="tel-local">
@@ -210,83 +218,158 @@ const TESTCASES = [
                </form>`,
     formValue: {
       "tel-country-code": "+1",
       "tel-area-code": "123",
       "tel-local": "4567890",
       "organization": "Mozilla",
     },
     expectedRecord: {
-      address: undefined,
+      address: [],
+      creditCard: [],
     },
   },
   {
     description: "A credit card form with the value of cc-number, cc-exp, and cc-name.",
     document: `<form>
                 <input id="cc-number" autocomplete="cc-number">
                 <input id="cc-name" autocomplete="cc-name">
                 <input id="cc-exp" autocomplete="cc-exp">
                </form>`,
     formValue: {
       "cc-number": "4444000022220000",
       "cc-name": "Foo Bar",
       "cc-exp": "2022-06",
     },
     expectedRecord: {
-      creditCard: {
+      address: [],
+      creditCard: [{
         "cc-number": "4444000022220000",
         "cc-name": "Foo Bar",
         "cc-exp": "2022-06",
-      },
+      }],
     },
   },
   {
     description: "A credit card form with cc-number value only.",
     document: `<form>
                 <input id="cc-number" autocomplete="cc-number">
                </form>`,
     formValue: {
       "cc-number": "4444000022220000",
     },
     expectedRecord: {
-      creditCard: {
+      address: [],
+      creditCard: [{
         "cc-number": "4444000022220000",
-      },
+      }],
     },
   },
   {
     description: "A credit card form must have cc-number value.",
     document: `<form>
                 <input id="cc-number" autocomplete="cc-number">
                 <input id="cc-name" autocomplete="cc-name">
                 <input id="cc-exp" autocomplete="cc-exp">
                </form>`,
     formValue: {
       "cc-number": "",
       "cc-name": "Foo Bar",
       "cc-exp": "2022-06",
     },
     expectedRecord: {
-      creditCard: undefined,
+      address: [],
+      creditCard: [],
     },
   },
   {
     description: "A credit card form must have cc-number field.",
     document: `<form>
                 <input id="cc-name" autocomplete="cc-name">
                 <input id="cc-exp" autocomplete="cc-exp">
                </form>`,
     formValue: {
       "cc-name": "Foo Bar",
       "cc-exp": "2022-06",
     },
     expectedRecord: {
-      creditCard: undefined,
+      address: [],
+      creditCard: [],
     },
   },
+  {
+    description: "A form with multiple sections",
+    document: `<form>
+                <input id="given-name" autocomplete="given-name">
+                <input id="organization" autocomplete="organization">
+                <input id="country" autocomplete="country">
+
+                <input id="given-name-shipping" autocomplete="shipping given-name">
+                <input id="family-name-shipping" autocomplete="shipping family-name">
+                <input id="organization-shipping" autocomplete="shipping organization">
+                <input id="country-shipping" autocomplete="shipping country">
+
+                <input id="given-name-billing" autocomplete="billing given-name">
+                <input id="organization-billing" autocomplete="billing organization">
+                <input id="country-billing" autocomplete="billing country">
+
+                <input id="cc-number-section-one" autocomplete="section-one cc-number">
+                <input id="cc-name-section-one" autocomplete="section-one cc-name">
+
+                <input id="cc-number-section-two" autocomplete="section-two cc-number">
+                <input id="cc-name-section-two" autocomplete="section-two cc-name">
+                <input id="cc-exp-section-two" autocomplete="section-two cc-exp">
+               </form>`,
+    formValue: {
+      "given-name": "Bar",
+      "organization": "Foo",
+      "country": "US",
+
+      "given-name-shipping": "John",
+      "family-name-shipping": "Doe",
+      "organization-shipping": "Mozilla",
+      "country-shipping": "US",
+
+      "given-name-billing": "Foo",
+      "organization-billing": "Bar",
+      "country-billing": "US",
+
+      "cc-number-section-one": "4444000022220000",
+      "cc-name-section-one": "John",
+
+      "cc-number-section-two": "4444000022221111",
+      "cc-name-section-two": "Foo Bar",
+      "cc-exp-section-two": "2026-26",
+    },
+    expectedRecord: {
+      address: [{
+        "given-name": "Bar",
+        "organization": "Foo",
+        "country": "US",
+      }, {
+        "given-name": "John",
+        "family-name": "Doe",
+        "organization": "Mozilla",
+        "country": "US",
+      }, {
+        "given-name": "Foo",
+        "organization": "Bar",
+        "country": "US",
+      }],
+      creditCard: [{
+        "cc-number": "4444000022220000",
+        "cc-name": "John",
+      }, {
+        "cc-number": "4444000022221111",
+        "cc-name": "Foo Bar",
+        "cc-exp": "2026-26",
+      }],
+    },
+  },
+
 ];
 
 for (let testcase of TESTCASES) {
   add_task(async function() {
     do_print("Starting testcase: " + testcase.description);
 
     let doc = MockDocument.createTestDocument("http://localhost:8080/test/", testcase.document);
     let form = doc.querySelector("form");
@@ -296,17 +379,14 @@ for (let testcase of TESTCASES) {
     handler.collectFormFields();
 
     for (let id in testcase.formValue) {
       doc.getElementById(id).value = testcase.formValue[id];
     }
 
     let record = handler.createRecords();
 
-    for (let type in testcase.expectedRecord) {
-      if (!testcase.expectedRecord[type]) {
-        do_check_eq(record[type], undefined);
-      } else {
-        Assert.deepEqual(record[type].record, testcase.expectedRecord[type]);
-      }
+    let expectedRecord = testcase.expectedRecord;
+    for (let type in record) {
+      Assert.deepEqual(record[type].map(secRecord => secRecord.record), expectedRecord[type]);
     }
   });
 }
--- a/browser/extensions/formautofill/test/unit/test_getRecords.js
+++ b/browser/extensions/formautofill/test/unit/test_getRecords.js
@@ -1,15 +1,15 @@
 /*
  * Test for make sure getRecords can retrieve right collection from storage.
  */
 
 "use strict";
 
-Cu.import("resource://formautofill/FormAutofillParent.jsm");
+let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {});
 Cu.import("resource://formautofill/MasterPassword.jsm");
 Cu.import("resource://formautofill/ProfileStorage.jsm");
 
 const TEST_ADDRESS_1 = {
   "given-name": "Timothy",
   "additional-name": "John",
   "family-name": "Berners-Lee",
   organization: "World Wide Web Consortium",
--- a/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
+++ b/browser/extensions/formautofill/test/unit/test_onFormSubmitted.js
@@ -57,52 +57,54 @@ const TESTCASES = [
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
       "tel": "1-650-903-0800",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Trigger credit card saving",
     formValue: {
       "cc-name": "John Doe",
       "cc-number": "1234567812345678",
       "cc-exp-month": 12,
       "cc-exp-year": 2000,
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        creditCard: {
+        address: [],
+        creditCard: [{
           guid: null,
           record: {
             "cc-name": "John Doe",
             "cc-number": "1234567812345678",
             "cc-exp-month": 12,
             "cc-exp-year": 2000,
           },
           untouchedFields: [],
-        },
+        }],
       },
     },
   },
   {
     description: "Trigger address and credit card saving",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
@@ -110,381 +112,394 @@ const TESTCASES = [
       "cc-name": "John Doe",
       "cc-number": "1234567812345678",
       "cc-exp-month": 12,
       "cc-exp-year": 2000,
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
-        },
-        creditCard: {
+        }],
+        creditCard: [{
           guid: null,
           record: {
             "cc-name": "John Doe",
             "cc-number": "1234567812345678",
             "cc-exp-month": 12,
             "cc-exp-year": 2000,
           },
           untouchedFields: [],
-        },
+        }],
       },
     },
   },
   {
     description: "Profile saved with trimmed string",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue  ",
       "country": "USA",
       "tel": "  1-650-903-0800",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Eliminate the field that is empty after trimmed",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
       "email": "  ",
       "tel": "1-650-903-0800",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "email": "",
             "tel": "1-650-903-0800",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Save state with regular select option",
     formValue: {
       "address-level1": "CA",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "CA",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Save state with lowercase value",
     formValue: {
       "address-level1": "ca",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "CA",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the label",
     formValue: {
       "address-level1": "AR",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "AR",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the value",
     formValue: {
       "address-level1": "US-CA",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "CA",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Save state with a country code prefixed to the value and label",
     formValue: {
       "address-level1": "US-AZ",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "AZ",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Should save select label instead when failed to abbreviate the value",
     formValue: {
       "address-level1": "Ariz",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "address-level1": "Arizonac",
             "address-level2": "",
             "street-address": "331 E. Evelyn Avenue",
             "country": "USA",
             "email": "",
             "tel": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Shouldn't save select with multiple selections",
     formValue: {
       "address-level1": ["AL", "AK", "AP"],
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
       "tel": "1-650-903-0800",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "tel": "1-650-903-0800",
             "email": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Shouldn't save select with empty value",
     formValue: {
       "address-level1": "",
       "street-addr": "331 E. Evelyn Avenue",
       "country": "USA",
       "tel": "1-650-903-0800",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "",
             "address-level2": "",
             "country": "USA",
             "tel": "1-650-903-0800",
             "email": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Shouldn't save tel whose length is too short",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "address-level1": "CA",
       "country": "US",
       "tel": "1234",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "CA",
             "address-level2": "",
             "country": "US",
             "tel": "",
             "email": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Shouldn't save tel whose length is too long",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "address-level1": "CA",
       "country": "US",
       "tel": "1234567890123456",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "CA",
             "address-level2": "",
             "country": "US",
             "tel": "",
             "email": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
   {
     description: "Shouldn't save tel which contains invalid characters",
     formValue: {
       "street-addr": "331 E. Evelyn Avenue",
       "address-level1": "CA",
       "country": "US",
       "tel": "12345###!!",
     },
     expectedResult: {
       formSubmission: true,
       records: {
-        address: {
+        address: [{
           guid: null,
           record: {
             "street-address": "331 E. Evelyn Avenue",
             "address-level1": "CA",
             "address-level2": "",
             "country": "US",
             "tel": "",
             "email": "",
           },
           untouchedFields: [],
-        },
+        }],
+        creditCard: [],
       },
     },
   },
 ];
 
 add_task(async function handle_earlyformsubmit_event() {
   do_print("Starting testcase: Test an invalid form element");
   let fakeForm = MOCK_DOC.createElement("form");
   sinon.spy(FormAutofillContent, "_onFormSubmit");
 
-  do_check_eq(FormAutofillContent.notify(fakeForm), true);
-  do_check_eq(FormAutofillContent._onFormSubmit.called, false);
+  Assert.equal(FormAutofillContent.notify(fakeForm), true);
+  Assert.equal(FormAutofillContent._onFormSubmit.called, false);
   FormAutofillContent._onFormSubmit.restore();
 });
 
 add_task(async function autofill_disabled() {
   let form = MOCK_DOC.getElementById("form1");
   form.reset();
 
   let testcase = {
@@ -503,43 +518,43 @@ add_task(async function autofill_disable
 
   sinon.stub(FormAutofillContent, "_onFormSubmit");
 
   // "_onFormSubmit" shouldn't be called if both "addresses" and "creditCards"
   // are disabled.
   Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
   Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
   FormAutofillContent.notify(form);
-  do_check_eq(FormAutofillContent._onFormSubmit.called, false);
+  Assert.equal(FormAutofillContent._onFormSubmit.called, false);
   FormAutofillContent._onFormSubmit.reset();
 
   // "_onFormSubmit" should be called as usual.
   Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
   Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
   FormAutofillContent.notify(form);
-  do_check_eq(FormAutofillContent._onFormSubmit.called, true);
-  do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined);
-  do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined);
+  Assert.equal(FormAutofillContent._onFormSubmit.called, true);
+  Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
+  Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
   FormAutofillContent._onFormSubmit.reset();
 
   // "address" should be empty if "addresses" pref is disabled.
   Services.prefs.setBoolPref("extensions.formautofill.addresses.enabled", false);
   FormAutofillContent.notify(form);
-  do_check_eq(FormAutofillContent._onFormSubmit.called, true);
-  do_check_eq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined);
-  do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined);
+  Assert.equal(FormAutofillContent._onFormSubmit.called, true);
+  Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
+  Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
   FormAutofillContent._onFormSubmit.reset();
   Services.prefs.clearUserPref("extensions.formautofill.addresses.enabled");
 
   // "creditCard" should be empty if "creditCards" pref is disabled.
   Services.prefs.setBoolPref("extensions.formautofill.creditCards.enabled", false);
   FormAutofillContent.notify(form);
-  do_check_eq(FormAutofillContent._onFormSubmit.called, true);
-  do_check_neq(FormAutofillContent._onFormSubmit.args[0][0].address, undefined);
-  do_check_eq(FormAutofillContent._onFormSubmit.args[0][0].creditCard, undefined);
+  Assert.deepEqual(FormAutofillContent._onFormSubmit.called, true);
+  Assert.notDeepEqual(FormAutofillContent._onFormSubmit.args[0][0].address, []);
+  Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0].creditCard, []);
   FormAutofillContent._onFormSubmit.reset();
   Services.prefs.clearUserPref("extensions.formautofill.creditCards.enabled");
 
   FormAutofillContent._onFormSubmit.restore();
 });
 
 TESTCASES.forEach(testcase => {
   add_task(async function check_records_saving_is_called_correctly() {
@@ -561,17 +576,17 @@ TESTCASES.forEach(testcase => {
       }
     }
     sinon.stub(FormAutofillContent, "_onFormSubmit");
 
     let element = MOCK_DOC.getElementById(TARGET_ELEMENT_ID);
     FormAutofillContent.identifyAutofillFields(element);
     FormAutofillContent.notify(form);
 
-    do_check_eq(FormAutofillContent._onFormSubmit.called,
+    Assert.equal(FormAutofillContent._onFormSubmit.called,
                 testcase.expectedResult.formSubmission);
     if (FormAutofillContent._onFormSubmit.called) {
       Assert.deepEqual(FormAutofillContent._onFormSubmit.args[0][0],
                        testcase.expectedResult.records);
     }
     FormAutofillContent._onFormSubmit.restore();
   });
 });
--- a/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
+++ b/browser/extensions/formautofill/test/unit/test_savedFieldNames.js
@@ -1,15 +1,15 @@
 /*
  * Test for keeping the valid fields information in initialProcessData.
  */
 
 "use strict";
 
-Cu.import("resource://formautofill/FormAutofillParent.jsm");
+let {FormAutofillParent} = Cu.import("resource://formautofill/FormAutofillParent.jsm", {});
 Cu.import("resource://formautofill/ProfileStorage.jsm");
 
 add_task(async function test_profileSavedFieldNames_init() {
   let formAutofillParent = new FormAutofillParent();
   sinon.stub(formAutofillParent, "_updateSavedFieldNames");
 
   await formAutofillParent.init();
   await formAutofillParent.profileStorage.initialize();
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -105,29 +105,29 @@ MOZ_PKG_MAC_ICON=branding/disk.icns
 MOZ_PKG_MAC_EXTRA=--symlink '/Applications:/ '
 endif
 
 include $(topsrcdir)/toolkit/mozapps/installer/signing.mk
 include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
 
 ifeq (bundle, $(MOZ_FS_LAYOUT))
 BINPATH = $(_BINPATH)
-DEFINES += -DAPPNAME=$(_APPNAME)
+DEFINES += -DAPPNAME='$(_APPNAME)'
 else
 # Every other platform just winds up in dist/bin
 BINPATH = bin
 endif
-DEFINES += -DBINPATH=$(BINPATH)
+DEFINES += -DBINPATH='$(BINPATH)'
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 RESPATH = $(_APPNAME)/Contents/Resources
 else
 RESPATH = $(BINPATH)
 endif
-DEFINES += -DRESPATH=$(RESPATH)
+DEFINES += -DRESPATH="$(RESPATH)"
 
 LPROJ_ROOT = $(firstword $(subst -, ,$(AB_CD)))
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 ifeq (zh-TW,$(AB_CD))
 LPROJ_ROOT := $(subst -,_,$(AB_CD))
 endif
 endif
 DEFINES += -DLPROJ_ROOT=$(LPROJ_ROOT)
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -166,20 +166,20 @@ else
 	  sed -e 's/%MOZ_APP_DISPLAYNAME%/$(MOZ_APP_DISPLAYNAME)/' > \
 	  $(FINAL_TARGET)/../updater.ini
 endif
 endif
 
 ident:
 	@printf 'fx_revision '
 	@$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \
-	    $(STAGEDIST)/application.ini App SourceStamp
+	    '$(STAGEDIST)'/application.ini App SourceStamp
 	@printf 'buildid '
 	@$(PYTHON) $(topsrcdir)/config/printconfigsetting.py \
-	    $(STAGEDIST)/application.ini App BuildID
+	    '$(STAGEDIST)'/application.ini App BuildID
 
 # test target, depends on make package
 # try to repack x-test, with just toolkit/defines.inc being there
 l10n-check:: INNER_UNMAKE_PACKAGE=true
 l10n-check::
 	$(RM) -rf x-test
 	$(NSINSTALL) -D x-test/toolkit
 	echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -492,16 +492,19 @@ offlineApps.manageUsageAccessKey=S
 # LOCALIZATION NOTE (canvas.siteprompt): %S is hostname
 canvas.siteprompt=Will you allow %S to use your HTML5 canvas image data? This may be used to uniquely identify your computer.
 canvas.notAllow=Don’t Allow
 canvas.notAllow.accesskey=n
 canvas.allow=Allow Data Access
 canvas.allow.accesskey=A
 canvas.remember=Always remember my decision
 
+# Spoof Accept-Language prompt
+privacy.spoof_english=Changing your language setting to English will make you more difficult to identify and enhance your privacy. Do you want to request English language versions of web pages?
+
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site.
 identity.identified.state_and_country=%S, %S
 
 identity.icon.tooltip=Show site information
 identity.extension.label=Extension (%S)
 identity.extension.tooltip=Loaded by extension: %S
 identity.showDetails.tooltip=Show connection details
--- a/browser/locales/en-US/chrome/browser/preferences/languages.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/languages.dtd
@@ -10,9 +10,9 @@
 <!ENTITY languages.customize.moveUp.accesskey           "U">
 <!ENTITY languages.customize.moveDown.label             "Move Down">
 <!ENTITY languages.customize.moveDown.accesskey         "D">
 <!ENTITY languages.customize.deleteButton.label         "Remove">
 <!ENTITY languages.customize.deleteButton.accesskey     "R">
 <!ENTITY languages.customize.selectLanguage.label       "Select a language to add…">
 <!ENTITY languages.customize.addButton.label            "Add">
 <!ENTITY languages.customize.addButton.accesskey        "A">
-
+<!ENTITY languages.customize.spoofEnglish               "Request English versions of web pages for enhanced privacy">
--- a/browser/locales/searchplugins/morfix-dic.xml
+++ b/browser/locales/searchplugins/morfix-dic.xml
@@ -1,38 +1,18 @@
 <!-- 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/. -->
 
 <!-- milon.morfix.co.il MozSearch plugin
      Compatible with Mozilla Firefox 2+
      Tomer Cohen, September 2006 - http://mozilla.org.il             -->
-   
+
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
   <ShortName>מילון מורפיקס</ShortName>
   <Description></Description>
   <InputEncoding>UTF-8</InputEncoding>
-  <Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+g
-vaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1gkdCRktdt52QwAAACR0
-RVh0Q29tbWVudABUb21lciBDb2hlbgp0b21lcmNAZ21haWwuY29tUau7swAAAxxJ
-REFUOMttk91rm2UYxq/7eZ/3fZO8SZqlcflct2at/YjokDJBPNAyHW4Hgw31wP9B
-kP0Bngw2QWHsxFOPdjJPhG1MEaqIddOWDbqxOmrStE3atGk+lrxJ3o/neTxYtlXx
-hpv74Oa64Oa+foQDpZQCAALAAPDhJADiQCsieqGh/4g5gCCAmO85cdFvhZVUJM0R
-O2AGGsTQJFAPgP/chA6IdQBxr7MzIde+m9Vqi+OsUxwl4ULo0dZecK4kXvvsYTqb
-faJpWuO5CQeAXrerBa1QzKuvnmD3L79n7P55grqVDKRrASAO6qfZcmV/69bY2utf
-BkLH37jfdBpNAJKqb09TMZM2kl9cyR+tfnNB37pzBv3GEQCaAgQ9O8sA4ChQ8fF+
-9IdL5vTNTlquCiUG2sUwo+ZGVTemvUzSXniX7NqbAPi+jcqDCisZnLXCpjIARAkI
-jZiOXyw1d5YIVWn6fQYACsCxSFUjuxYFoA181H4pmUsL1uc//n7k65/7RvoJABtA
-iGvInrS8XGptKjxWn2EcAJ6mQiogGgTpAkC372GjTIWVs+c/fliYmR11/thN4dFX
-efiuxYBgBH7o7NxpY/7UB8QBQHZcLJRT0zFnaqrbE/H1QWyCItnruUzW1Q2DfCsB
-MAOAKxXg+hKOyTX/cDKp+A1KKxUPQ7fzj47K1pzrdNHCYeTe+vSiETAPOY1iRt+7
-Ow7hWgAcV2Bvs2PVzEjcJkaSz9+4Tp7jUDqTIfb420uB7dtzuqoaJq721E/XFEkn
-Rp1yGsLVQahtNdVff4vJ4ju5XDcSjChemJkBAO5sL0eM+vejrL2SgvQPvUypYlBK
-Athp2Hhwd8u6xyfPrR8fHx+AoPgwksrr7Qv96UYPwm0PGWAApFQYeAL17bZa/W3T
-uree+GT59Pz79WQyKRhj4MMvuvKVk5srzfyi2N3dAxAeGvgKsEvt4HZZ5Ivs1Y/W
-z5z6sD5bKDicc3WQBVJSBsqVamxp8ddou93ShzsJYo5hjdj5Y2OdyYmJQSKREJqm
-qf+jkQAw3/eZEPIlrgTFGJOapiki+hfKAPAPFp9sbyN2GzAAAAAASUVORK5CYII=
-</Image>
+  <Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIACzAgAAJgAAACAgAAABACAAFQYAANkCAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAJ6SURBVDiNpZI7aN91FMU/9/H952URunQQE0qKhpYiFtFFRQe36iJa6upcRIoIulRXncW1gw5KKJWCIKhRFB9FEQpW0LZ2iAEflERo8s/ve+91+MXn6oWz3MfhcM6F/1kC8PD+k0ci67mqWgEoles15Vlb4IClPFJVc5VxVSIvrv2++t2/CB649cQxETknyGKSiAhmhqpcUtHbQPZXFZlBJb9l9nO70zzz2fbb6wC2NHfkNUHvKwJVxd1wV9zbATObUxNUBRkxr2LHROvBg/N3vPvj9uUtlWJBqL1jx5vTWqM1o02cNmkjWsPdEAP3dk9lexnOqC3NHl4X1ePmNu9u42JzmjuTyQzNG2b+l2MFVCVZHFqc3bhg16ffXjt4y9FNNz8+ynd84kzaDGJyUdReQvIbUb0LZLYqqSooZqrY9JHYNkQFVUVNcXPMdcPdTp796pUrAE8efmbTzV/tOiCqIEEVTyiAaJmIgMiYgjqm7fuzjy5c+zsv+RxJRHTMHhBhWf/7GFVFUSTFQ2v8Y173C8rowl4HthwgAkyTyqSyyEhSOmtrZ/pTd59eyl5PZ/ZTPTqZSY4eoFXvjPZGkCZkJpFB9KCKfuLQqeVhd7hgZSt96PQeI3kkVbWTwus+SinJSEICRFAGtGIQ5DEbbGU7bpKR9AgiAlKIzPN24+cvHCCrtiSCTlGAlRKhN7Eqw+kx7KlLCBhi93Lm8MLHrHUF2Gfty4IPK4XowXQYhmF3562hOL+zu7Peh6QPQQxJ7/2Dkunjn2yuXgUwgB+2L00X9c73cG7PzH2Qp9//5c03rmx+fWN5/uinEf3eHpER8aJ4PP/Rr6s//ZnEH4VnUn6KMJ1eAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAF3ElEQVRYhc2WW4idVxXHf2vtc6bJ1FwmVhqoSVD70DKpEPpgpRpDUaQ+iLZMaksf+tAYoUIQERQRCr4Z1IdQhYpGLC2paUuJFcT2YaSTlkBCq21iKcHamLb2emYaE2bmfHv9ffiuZ86M9k03Z/Nd+M76/9Zlr73hfzys+7B78627IH1ZaKewywwEIDCHokDHrprngaMczQB7PjKzVeHbXNqeIclsyYe8nq14V/3h28ffOXbhAwHcvOXmjRdjww8Fdxs+CSqVux+a4WZZxg9Stoci8R0z+yLoasNQhSqJULwH9pIpThTKDx1fePTkmgB72NOLqSvvc9LXQ3kV6UrcHTPHTAtYOt8znxYiFM13UglggFTGL8dwPuA3Qy8OnnjvsfNjtndv3rsb7EnBBCvEW2HD3DAMd8PMERrJXxkAIbUgklAEyAmGLwXsmxscnetquOArYGuK1zO5k1L9bCR3PKVmpua+811yLDmywOhf4/iRz07tvXEUQLpmZdi7wmMzeSWSGqiUKsBqNt91/mcu3NJVFvaLz6yf2d5qmS2Oek4Tcq9n8tY7r711Ui9VIKkBa+YIeGlPBO5+rU3Y9xoAxDNeZbMUXulx6qQhNcZT530TkZRwL4HSGhGUCdzu/NyWmU+XAOv0aFi8YeYtRLnk2vsqEmVUWvFaNKVxb807NupCrmYifSiLuwDSqxfPzG+/bOc5Qzd56k16lVer89mNQEpj4e2nPsn7pJRGUzi2RoyyT1RvFFMf3XT9kQRwbun0mR0bpl90/EsppfVN3n2ld3UNlDCYXcD5nSmOyXTajA+7+VRJ0hFXi1E3K7ApDYdP9hrI7C+QWDCzqdoDG0mDN6FNnhD8tdfr7dvyr8WTh84eWgK4Zdc9O3pL6UfJ015JzX+rsNSVVv9Izk1e609k77uZd0VHxKtrWVx2KfXZ/+DzB4/X4gCPPXffq/1l7RM65Z5K4BX1VDc0zJD8hgaAdZ24WZlMs9F3hpG8j9z+cH7jpmdZZTx49tD75nY4eV0TlceVTZqLMHS1j/y7EqFTQnVR1V4kT5jshdnZe4vVAKpxRgq8Xlkdm3UarNwqtvraNjrej92vsmN1Ryh11NYwYwC9/wKgzp2q5/+sDYBzveFVtdMuvRGTwmBpFECtkLrvyi3uA8nPTH9rC2Z3hnID0AVvlyII3mwBFivZUmt0S5UIgSKIHBSxevrv2PXtHcn0c5PtjIgGvuKvDSNRZ+hU0wcW18FEBrmw5nTTQrV7fYyF4WufPPApw2/Pw3yLiW05Su9VN52xCFYQZk+1jWgR1FNLHEIWRBhmAVGecrIHhdoI3Hbdgf0KO5gsbcixTI5MhIgIIgKFCJVT1bNkiFggp04nBEIZizorUXWvoD50mRmRS8MAM9fe8wUV8VPM1i9psRSLKFNWAYRqkCghJEyGFLOX3t/095EiVBPqlfmvjNUCIcG9noNvIl9f5KIBU6gCqY9k4/ayigL88CnuH/ZGAUoxCwMcLEbaklkJUOScv/qJf16Rs67D2oNpGe42At1I1NEpvdfjT8/rCYAGwBlKWCgEXhabwgiEVWvGzMgWZEUeZp/suyZFbgpTtB7X4Y6o3wXIyMqvKdv34bd5FMAuHxRcmkdUOe40zno7MCMXBaZ4u7gYA98YC45dKUVzKo6q4luAaKKhiIsB35i78PDLrePVmJ3/9bzLnil3KY1UcoTIVRiXY3kBsxNPDe5fyDmfVEDOmci5vVb9oilYQeRYCPLdc4OHn+imfaQIe/CTUD7npA59bVgoIIr41e//8csXAYbBj4fF8rsKI+cgRzTiOedSPIyIOK0obnt68MgRVozUfXhl6fRg27rpP2PcYPgVTUMuLzmTH/Dh8nf/tnhmCeCVC8+/8bHJ6bOIGyU2Nqug6iWhGEj5cM9j/5/mH3lupTis2OLqsefyma3FhN9lituBKbC/mPEzH7z1x1lmx/rw5zfd8XH1OBDErZI2h3hZ4nH1ODL3Tpvv/8vxb8af5VHCgdKPAAAAAElFTkSuQmCC</Image>
   <Url type="text/html" method="GET" template="http://milon.morfix.co.il/default.aspx" resultdomain="morfix.co.il">
     <Param name="q" value="{searchTerms}"/>
   </Url>
   <SearchForm>http://milon.morfix.co.il/</SearchForm>
 </SearchPlugin>
--- a/browser/locales/searchplugins/rakuten.xml
+++ b/browser/locales/searchplugins/rakuten.xml
@@ -1,16 +1,16 @@
 <!-- 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/. -->
 
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>楽天市場</ShortName>
 <Description>楽天市場 商品検索</Description>
 <InputEncoding>EUC-JP</InputEncoding>
-<Image width="16" height="16">data:image/x-icon;base64,R0lGODlhEAAQALMOAOefn9psbMswMMMQEPjj4/PPz++/v+OPj99/f9+AgMcgIM9AQOuvr////wAAAL8AACH5BAEAAA4ALAAAAAAQABAAAARf0EnJ0FqIze2YemCoaBsQniEwMeIloOQHJk1NvKDSnTRQNIZThddYPAiNk4UYCDQIpwux1ghEEUTGc6BkhWiLA1DokD3Ag5/1odvlFlzFAkdymFAn1caDH3FWFhh1EhEAOw==</Image>
+<Image width="16" height="16">data:image/x-icon;base64,AAABAAIAEBAAAAEAIACYAgAAJgAAACAgAAABACAARwUAAL4CAACJUE5HDQoaCgAAAA1JSERSAAAAEAAAABAIBgAAAB/z/2EAAAJfSURBVDiNdZFLSJRhFIaf8/9jOoFa3tLEBC/JCEU5yUgGahIS5TIqiCxTNIiKpCiiVWJBpV0Iw9vGnbMqokW6kYRJZCK6qRNmXhJprMzQ0XH8vxZOo47T2Z7zPuc95xWCytV4J3ug6WE5IoWiSFWiDIEhFJ3Z1TWt6VUXR1fPS0DoehE+cLT6NnBOBFMwGADFXNweW11eU0ediKgAwC9+KkJJSOE6jmordY5WiIjSAQ70vLorwgkApQyiMi1EZWSxMXkb4THxLEx9B8MAEb9t2f3D6fjd+ubTaxl83GBxtTS8R9ABDK+X3PttJBUfDGycGx/BUXUMz+TEigulZjLKr6TpJZP91xDyA42lJZJLSjEnJNF3qZJf75wkFR8iMiOLsWd2RNeXXYiEa5oxblJQEPjkqjJ8i7gd3SzNe9iybz/Rlp2Iae1vp5yOIpMoUglBEE0nMi2TiMStbN6Vy5/P/SjfIhK2YdWQpJiWc15PCIuKpsDeBcDizDQf62+imcKC4zA0UXwJcQE+zxyDT+5heL3Mfhtj+sPbQAorBhjWELpCAYyFeQYb6+l/dItNlh1Yzl/F8PnWzMTm2Dq17KqaZqXUfDBaNB3NZGKovQm3o5v0srMk5BeCUn73uDNPXegQAEflketTzt5aAKUU5oRE9Agzs2NfAdDDIzAnJePzzOGZnEBEiLHaTuY329vFL9KeW1NbgNOhzgmuWKutdm+z/QaAtuxYjMPOkTMxVttlpdTM/4QK3LE5eWX/xMD6/FytD+LdvT3Hf/b1FikhRcBAMRxrtb3cXlFjj8vLW7PgL85j5b2Pva1GAAAAAElFTkSuQmCCiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAFDklEQVRYha2Wa2xUVRSFv33ubadT2lLCQwSngwQRCYVCiwIqSIgPEHkkPDQQoyYYjBgjCBgfGFCDkT/8UjCRVE0UwYhUXlEiEiJRbCkgNEKgQikoD0npTDvtzL13+6N0+hqmM4SVTHLu3mevte6dk7O3kCK+n3BvrmluHI/HQ4KMVNGgQL6qeMBVhDNApYi3v7yitnw1eKnwSncbto0KDrIs7yVgviDBVEgVPWJUNjl+f+msgydDt2Rg3yTscCiwQpHXBXqlItzVCKeM8Pb0ipqtaRkoKx5cgBcrFZHJtyKcABsiTTmvzquqinZroKw4OAxPd4ow+DaJ34DuznSicx8/dqmhfdS0f9hVOPAuUW/X7RcHkKlRK3PzvknY7aNW66K8uDjjuolsF6Soa63gOTGchga8aHOXn8aigCCW1aW0E8/Q5mhe1tf/1O+Nh1oXZcWBN0RlbaI6L9pMr1ElFMx6uktOXZdwTTWXD/xM/akqLH82YkwClhv7QfG8yTOO1O6PG9hWMiBgu9YJRHITFTnhEIPmPkvR++tvSuxGGjnz+Qb++ngdYtnJTSiHKiprxq8GzwBYnrXkZuJA/C9IBsufzdDFSxmxYg1uUyTpXhHuLykOPgFgtgzvmwOyIGlFJzjhELU7vuX89m+4+NMOYvXX47nBCxfRb8IjuJHkJvD0RQDb588aL8rAdAxELv9LxfLFuLEYiJA/vJBxn3xF9oAAAIHZz3D5133JSUQn7Srq39cYZWI64gBiDHZOLhm5eWTm9aTuz0rObi6N53vecx9Wdg/US9YOJN+xMkYbRUama6CrIYtYuL7t2baSHsL4PkyhDRpMoSd1gHoeTjiE5zioemTm9SIwY34833ihBqexAdufnZzI8wpsID8tdSAzL5+7Fy5CHQfL72fg1Nn0Khwdz1/YUwZJP38rJM+WFPt2e/j69GXkWwnvLK4dLefinu1Y3b09oKKeAbmaroHEbMqV3w9QvnQRbiya2hlQrhmgOl0tz4nRWHuOWLht1lDg7JYvCFWfwvJlpUZkqDYoh9M10HjhPL/MmcKBBU/SdPkSACJC4cr3yBsyDLepKSUeceWoUfW6uTESQBUvFqPu+BGOrlkOqgBk9evPyFUfgWo3dwAAFxujkWMmt2dtJXAiXQ9iDBl5Pbn4Yxlnvvw0Hr/j4SkMeeFlnHDSURBV3Tmv6krYTN6PI8rGdA20ws7uQdX6D6g7fiQeG7ZkJb3HTsBpbEhYo6BGZCPcmIgcv79U0XNJ7CJW2yBjZfnja7Fs3KYIlateiwtaPh8l6zaS1a8/6jhd6AT9bvrhmoqW9Q38UFQwB0PC6dVzHHoEgvQe/QAAsXCISwf2tlw20kLhNkXoM/ZBehQMQlUxGZn898dBwjV/Y+w286pabxvGTKs4f6aDAYCy0YFPRWRRV8uCxmK4zS2nW4xJeNG4zU0d3tjK8iO2HT+kAB76/MzD50tbnzsMiHbI94qbGw0iPNaBWRWxbWw7J9EHahP0ZYGvU7CduCprZ1a2iUOnqXja6dPNrt8/R1V3JlW6BaiydkZlzZud4wnb4L5J2KH6wIcisuw2SNehZtlTlec2Jcom7cPbiwOPGo81iIy7JWllq2eZd2aVnz15sz3dDgLvgikZE5yp6HOCTgRJ2r5VqRV0jyfms5mHz/3WHX9ak8ju4oI7Y64UYXSEqAYVyRFQhGsK1YIes2zf0WmHTtd3z9aC/wGRJv+fU1BOjgAAAABJRU5ErkJggg==</Image>
 <Url type="text/html" method="GET" template="https://pt.afl.rakuten.co.jp/c/013ca98b.cd7c5f0c/" resultdomain="rakuten.co.jp">
   <Param name="sitem" value="{searchTerms}"/>
   <Param name="sv" value="2"/>
   <Param name="p" value="0"/>
 </Url>
-<SearchForm>http://www.rakuten.co.jp/</SearchForm>
+<SearchForm>https://www.rakuten.co.jp/</SearchForm>
 </SearchPlugin>
--- a/browser/locales/searchplugins/yandex-az.xml
+++ b/browser/locales/searchplugins/yandex-az.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Yandex</ShortName>
 <Description>İnternetdə axtarış üçün Yandexdən istifadə edin.</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-ru.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.ru/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://yandex.ru/yandsearch" resultdomain="yandex.ru">
+<Url type="text/html" method="GET" template="https://yandex.ru/search" resultdomain="yandex.ru">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.ru/</SearchForm>
--- a/browser/locales/searchplugins/yandex-by.xml
+++ b/browser/locales/searchplugins/yandex-by.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Яндекс</ShortName>
 <Description>Пошук з дапамогаю Яндекс</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-ru.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.by/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://yandex.by/yandsearch" resultdomain="yandex.by">
+<Url type="text/html" method="GET" template="https://yandex.by/search" resultdomain="yandex.by">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.by/</SearchForm>
--- a/browser/locales/searchplugins/yandex-kk.xml
+++ b/browser/locales/searchplugins/yandex-kk.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Яндекс</ShortName>
 <Description>Воспользуйтесь Яндексом для поиска в Интернете.</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-ru.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.kz/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://yandex.kz/yandsearch" resultdomain="yandex.kz">
+<Url type="text/html" method="GET" template="https://yandex.kz/search" resultdomain="yandex.kz">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.kz/</SearchForm>
--- a/browser/locales/searchplugins/yandex-ru.xml
+++ b/browser/locales/searchplugins/yandex-ru.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Яндекс</ShortName>
 <Description>Воспользуйтесь Яндексом для поиска в Интернете.</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-ru.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.ru/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://yandex.ru/yandsearch" resultdomain="yandex.ru">
+<Url type="text/html" method="GET" template="https://yandex.ru/search" resultdomain="yandex.ru">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.ru/</SearchForm>
--- a/browser/locales/searchplugins/yandex-tr.xml
+++ b/browser/locales/searchplugins/yandex-tr.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Yandex</ShortName>
 <Description>Yandex Türkiye arama motoru</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-en.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.com.tr/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://yandex.com.tr/yandsearch" resultdomain="yandex.com.tr">
+<Url type="text/html" method="GET" template="https://yandex.com.tr/search" resultdomain="yandex.com.tr">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.com.tr/</SearchForm>
--- a/browser/locales/searchplugins/yandex-uk.xml
+++ b/browser/locales/searchplugins/yandex-uk.xml
@@ -5,17 +5,17 @@
 <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
 <ShortName>Яндекс</ShortName>
 <Description>Пошук Яндексом.</Description>
 <InputEncoding>UTF-8</InputEncoding>
 <Image width="16" height="16">resource://search-plugins/images/yandex-ru.ico</Image>
 <Url type="application/x-suggestions+json" method="GET" template="https://suggest.yandex.ua/suggest-ff.cgi">
   <Param name="part" value="{searchTerms}"/>
 </Url>
-<Url type="text/html" method="GET" template="https://www.yandex.ua/yandsearch" resultdomain="yandex.ua">
+<Url type="text/html" method="GET" template="https://www.yandex.ua/search" resultdomain="yandex.ua">
   <MozParam name="clid" condition="purpose" purpose="searchbar"   value="2186618"/>
   <MozParam name="clid" condition="purpose" purpose="keyword"     value="2186621"/>
   <MozParam name="clid" condition="purpose" purpose="contextmenu" value="2186623"/>
   <MozParam name="clid" condition="purpose" purpose="homepage"    value="2186617"/>
   <MozParam name="clid" condition="purpose" purpose="newtab"      value="2186620"/>
   <Param    name="text" value="{searchTerms}"/>
 </Url>
 <SearchForm>https://www.yandex.ua/</SearchForm>
--- a/browser/themes/linux/preferences/in-content/dialog.css
+++ b/browser/themes/linux/preferences/in-content/dialog.css
@@ -1,13 +1,10 @@
 /* - 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 ../../../shared/incontentprefs/dialog.inc.css
 
-label,
-textbox,
-description,
-.tab-text,
-menulist label {
-  font-size: 1.11rem;
+:root > * {
+  font-size: 1.05em;
 }
+
--- a/browser/themes/linux/preferences/in-content/preferences.css
+++ b/browser/themes/linux/preferences/in-content/preferences.css
@@ -1,45 +1,23 @@
 /* - This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../../shared/incontentprefs/preferences.inc.css
 
-html *,
-page *,
-window * {
-  font-size: 1.11rem;
-}
-
-caption label {
-  font-size: 1.27rem;
-}
-
-.tip-caption,
-.help-label {
-  font-size: 1rem;
+:root > * {
+  font-size: 1.11em;
 }
 
 .treecol-sortdirection {
   /* override the Linux only toolkit rule */
   -moz-appearance: none;
 }
 
-.actionsMenu {
-  font-family: "Clear Sans", sans-serif;
-  font-size: 1.25rem;
-  line-height: 22px;
-}
-
 .actionsMenu > .menulist-label-box > .menulist-icon {
-  margin-top: 1px;
   margin-inline-start: 1px;
-  margin-inline-end: 6px;
-}
-
-.actionsMenu > .menulist-label-box > .menulist-label {
-  margin-top: 2px !important;
+  margin-inline-end: 8px;
 }
 
 filefield + button {
   margin-inline-start: -4px;
 }
--- a/browser/themes/osx/preferences/in-content/dialog.css
+++ b/browser/themes/osx/preferences/in-content/dialog.css
@@ -4,22 +4,18 @@
 
 %include ../../../shared/incontentprefs/dialog.inc.css
 
 prefwindow,
 .windowDialog {
   font: message-box !important;
 }
 
-label,
-textbox,
-description,
-.tab-text,
-menulist label {
-  font-size: 1.36rem;
+:root > * {
+  font-size: 1.18em;
 }
 
 caption {
   font: message-box;
 }
 
 .prefWindow-dlgbuttons {
   margin: 0;
--- a/browser/themes/osx/preferences/in-content/preferences.css
+++ b/browser/themes/osx/preferences/in-content/preferences.css
@@ -1,27 +1,16 @@
 /* - 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 ../../../shared/incontentprefs/preferences.inc.css
 
-html *,
-page *,
-window * {
-  font-size: 1.36rem;
-}
-
-caption label {
-  font-size: 1.55rem;
-}
-
-.tip-caption,
-.help-label {
-  font-size: 1.18rem;
+:root > * {
+  font-size: 1.36em;
 }
 
 .actionsMenu > .menulist-label-box > .menulist-icon {
   margin-top: 2px;
   margin-inline-start: 2px;
   margin-inline-end: 8px !important;
 }
 
--- a/browser/themes/shared/fxa/sync-illustration.svg
+++ b/browser/themes/shared/fxa/sync-illustration.svg
@@ -1,59 +1,62 @@
 <!-- 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/. -->
-
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 173.87 156.5">
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 173.9 156.5">
   <style>
-    .cls-1{fill:#eaeaee;}
-    .cls-2{fill:#fff;}
-    .cls-3{fill:url(#linear-gradient);}
-    .cls-4{fill:#f9f9fa;}
-    .cls-5{fill:url(#New_Gradient_Swatch_1);}
-    .cls-6{fill:url(#New_Gradient_Swatch_1-2);}
-    .cls-7{fill:url(#New_Gradient_Swatch_1-3);}
-    .cls-8{fill:url(#New_Gradient_Swatch_1-4);}
-    .cls-9{fill:url(#linear-gradient-2);}
-    .cls-10{fill:url(#New_Gradient_Swatch_1-5);}
+    .st0{opacity:0.1;fill:#0C0C0D;enable-background:new ;} .st1{fill:#FFFFFF;} .st2{fill:url(#SVGID_1_);} .st3{fill:#F9F9FA;} .st4{fill:url(#SVGID_2_);} .st5{fill:url(#SVGID_3_);} .st6{fill:url(#SVGID_4_);} .st7{fill:url(#SVGID_5_);} .st8{fill:url(#SVGID_6_);} .st9{fill:url(#SVGID_7_);}
   </style>
-  <defs>
-    <linearGradient id="linear-gradient" x1="55.45" y1="55.54" x2="128.28" y2="128.38" gradientUnits="userSpaceOnUse">
-      <stop offset="0" stop-color="#ccfbff"/>
-      <stop offset="1" stop-color="#c9e4ff"/>
-    </linearGradient>
-    <linearGradient id="New_Gradient_Swatch_1" x1="-4.48" y1="17.64" x2="182.52" y2="187.14" gradientUnits="userSpaceOnUse">
-      <stop offset="0" stop-color="#00c8d7"/>
-      <stop offset="1" stop-color="#0a84ff"/>
+  <path class="st0" d="M140.9 152h-69c-.6 0-1-.4-1-1s.4-1 1-1H141c.6 0 1 .4 1 1s-.5 1-1.1 1zm-9.3-5.1h-12c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h12c.3 0 .5.2.5.5s-.2.5-.5.5zm-15.7 9.6h-12c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h12c.3 0 .5.2.5.5s-.2.5-.5.5zm-20 0h-3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h3c.3 0 .5.2.5.5s-.2.5-.5.5zm-7 0h-1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h1c.3 0 .5.2.5.5s-.2.5-.5.5zm-10 0h-12c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h12c.3 0 .5.2.5.5s-.2.5-.5.5zm-20 0h-3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h3c.3 0 .5.2.5.5s-.2.5-.5.5zm-7 0h-1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h1c.3 0 .5.2.5.5s-.2.5-.5.5zm-10 0h-12c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h12c.3 0 .5.2.5.5s-.2.5-.5.5zm-20 0h-3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h3c.3 0 .5.2.5.5s-.2.5-.5.5zm-7 0h-1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h1c.3 0 .5.2.5.5s-.2.5-.5.5z"/>
+  <path class="st1" d="M85 20.4h21.3s-6.7-14.9 7.5-16.8c12.6-1.7 17.6 11.3 17.6 11.3s1.5-7.5 9-6.1 12.9 13.3 12.9 13.3h18.6"/>
+  <path class="st0" d="M172.2 18.6h-4c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h4c.3 0 .5.2.5.5s-.2.5-.5.5zm-13 0h-1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h1c.3 0 .5.2.5.5s-.2.5-.5.5zm-5 0h-.8c-.1-.1-.2-.1-.2-.2-.1-.2-.5-1-1.2-2.1-.1-.2-.1-.5.2-.7.2-.1.5-.1.7.2.5.8.9 1.5 1.1 1.9h.2c.3 0 .5.2.5.5s-.2.4-.5.4zm-47.5-.6h-1.3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h.6c-.1-.2-.2-.6-.3-.9-.1-.3.1-.6.3-.7.3-.1.6.1.7.3.3.9.6 1.5.6 1.5.1.3 0 .5-.3.7-.1.1-.2.1-.3.1zm-9.3 0h-12c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h12c.3 0 .5.2.5.5s-.3.5-.5.5zm7.8-5.5c-.3 0-.5-.2-.5-.4 0-.3-.1-.7-.1-1s.2-.5.5-.5.5.2.5.5 0 .6.1 1c.1.2-.1.4-.5.4.1.1.1.1 0 0zm26.2-1c-.2 0-.4-.1-.4-.3-.1-.2-.3-.5-.4-.9-.1-.2 0-.5.2-.7.2-.1.5 0 .7.2.2.3.3.6.5.9.1.2 0 .5-.2.7-.3.1-.3.1-.4.1zm16.1-1.3c-.1 0-.3 0-.4-.1-1.7-1.8-4-3.1-6.4-3.7-1.3-.3-2.6-.2-3.9.2-.3.1-.5-.1-.6-.3-.1-.3.1-.5.3-.6 1.4-.4 2.9-.5 4.4-.2 2.6.6 5.1 2 6.9 4 .2.2.2.5 0 .7-.1-.1-.2 0-.3 0zm-18.8-3c-.2 0-.3-.1-.4-.2-.6-.8-1.3-1.5-2-2.1-.2-.2-.1-.5.1-.7.2-.1.4-.1.6 0 .8.7 1.5 1.4 2.1 2.2.2.2.1.5-.1.7 0 .1-.2.1-.3.1zm-20.5-3.8c-.3 0-.5-.2-.5-.5 0-.2.1-.3.2-.4 1.8-1.3 4-2.2 6.2-2.4 1.9-.3 3.8-.2 5.7.2.3.1.5.3.4.6s-.3.5-.6.4c-1.7-.4-3.5-.4-5.3-.2-2.1.2-4.1.9-5.7 2.2-.2.1-.3.1-.4.1z"/>
+  <path class="st1" d="M172.9 22.4H85c-.6 0-1-.4-1-1s.4-1 1-1h87.9c.6 0 1 .4 1 1s-.5 1-1 1zM.8 37.7h11.9s-3.7-8.3 4.2-9.4c7-1 9.8 6.3 9.8 6.3s.8-4.2 5-3.4 7.2 7.4 7.2 7.4h10.3"/>
+  <path class="st0" d="M13 36.4H1.1c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h11.5c.2-.2.5-.2.7 0l.1.1v.1c.1.3 0 .5-.3.7 0 .1-.1.1-.1.1zm32.9-.2h-3c-.3 0-.5-.2-.5-.5s.2-.5.5-.5h3c.3 0 .5.2.5.5s-.2.5-.5.5zM27 33h-.1c-.3-.1-.4-.4-.3-.6.5-2 2.3-3.5 4.4-3.5.4 0 .7 0 1.1.1 1.9.5 3.6 1.5 4.9 3 .2.2.2.5 0 .7s-.5.2-.7 0c-1.1-1.3-2.6-2.3-4.3-2.7-.3-.1-.6-.1-.9-.1-1.7 0-3.1 1.2-3.4 2.8-.3.2-.5.3-.7.3zm-13.6-4.3c-.3 0-.5-.2-.5-.5 0-.1.1-.3.1-.4.8-.8 1.8-1.3 2.8-1.6.3-.1.6.1.6.4s-.1.6-.4.6c-.9.2-1.7.7-2.4 1.3.1.2 0 .2-.2.2zm7.5-1.3h-.1c-.3-.1-.6-.2-.9-.2-.3-.1-.5-.3-.4-.6.1-.3.3-.5.6-.4.3.1.7.2 1 .3.3 0 .5.3.4.6 0 .1-.3.3-.6.3z"/>
+  <path class="st1" d="M49.9 39.7H1c-.6 0-1-.4-1-1s.4-1 1-1h48.8c.6 0 1 .4 1 1s-.4 1-.9 1zm85.5 37.5h-15.3V60.3c0-4.2-3.4-7.5-7.6-7.5H51.1c-4.2 0-7.5 3.4-7.5 7.5V101c0 1.3.4 2.6 1 3.7-.4.5-.8 1-1 1.6l-6.9 16.1c-.3.7-.5 1.5-.5 2.3v1.1c.1 3.4 2.8 6.1 6.2 6h60v3.2c0 4.1 3.3 7.4 7.4 7.4h25.6c4.1 0 7.4-3.3 7.4-7.4V84.7c.1-4.2-3.2-7.5-7.4-7.5z"/>
+  <path class="st1" d="M50.8 56.5h61.4c2 0 3.6 1.6 3.6 3.5v40.7c0 2-1.6 3.6-3.6 3.6H50.8c-2 0-3.5-1.6-3.5-3.6V60.1c0-2 1.6-3.6 3.5-3.6z"/>
+  <path class="st1" d="M52.7 62.5h57.7c1.2 0 2.1.9 2.1 2.1V99c0 1.2-.9 2.1-2.1 2.1H52.7c-1.2 0-2.1-.9-2.1-2.1V64.6c0-1.1 1-2.1 2.1-2.1z"/>
+  <linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="55.4468" y1="665.5432" x2="128.2768" y2="738.3832" gradientTransform="translate(.02 -609.83)">
+    <stop offset="0" stop-color="#CCFBFF"/>
+    <stop offset="1" stop-color="#C9E4FF"/>
+  </linearGradient>
+  <path class="st2" d="M110.4 63.5c.6 0 1.1.5 1.1 1.1V99c0 .6-.5 1.1-1.1 1.1H52.7c-.6 0-1.1-.5-1.1-1.1V64.6c0-.6.5-1.1 1.1-1.1h57.7"/>
+  <path class="st3" d="M115.7 107.6c-.4-.8-1.2-1.3-2.1-1.2H49c-.9 0-1.7.5-2.1 1.3L40 123.8c-.1.2-.1.5-.1.7v1.1c.1 1.2 1 2.1 2.2 2H121c1.2.1 2.2-.8 2.2-2v-1c0-.3-.1-.5-.2-.7l-7.3-16.3z"/>
+  <linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-4.5021" y1="627.6644" x2="182.4979" y2="797.1644" gradientTransform="translate(.02 -609.83)">
+    <stop offset="0" stop-color="#00C8D7"/>
+    <stop offset="1" stop-color="#0A84FF"/>
+  </linearGradient>
+  <path class="st4" d="M124.9 122.9l-7.3-16.1c-.3-.8-.9-1.4-1.6-1.8 1.2-1.1 1.9-2.6 1.9-4.2V60.1c0-3.1-2.5-5.5-5.6-5.5H50.9c-3.1 0-5.5 2.5-5.5 5.5v40.7c0 1.5.6 3 1.7 4-.9.4-1.6 1.1-2 2L38.2 123c-.2.5-.3 1-.3 1.5v1.1c.1 2.3 1.9 4.1 4.2 4H121c2.3.1 4.2-1.7 4.2-4v-1c0-.6-.1-1.2-.3-1.7zm-1.7 2.6c-.1 1.2-1 2.1-2.2 2H42.1c-1.2.1-2.2-.8-2.2-2v-1.1c0-.2 0-.5.1-.7l6.9-16.1c.4-.8 1.2-1.3 2.1-1.3h64.7c.9 0 1.7.5 2.1 1.2l7.3 16.1c.1.2.2.5.2.7l-.1 1.2zm-75.9-24.7V60.1c0-2 1.6-3.5 3.5-3.5h61.4c2 0 3.6 1.6 3.6 3.5v40.7c0 2-1.6 3.6-3.5 3.6H50.9c-2 0-3.6-1.6-3.6-3.6z"/>
+  <linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-51.3994" y1="590.94" x2="201.6006" y2="840.94" gradientTransform="translate(.02 -609.83)">
+    <stop offset="0" stop-color="#00C8D7"/>
+    <stop offset="1" stop-color="#0A84FF"/>
+  </linearGradient>
+  <path class="st5" d="M94.7 121.8H68.4c-.4 0-.8-.2-.8-.5.3-1.7.5-2.6.8-4.4 0-.2.3-.4.7-.4l25.3-.2c.4 0 .7.2.7.4.2 1.5.4 3 .4 4.5.1.4-.3.6-.8.6zM64 112.4h-3.9c-.3 0-.6.2-.7.4-.1.5-.2.7-.3 1.2s.4.7.9.7h3.8c.3 0 .6-.1.7-.3.1-.5.2-.7.3-1.2s-.3-.8-.8-.8zm9.9 0h-4.1c-.4 0-.7.2-.7.4-.1.5-.1.7-.2 1.2-.1.3.4.6.9.6h3.9c.3 0 .6-.1.7-.4.1-.5.2-.7.3-1.2.1-.4-.3-.7-.8-.6zm20.5 1.4c-.1-.5-.1-.7-.2-1.2 0-.2-.3-.4-.7-.4h-4.1c-.5 0-.9.3-.9.6l.3 1.2c0 .2.3.4.7.4h3.9c.6 0 1.1-.3 1-.6zm-9.9.1l-.2-1.2c0-.2-.4-.4-.8-.4h-3.7c-.4 0-.8.2-.8.4-.1.5-.2.7-.2 1.2-.1.3.3.6.8.6h4.1c.4-.1.8-.3.8-.6zm19.6-.2l-.2-1.2c0-.2-.3-.3-.6-.3h-3.9c-.5 0-1 .3-.9.7l.3 1.2c.1.2.3.3.7.3h3.8c.4-.1.9-.4.8-.7zm-39.1-5h-3.7c-.3 0-.5.1-.6.3-.1.4-.2.7-.3 1.1s.3.6.8.6h3.6c.3 0 .5-.1.7-.3.2-.4.2-.7.4-1.1s-.4-.6-.9-.6zm9.4 0h-3.9c-.4 0-.7.2-.7.4-.1.4-.2.7-.3 1.1s.3.6.8.6h3.8c.3 0 .6-.1.7-.4.1-.4.2-.7.3-1.1s-.3-.6-.7-.6zm19.6 1.4l-.2-1.1c0-.2-.3-.4-.7-.4h-3.9c-.5 0-.9.3-.8.6l.2 1.1c0 .2.3.4.7.4h3.8c.5-.1.9-.3.9-.6zm-9.5.1l-.2-1.1c0-.2-.3-.4-.7-.4H80c-.4 0-.7.2-.8.4-.1.4-.2.7-.3 1.1-.1.3.3.5.8.5h3.9c.5 0 .9-.3.9-.5zm18.7-.1l-.2-1.1c0-.2-.3-.3-.6-.3h-3.7c-.5 0-1 .3-.9.6l.3 1.1c.1.2.3.3.6.3h3.6c.5-.1.9-.4.9-.6zm-48.1 2.4h-3.8c-.3 0-.6.1-.7.4-.2.4-.3.8-.4 1.2-.1.3.4.7.9.7h3.7c.3 0 .6-.2.6-.4.1-.4.2-.8.4-1.2.2-.5-.2-.8-.7-.7zm1.3-3.7h-3.5c-.2 0-.5.1-.6.3-.1.4-.2.7-.4 1.1-.1.3.2.6.7.6h3.5c.3 0 .5-.1.7-.3.2-.4.3-.7.5-1.1s-.4-.7-.9-.6zm50.9 4.1c.1.4.3.8.3 1.1 0 .2.3.3.6.3h3.7c.5 0 1-.3.9-.6-.1-.4-.2-.8-.3-1.1-.1-.2-.4-.4-.7-.3h-3.7c-.5-.1-.9.2-.8.6zm-1.1-3.7c.1.4.2.7.4 1.1.1.2.4.3.6.3h3.4c.5 0 .8-.3.8-.6-.1-.4-.2-.7-.3-1.1 0-.2-.2-.3-.6-.3H107c-.4 0-.9.3-.8.6z"/>
+  <g>
+    <linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-22.6206" y1="563.3309" x2="225.3794" y2="813.3309" gradientTransform="translate(0 -610)">
+      <stop offset="0" stop-color="#00C8D7"/>
+      <stop offset="1" stop-color="#0A84FF"/>
     </linearGradient>
-    <linearGradient id="New_Gradient_Swatch_1-2" x1="-51.35" y1="-19.11" x2="201.65" y2="230.89" xlink:href="#New_Gradient_Swatch_1"/>
-    <linearGradient id="New_Gradient_Swatch_1-3" x1="-22.65" y1="-46.64" x2="225.35" y2="203.36" xlink:href="#New_Gradient_Swatch_1"/>
-    <linearGradient id="New_Gradient_Swatch_1-4" x1="-19.25" y1="-19.23" x2="221.25" y2="199.27" xlink:href="#New_Gradient_Swatch_1"/>
-    <linearGradient id="linear-gradient-2" x1="63.01" y1="47.98" x2="135.85" y2="120.82" xlink:href="#linear-gradient"/>
-    <linearGradient id="New_Gradient_Swatch_1-5" x1="-73.41" y1="91.75" x2="262.92" y2="91.75" xlink:href="#New_Gradient_Swatch_1"/>
-  </defs>
-  <path class="cls-1" d="M140.93,151.83H71.85a1,1,0,0,1,0-2h69.08a1,1,0,0,1,0,2Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-1" d="M131.6,146.72h-12a.5.5,0,0,1,0-1h12a.5.5,0,1,1,0,1Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-1" d="M115.89,156.33h-12a.5.5,0,1,1,0-1h12a.5.5,0,0,1,0,1Zm-20,0h-3a.5.5,0,0,1,0-1h3a.5.5,0,1,1,0,1Zm-7,0h-1a.5.5,0,0,1,0-1h1a.5.5,0,1,1,0,1Zm-10,0h-12a.5.5,0,0,1,0-1h12a.5.5,0,1,1,0,1Zm-20,0h-3a.5.5,0,1,1,0-1h3a.5.5,0,0,1,0,1Zm-7,0h-1a.5.5,0,1,1,0-1h1a.5.5,0,0,1,0,1Zm-10,0h-12a.5.5,0,1,1,0-1h12a.5.5,0,0,1,0,1Zm-20,0h-3a.5.5,0,1,1,0-1h3a.5.5,0,0,1,0,1Zm-7,0h-1a.5.5,0,1,1,0-1h1a.5.5,0,0,1,0,1Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M85,20.23H106.3S99.63,5.33,113.8,3.4c12.63-1.72,17.62,11.27,17.62,11.27s1.5-7.49,9-6.05S153.3,21.94,153.3,21.94h18.57" transform="translate(0.02 0.17)"/>
-  <path class="cls-1" d="M172.23,18.44h-4a.5.5,0,0,1,0-1h4a.5.5,0,0,1,0,1Zm-13,0h-1a.5.5,0,1,1,0-1h1a.5.5,0,1,1,0,1Zm-5,0h-.56a.5.5,0,0,1-.22-.05.54.54,0,0,1-.23-.23c-.11-.22-.53-1-1.2-2.08a.5.5,0,0,1,.85-.53c.53.84.9,1.52,1.1,1.89h.25a.5.5,0,1,1,0,1Zm-47.56-.61h-1.32a.5.5,0,1,1,0-1h.58c-.09-.25-.2-.56-.32-.92a.5.5,0,1,1,1-.31c.3.93.56,1.52.56,1.53a.5.5,0,0,1-.46.7Zm-9.32,0h-12a.5.5,0,1,1,0-1h12a.5.5,0,1,1,0,1Zm7.87-5.48a.5.5,0,0,1-.5-.44c0-.34-.07-.68-.09-1a.5.5,0,1,1,1-.06c0,.33,0,.65.09,1a.5.5,0,0,1-.44.56Zm26.15-1a.5.5,0,0,1-.45-.28c-.12-.25-.27-.54-.45-.87a.5.5,0,1,1,.88-.48c.19.34.34.65.47.91a.5.5,0,0,1-.45.72ZM147.48,10a.5.5,0,0,1-.35-.14,13.18,13.18,0,0,0-6.43-3.72,7.66,7.66,0,0,0-3.88.16.5.5,0,1,1-.32-.95,8.66,8.66,0,0,1,4.38-.19,14.15,14.15,0,0,1,6.93,4,.5.5,0,0,1-.35.86Zm-18.8-3a.5.5,0,0,1-.39-.19,19.12,19.12,0,0,0-2-2.13A.5.5,0,0,1,127,4a20.34,20.34,0,0,1,2.1,2.24.5.5,0,0,1-.39.81ZM108.19,3.27a.5.5,0,0,1-.31-.89A12.51,12.51,0,0,1,114.1,0a16.12,16.12,0,0,1,5.66.2.5.5,0,1,1-.22,1A15.06,15.06,0,0,0,114.23,1a11.54,11.54,0,0,0-5.73,2.18A.5.5,0,0,1,108.19,3.27Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M172.85,22.23h0L85,22.21a1,1,0,1,1,0-2h0l87.87,0a1,1,0,1,1,0,2Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M.76,37.55H12.62s-3.71-8.29,4.17-9.36c7-1,9.8,6.26,9.8,6.26s.83-4.16,5-3.37,7.16,7.41,7.16,7.41H49.08" transform="translate(0.02 0.17)"/>
-  <path class="cls-1" d="M13,36.23H1.13a.5.5,0,0,1,0-1H12.59a.5.5,0,0,1,.8.18l0,.12a.5.5,0,0,1-.46.7Zm32.86-.15h-3a.5.5,0,1,1,0-1h3a.5.5,0,1,1,0,1ZM27,32.86l-.14,0a.5.5,0,0,1-.34-.62,4.66,4.66,0,0,1,4.4-3.55,6,6,0,0,1,1.11.11,9.37,9.37,0,0,1,4.88,3,.5.5,0,1,1-.73.69,8.42,8.42,0,0,0-4.34-2.73,5,5,0,0,0-.93-.09,3.63,3.63,0,0,0-3.44,2.84A.5.5,0,0,1,27,32.86ZM13.4,28.53a.5.5,0,0,1-.36-.85,6,6,0,0,1,2.82-1.57.5.5,0,1,1,.26,1,5,5,0,0,0-2.36,1.29A.5.5,0,0,1,13.4,28.53Zm7.52-1.35-.15,0a7.39,7.39,0,0,0-.91-.23.5.5,0,0,1,.19-1,8.41,8.41,0,0,1,1,.26.5.5,0,0,1-.15,1Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M49.84,39.56H1a1,1,0,1,1,0-2H49.84a1,1,0,1,1,0,2Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M135.34,77H120.06V60.18a7.56,7.56,0,0,0-7.55-7.55H51.08a7.56,7.56,0,0,0-7.55,7.55v40.74a7.55,7.55,0,0,0,1,3.67,5.88,5.88,0,0,0-1,1.6l-6.89,16.12a5.73,5.73,0,0,0-.46,2.26v1.05a6.1,6.1,0,0,0,6.22,6h60v3.19a7.45,7.45,0,0,0,7.45,7.45h25.55a7.46,7.46,0,0,0,7.45-7.45V84.49A7.46,7.46,0,0,0,135.34,77Z" transform="translate(0.02 0.17)"/>
-  <rect class="cls-2" x="47.3" y="56.55" width="68.54" height="47.84" rx="3.55" ry="3.55"/>
-  <rect class="cls-2" x="50.64" y="62.52" width="61.86" height="38.58" rx="2.09" ry="2.09"/>
-  <path class="cls-3" d="M110.38,63.35a1.09,1.09,0,0,1,1.09,1.09V98.83a1.09,1.09,0,0,1-1.09,1.09H52.71a1.09,1.09,0,0,1-1.09-1.09V64.44a1.09,1.09,0,0,1,1.09-1.09h57.67" transform="translate(0.02 0.17)"/>
-  <path class="cls-4" d="M115.72,107.47a2.23,2.23,0,0,0-2.06-1.23H49a2.22,2.22,0,0,0-2.07,1.27L40,123.63a1.76,1.76,0,0,0-.14.69v1.05a2.1,2.1,0,0,0,2.22,2H121a2.1,2.1,0,0,0,2.22-2v-1a1.77,1.77,0,0,0-.16-.73Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-5" d="M124.88,122.78l-7.34-16.14a4,4,0,0,0-1.64-1.79,5.54,5.54,0,0,0,1.91-4.18V59.93a5.56,5.56,0,0,0-5.55-5.55H50.83a5.56,5.56,0,0,0-5.55,5.55v40.74a5.53,5.53,0,0,0,1.74,4,4,4,0,0,0-2,2l-6.89,16.12a3.75,3.75,0,0,0-.3,1.48v1.05a4.1,4.1,0,0,0,4.22,4H121a4.1,4.1,0,0,0,4.22-4v-1A3.75,3.75,0,0,0,124.88,122.78Zm-1.66,2.59a2.1,2.1,0,0,1-2.22,2H42.09a2.1,2.1,0,0,1-2.22-2v-1.05a1.76,1.76,0,0,1,.14-.69L46.9,107.5A2.22,2.22,0,0,1,49,106.24h64.69a2.23,2.23,0,0,1,2.06,1.23l7.34,16.14a1.77,1.77,0,0,1,.16.73ZM47.27,100.66V59.93a3.55,3.55,0,0,1,3.55-3.55h61.43a3.55,3.55,0,0,1,3.55,3.55v40.74a3.55,3.55,0,0,1-3.55,3.55H50.83A3.55,3.55,0,0,1,47.27,100.66Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-6" d="M94.71,121.68H68.43c-.43,0-.81-.25-.79-.53.33-1.7.48-2.59.82-4.38,0-.24.3-.43.68-.43l25.34-.19c.38,0,.66.17.66.41a43.55,43.55,0,0,1,.44,4.53C95.55,121.4,95.14,121.68,94.71,121.68ZM64,112.2H60.07c-.34,0-.61.15-.66.36-.11.48-.17.72-.28,1.2s.39.66.91.66l3.82,0c.34,0,.61-.14.67-.35.13-.47.2-.71.34-1.19S64.53,112.2,64,112.2Zm9.85,0-4.07,0c-.38,0-.69.17-.73.4-.09.48-.13.71-.21,1.18-.05.31.37.61.87.6l3.95,0a.73.73,0,0,0,.74-.4c.12-.47.18-.7.31-1.18C74.76,112.46,74.35,112.16,73.83,112.16Zm20.57,1.46c-.06-.46-.1-.69-.16-1.16,0-.23-.33-.39-.71-.39l-4.07,0c-.52,0-.95.3-.88.61l.26,1.16c0,.23.35.39.73.39l3.95,0C94,114.22,94.45,113.93,94.4,113.63Zm-9.93.09-.2-1.17c0-.25-.38-.43-.79-.43l-3.68,0c-.41,0-.76.19-.81.44-.1.47-.15.71-.25,1.17-.06.3.32.57.8.56l4.09,0C84.13,114.28,84.52,114,84.47,113.72Zm19.61-.19-.23-1.15c0-.2-.3-.34-.64-.34H99.3c-.54,0-1,.33-.93.65l.3,1.15c.05.2.32.34.65.34l3.82,0C103.67,114.17,104.14,113.84,104.08,113.53Zm-39.11-5H61.26a.64.64,0,0,0-.64.33c-.13.44-.2.66-.32,1.09s.33.61.83.61h3.62a.66.66,0,0,0,.65-.33c.15-.44.23-.66.39-1.09S65.49,108.53,65,108.53Zm9.36,0H70.46c-.37,0-.67.16-.72.37-.11.44-.16.66-.26,1.1s.33.56.81.56h3.77a.72.72,0,0,0,.72-.37c.13-.44.2-.66.33-1.1S74.83,108.5,74.33,108.5Zm19.61,1.4-.16-1.1c0-.22-.31-.37-.68-.37H89.23c-.5,0-.91.28-.85.58l.24,1.09c0,.21.34.37.7.36h3.77C93.57,110.46,94,110.19,93.94,109.9Zm-9.49.06-.17-1.1c0-.23-.35-.4-.74-.4H80c-.39,0-.73.18-.79.41-.11.44-.17.66-.27,1.1-.07.28.29.53.76.53h3.92C84.12,110.5,84.5,110.25,84.45,110Zm18.67-.13-.22-1.1c0-.2-.28-.33-.61-.33h-3.7c-.52,0-1,.32-.88.62l.29,1.09c.05.19.3.32.63.32h3.62C102.74,110.44,103.19,110.13,103.13,109.83Zm-48.06,2.4H51.32a.71.71,0,0,0-.69.36,9.8,9.8,0,0,0-.39,1.21c-.09.33.37.67.88.67l3.7,0c.33,0,.58-.15.63-.36a7.82,7.82,0,0,1,.39-1.2C56,112.57,55.59,112.23,55.06,112.23Zm1.32-3.68h-3.5a.57.57,0,0,0-.59.32,10,10,0,0,1-.37,1.09c-.12.3.23.61.7.61h3.45a.72.72,0,0,0,.67-.33c.2-.44.29-.66.45-1.09S56.87,108.55,56.38,108.55Zm50.9,4.12a7.26,7.26,0,0,1,.34,1.14c0,.2.29.33.62.33l3.7,0c.51,0,1-.32.9-.63a9.34,9.34,0,0,0-.34-1.14.68.68,0,0,0-.67-.34h-3.74C107.57,112,107.17,112.36,107.28,112.67Zm-1.1-3.66a8.86,8.86,0,0,0,.36,1.09.66.66,0,0,0,.64.32h3.45c.47,0,.84-.3.75-.6a7.35,7.35,0,0,1-.27-1.1c0-.2-.25-.33-.56-.33H107C106.55,108.39,106.1,108.71,106.18,109Z" transform="translate(0.02 0.17)"/>
-  <circle class="cls-7" cx="82.57" cy="59.42" r="1.15"/>
-  <rect class="cls-2" x="105.11" y="79.96" width="34.45" height="59.17" rx="4.45" ry="4.45"/>
-  <path class="cls-8" d="M135.09,80.79a3.45,3.45,0,0,1,3.45,3.45v50.28a3.45,3.45,0,0,1-3.45,3.45H109.53a3.45,3.45,0,0,1-3.45-3.45V84.24a3.45,3.45,0,0,1,3.45-3.45h25.55m0-2H109.53a5.45,5.45,0,0,0-5.45,5.45v50.28a5.45,5.45,0,0,0,5.45,5.45h25.55a5.45,5.45,0,0,0,5.45-5.45V84.24a5.45,5.45,0,0,0-5.45-5.45Z" transform="translate(0.02 0.17)"/>
-  <rect class="cls-2" x="109.42" y="84.79" width="25.82" height="45.33" rx="1.72" ry="1.72"/>
-  <path class="cls-9" d="M133.49,85.62a.72.72,0,0,1,.72.72v41.88a.72.72,0,0,1-.72.72H111.12a.72.72,0,0,1-.72-.72V86.34a.72.72,0,0,1,.72-.72h22.37" transform="translate(0.02 0.17)"/>
-  <path class="cls-10" d="M82.85,97.67a2.47,2.47,0,0,1-1.55-.55L66.32,85a12.45,12.45,0,0,1-3.66-3.66,12.77,12.77,0,0,1,1.67-16,12.69,12.69,0,0,1,17.95,0,.67.67,0,0,0,.49.22.68.68,0,0,0,.48-.21A12.71,12.71,0,1,1,99.26,85L84.41,97.12A2.49,2.49,0,0,1,82.85,97.67Zm41,23.7,11-9a9.67,9.67,0,1,0-12.19-14.9.24.24,0,0,1-.17.08.25.25,0,0,1-.18-.09,9.66,9.66,0,0,0-13.66,0,9.72,9.72,0,0,0-1.27,12.15,9.46,9.46,0,0,0,2.78,2.78l11.09,9a2.09,2.09,0,0,0,2.63,0Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M73.28,62.67a11.66,11.66,0,0,1,8.29,3.44l0,0a1.66,1.66,0,0,0,1.18.5,1.68,1.68,0,0,0,1.19-.5l0,0A11.71,11.71,0,1,1,98.7,84.16L83.78,96.34a1.48,1.48,0,0,1-.93.33,1.45,1.45,0,0,1-.92-.33l-15-12.16a11.41,11.41,0,0,1-3.41-3.4A11.82,11.82,0,0,1,65,66.08a11.65,11.65,0,0,1,8.25-3.41m0-2h0a13.6,13.6,0,0,0-9.66,4,13.77,13.77,0,0,0-1.8,17.22,13.4,13.4,0,0,0,3.91,3.92L80.67,97.9a3.47,3.47,0,0,0,4.35,0L99.89,85.77A13.71,13.71,0,1,0,82.77,64.49a13.62,13.62,0,0,0-9.49-3.82Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-4" d="M66.31,76h-.16a2,2,0,0,1-1.79-2.11,8.69,8.69,0,0,1,6.8-7.8A2,2,0,1,1,72,69.92a4.8,4.8,0,0,0-3.76,4.31A2,2,0,0,1,66.31,76Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-2" d="M115.4,95.66a8.64,8.64,0,0,1,6.14,2.55h0a1.23,1.23,0,0,0,.88.37,1.24,1.24,0,0,0,.88-.37h0a8.67,8.67,0,1,1,10.91,13.37l-11.05,9a1.1,1.1,0,0,1-.69.24,1.08,1.08,0,0,1-.68-.24l-11.13-9a8.45,8.45,0,0,1-2.53-2.52,8.76,8.76,0,0,1,1.14-10.89,8.63,8.63,0,0,1,6.11-2.52m0-2h0a10.59,10.59,0,0,0-7.52,3.11,10.72,10.72,0,0,0-1.4,13.4,10.44,10.44,0,0,0,3,3l11,8.94a3.09,3.09,0,0,0,3.87,0l11-9a10.67,10.67,0,1,0-13-16.88,10.59,10.59,0,0,0-7-2.64Z" transform="translate(0.02 0.17)"/>
-  <path class="cls-4" d="M110.24,105.54h-.12a1.46,1.46,0,0,1-1.33-1.56,6.43,6.43,0,0,1,5-5.78,1.45,1.45,0,1,1,.63,2.83,3.56,3.56,0,0,0-2.78,3.19A1.45,1.45,0,0,1,110.24,105.54Z" transform="translate(0.02 0.17)"/>
+    <circle class="st6" cx="82.6" cy="59.4" r="1.2"/>
+  </g>
+  <path class="st1" d="M109.6 80h25.6c2.5 0 4.4 2 4.4 4.4v50.3c0 2.5-2 4.4-4.4 4.4h-25.6c-2.5 0-4.4-2-4.4-4.4V84.4c-.1-2.4 1.9-4.4 4.4-4.4z"/>
+  <linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="-19.2553" y1="590.7758" x2="221.2447" y2="809.2758" gradientTransform="translate(.02 -609.83)">
+    <stop offset="0" stop-color="#00C8D7"/>
+    <stop offset="1" stop-color="#0A84FF"/>
+  </linearGradient>
+  <path class="st7" d="M135.1 81c1.9 0 3.4 1.5 3.4 3.4v50.3c0 1.9-1.5 3.4-3.4 3.4h-25.6c-1.9 0-3.4-1.5-3.4-3.4V84.4c0-1.9 1.5-3.4 3.4-3.4h25.6m0-2h-25.6c-3 0-5.4 2.4-5.4 5.4v50.3c0 3 2.4 5.4 5.4 5.4h25.6c3 0 5.4-2.4 5.4-5.4V84.4c.1-3-2.4-5.4-5.4-5.4z"/>
+  <g>
+    <path class="st1" d="M111.1 84.8h22.4c.9 0 1.7.8 1.7 1.7v41.9c0 .9-.8 1.7-1.7 1.7h-22.4c-.9 0-1.7-.8-1.7-1.7V86.5c0-.9.8-1.7 1.7-1.7z"/>
+    <linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="62.995" y1="657.995" x2="135.835" y2="730.835" gradientTransform="translate(.02 -609.83)">
+      <stop offset="0" stop-color="#CCFBFF"/>
+      <stop offset="1" stop-color="#C9E4FF"/>
+    </linearGradient>
+    <path class="st8" d="M133.5 85.8c.4 0 .7.3.7.7v41.9c0 .4-.3.7-.7.7h-22.4c-.4 0-.7-.3-.7-.7V86.5c0-.4.3-.7.7-.7h22.4"/>
+  </g>
+  <linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="-73.41" y1="701.6741" x2="262.92" y2="701.6741" gradientTransform="translate(.02 -609.83)">
+    <stop offset="0" stop-color="#00C8D7"/>
+    <stop offset="1" stop-color="#0A84FF"/>
+  </linearGradient>
+  <path class="st9" d="M82.9 97.8c-.6 0-1.1-.2-1.6-.6l-15-12.1c-1.5-1-2.7-2.2-3.7-3.7-3.3-5.1-2.6-11.7 1.7-16 5-5 13-5 17.9 0 .1.1.3.2.5.2s.4-.1.5-.2c5.1-4.8 13.1-4.6 18 .5s4.6 13.1-.5 18c-.5.4-1 .9-1.5 1.2L84.4 97.3c-.4.3-1 .5-1.5.5zm41 23.7l11-9c4.4-3 5.6-9 2.7-13.4-3-4.4-9-5.6-13.4-2.7-.5.3-1 .7-1.4 1.2 0 0-.1.1-.2.1s-.1 0-.2-.1c-3.8-3.8-9.9-3.8-13.7 0-3.2 3.2-3.8 8.3-1.3 12.2.7 1.1 1.7 2.1 2.8 2.8l11.1 9c.7.6 1.8.6 2.6-.1z"/>
+  <path class="st1" d="M73.3 62.8c3.1 0 6.1 1.2 8.3 3.4.3.3.7.5 1.2.5.4 0 .9-.2 1.2-.5 4.6-4.5 12-4.5 16.6.1 4.5 4.6 4.5 12-.1 16.6-.5.5-1.1 1-1.7 1.4l-15 12.2c-.3.2-.6.3-.9.3s-.7-.1-.9-.3L67 84.3c-1.4-.9-2.5-2-3.4-3.4-3-4.6-2.4-10.7 1.5-14.7 2.1-2.1 5.1-3.4 8.2-3.4m0-2c-3.6 0-7.1 1.4-9.7 4-4.6 4.6-5.3 11.8-1.8 17.2 1 1.6 2.3 2.9 3.9 3.9l15 12.1c1.3 1 3.1 1 4.3 0l14.9-12.1c6.3-4.2 8-12.7 3.8-19s-12.7-8-19-3.8c-.7.5-1.3 1-1.9 1.5-2.6-2.4-6-3.8-9.5-3.8z"/>
+  <path class="st3" d="M66.3 76.2h-.2c-1.1-.1-1.9-1-1.8-2.1.3-3.8 3.1-7 6.8-7.8 1-.4 2.2 0 2.6 1s0 2.2-1 2.6c-.2.1-.5.2-.8.2-2.1.5-3.6 2.2-3.8 4.3 0 1-.8 1.7-1.8 1.8z"/>
+  <path class="st1" d="M115.4 95.8c2.3 0 4.5.9 6.1 2.6.2.2.5.4.9.4.3 0 .6-.1.9-.4 3.4-3.4 8.9-3.4 12.3 0 3.4 3.4 3.4 8.9 0 12.3-.4.4-.8.8-1.3 1.1l-11.1 9c-.2.2-.4.2-.7.2-.2 0-.5-.1-.7-.2l-11.1-9c-1-.7-1.9-1.5-2.5-2.5-2.2-3.4-1.7-8 1.1-10.9 1.6-1.7 3.8-2.6 6.1-2.6m0-2c-2.8 0-5.5 1.1-7.5 3.1-3.6 3.6-4.1 9.2-1.4 13.4.8 1.2 1.8 2.2 3 3l11 8.9c1.1.9 2.7.9 3.9 0l11-9c4.9-3.3 6.1-10 2.8-14.8-3.3-4.9-10-6.1-14.8-2.8-.3.2-.7.5-1 .7-2-1.6-4.5-2.6-7-2.5z"/>
+  <path class="st3" d="M110.3 105.7c-.9-.1-1.5-.8-1.4-1.6.2-2.8 2.2-5.2 5-5.8.8-.2 1.6.3 1.8 1.1.2.8-.3 1.6-1.1 1.8h-.1c-1.5.3-2.7 1.6-2.8 3.2-.1.7-.7 1.3-1.4 1.3z"/>
 </svg>
--- a/browser/themes/shared/incontentprefs/dialog.inc.css
+++ b/browser/themes/shared/incontentprefs/dialog.inc.css
@@ -45,33 +45,27 @@ tree:not(#rejectsTree) {
   margin: 3px 0 0 !important;
 }
 
 caption {
   padding-inline-start: 0;
 }
 
 groupbox {
-  font-size: 1em;
   margin-top: 0;
   margin-right: 4px;
   margin-left: 4px;
   padding-top: 0;
   padding-bottom: 5px;
 }
 
 prefpane .groupbox-body {
   padding: 0 0 5px;
 }
 
 groupbox description {
   margin-right: 0;
   margin-left: 0;
 }
 
-label.menu-text,
-textbox.tree-input {
-  font-size: unset;
-}
-
 menulist label {
   font-weight: unset;
 }
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -21,18 +21,16 @@
   /* A workaround to keep the container always float on the `top: 0` (Bug 1377009) */
   display: block;
   width: 664px;
 }
 
 #mainPrefPane {
   width: 100%;
   padding: 0;
-  font: message-box;
-  color: currentColor;
 }
 
 prefpane > groupbox + groupbox {
   margin-top: 16px;
 }
 
 groupbox + groupbox > .groupbox-body,
 groupbox + vbox groupbox > .groupbox-body {
@@ -52,21 +50,25 @@ button,
 treecol,
 html|option {
   /* override the * rule */
   -moz-user-select: none;
 }
 
 description,
 label {
-  line-height: 30px;
+  line-height: 1.8em;
   margin-top: 0 !important;
   margin-bottom: 0 !important;
 }
 
+.tip-caption {
+  font-size: .9em;
+}
+
 menulist > hbox > label,
 menuitem > label,
 button > hbox > label {
   line-height: unset;
 }
 
 .indent {
   margin-inline-start: 28px !important;
@@ -90,16 +92,34 @@ button > hbox > label {
 }
 
 .accessory-button {
   height: 30px;
   min-width: 150px;
   margin: 4px 0;
 }
 
+.spotlight {
+  background-color: rgba(0,200,215,0.3);
+  /* Show the border to spotlight the components in high-contrast mode. */
+  border: 1px solid transparent;
+  border-radius: 2px;
+}
+
+[data-subcategory] {
+  margin-left: -4px;
+  margin-right: -4px;
+  padding-left: 4px;
+  padding-right: 4px;
+}
+
+[data-subcategory] > .groupbox-title {
+  padding-inline-start: 4px;
+}
+
 #searchInput {
   border-radius: 0;
 }
 
 /* Subcategory title */
 
 /**
  * The first subcategory title for each category should not have margin-top.
@@ -206,22 +226,20 @@ button > hbox > label {
 }
 
 #browserHomePage:-moz-locale-dir(rtl) input {
   unicode-bidi: plaintext;
   direction: rtl;
 }
 
 #updateApp > .groupbox-body > label {
-  margin: 0 0 4px 0;
-  line-height: 30px;
+  margin: 0 0 4px;
 }
 
 #updateApp > .groupbox-body > description {
-  line-height: 30px;
   margin: 0;
 }
 
 #updateBox {
   margin-top: 4px;
   margin-bottom: 32px;
 }
 
@@ -241,17 +259,17 @@ button > hbox > label {
 
 #updateRadioGroup > radio,
 #browserStartupPage > radio {
   height: 30px;
   margin: 2px 0;
 }
 
 #filter {
-  margin: 4px 0 8px 0;
+  margin: 4px 0 8px;
 }
 
 #handlersView {
   height: 25em;
   margin-inline-end: 0;
 }
 
 #handlersView > richlistitem {
@@ -285,17 +303,17 @@ button > hbox > label {
 
 .actionsMenu > menupopup > menuitem > .menu-iconic-left {
   margin-inline-end: 8px !important;
 }
 
 /* Search Pane */
 
 #engineList {
-  margin: 2px 0 5px 0;
+  margin: 2px 0 5px;
 }
 
 #engineList > treechildren::-moz-tree-image(engineShown, checked),
 #blocklistsTree > treechildren::-moz-tree-image(selectionCol, checked) {
   list-style-image: url("chrome://global/skin/in-content/check.svg");
   -moz-context-properties: fill, stroke;
   fill: #2292d0;
   stroke: none;
@@ -314,17 +332,17 @@ button > hbox > label {
   min-height: 36px;
 }
 
 #selectionCol {
   min-width: 26px;
 }
 
 #addEnginesBox {
-  margin: 4px 0 0 0;
+  margin: 4px 0 0;
 }
 
 /* Privacy Pane */
 
 #formAutofillGroup {
   margin-top: 28px;
 }
 
@@ -407,17 +425,16 @@ button > hbox > label {
 .dialogOverlay[topmost="true"] {
   background-color: rgba(0,0,0,0.5);
 }
 
 .dialogBox {
   background-color: #fbfbfb;
   background-clip: content-box;
   color: #424e5a;
-  font-size: 14px;
   /* `transparent` will use the dialogText color in high-contrast themes and
      when page colors are disabled */
   border: 1px solid transparent;
   border-radius: 3.5px;
   box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3);
   display: -moz-box;
   margin: 0;
   padding: 0;
@@ -433,17 +450,17 @@ button > hbox > label {
 .dialogBox > .groupbox-title {
   margin-top: 0;
   padding: 3.5px 0;
   background-color: #F1F1F1;
   border-bottom: 1px solid #C1C1C1;
 }
 
 .dialogTitle {
-  font-size: unset;
+  font-size: .9em;
   text-align: center;
   -moz-user-select: none;
 }
 
 .close-icon {
   border: none;
   background: none !important;
   min-width: 0;
@@ -678,17 +695,17 @@ button > hbox > label {
 }
 
 .help-icon:hover {
   fill: currentColor !important;
 }
 
 .help-label {
   margin: 0 4px;
-  line-height: 22px;
+  font-size: .9em;
   -moz-user-select: none;
 }
 
 @media (max-width: 830px) {
   .help-button > .text-link {
     -moz-box-flex: 0;
     width: 36px;
     height: 36px;
@@ -720,19 +737,24 @@ button > hbox > label {
   margin-inline-end: 8px;
   margin-top: 5px;
   margin-bottom: 5px;
 }
 
 .search-container {
   position: sticky;
   background-color: var(--in-content-page-background);
-  width: 100%;
   top: 0;
   z-index: 1;
+  /* The search-container should have the capability to cover all spotlight area. */
+  width: calc(100% + 8px);
+  margin-left: -4px;
+  margin-right: -4px;
+  padding-left: 4px;
+  padding-right: 4px;
 }
 
 #searchInput {
   margin: 20px 0 30px 0;
 }
 
 #searchInput .textbox-search-icons:not([selectedIndex="1"]) {
   display: none;
--- a/browser/themes/shared/syncedtabs/sidebar.inc.css
+++ b/browser/themes/shared/syncedtabs/sidebar.inc.css
@@ -226,16 +226,17 @@ body {
 }
 
 .deck .syncIllustrationIssue {
   background-image: url(chrome://browser/skin/fxa/sync-illustration-issue.svg);
 }
 
 .deck .instructions {
   text-align: center;
+  color: rgba(12, 12, 13, .5);
   padding: 0 11px;
   max-width: 15em;
   margin: 1em auto;
 }
 
 .deck .button {
   display: block;
   background-color: #0060df;
--- a/browser/themes/windows/preferences/in-content/dialog.css
+++ b/browser/themes/windows/preferences/in-content/dialog.css
@@ -1,13 +1,9 @@
 /* - 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 ../../../shared/incontentprefs/dialog.inc.css
 
-label,
-textbox,
-description,
-.tab-text,
-menulist label {
-  font-size: 1.25rem;
+:root > * {
+  font-size: 1.12em;
 }
--- a/browser/themes/windows/preferences/in-content/preferences.css
+++ b/browser/themes/windows/preferences/in-content/preferences.css
@@ -1,27 +1,16 @@
 /* - 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 ../../../shared/incontentprefs/preferences.inc.css
 
-html *,
-page *,
-window * {
-  font-size: 1.25rem;
-}
-
-caption label {
-  font-size: 1.42rem;
-}
-
-.tip-caption,
-.help-label {
-  font-size: 1.08rem;
+:root > * {
+  font-size: 1.25em;
 }
 
 .actionsMenu > .menulist-label-box > .menulist-icon {
   margin-inline-end: 9px;
 }
 
 filefield + button {
   margin-inline-start: -4px;
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -41,17 +41,16 @@ MACH_MODULES = [
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
     'python/mach/mach/commands/settings.py',
     'python/mozboot/mozboot/mach_commands.py',
     'python/mozbuild/mozbuild/mach_commands.py',
     'python/mozbuild/mozbuild/backend/mach_commands.py',
     'python/mozbuild/mozbuild/compilation/codecomplete.py',
     'python/mozbuild/mozbuild/frontend/mach_commands.py',
-    'services/common/tests/mach_commands.py',
     'taskcluster/mach_commands.py',
     'testing/awsy/mach_commands.py',
     'testing/firefox-ui/mach_commands.py',
     'testing/geckodriver/mach_commands.py',
     'testing/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/mozharness/mach_commands.py',
--- a/devtools/client/aboutdebugging/aboutdebugging.css
+++ b/devtools/client/aboutdebugging/aboutdebugging.css
@@ -214,25 +214,26 @@ button {
   margin-inline-end: 1ch;
 }
 
 .addons-tip {
   display: flex;
   align-items: center;
   margin-top: 1em;
   margin-bottom: 1em;
-  background-image: url(chrome://devtools/skin/images/help.svg);
-  background-repeat: no-repeat;
-  background-size: 24px;
-  padding-inline-start: 32px;
   height: 24px;
 }
 
 .addons-tip-icon {
-  margin-inline-end: 1ch;
+  width: 24px;
+  height: 24px;
+  background-image: url(chrome://devtools/skin/images/help.svg);
+  background-repeat: no-repeat;
+  background-size: 24px;
+  margin-inline-end: 8px;
 }
 
 .error-page {
   display: flex;
   justify-content: center;
   align-items: center;
   flex-direction: column;
   width: 100%;
--- a/devtools/client/aboutdebugging/components/addons/Panel.js
+++ b/devtools/client/aboutdebugging/components/addons/Panel.js
@@ -166,16 +166,17 @@ class AddonsPanel extends Component {
         targets: temporaryTargets,
         client,
         connect,
         debugDisabled,
         targetClass,
         sort: true
       }),
       dom.div({ className: "addons-tip"},
+        dom.div({ className: "addons-tip-icon"}),
         dom.span({
           className: "addons-web-ext-tip",
         }, Strings.GetStringFromName("webExtTip")),
         dom.a({ href: WEB_EXT_URL, target: "_blank" },
           Strings.GetStringFromName("webExtTip.learnMore")
         )
       )
     ),
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,11 +1,11 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Taken from upstream commit: d2e91e574acbe3d5b546508d028bd278eaabd286
+Taken from upstream commit: efa4ca367dadb1bb54525f9fe305dd38c0ec6323
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @15.6.2
 - react-dom @15.6.2
 - webpack @3.8.1
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -1753,16 +1753,21 @@ html .toggle-button-end.vertical svg {
 
 .source-footer > .commands > .blackboxed > img.blackBox {
   background: var(--theme-highlight-blue);
 }
 
 .source-footer .blackbox-summary {
   color: var(--theme-body-color);
 }
+
+.source-footer .mapped-source {
+  color: var(--theme-body-color);
+  padding: 2.5px;
+}
 /* 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/>. */
 
 .search-bar {
   display: flex;
   flex-direction: column;
 }
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -14261,17 +14261,17 @@ module.exports = "<svg xmlns=\"http://ww
 /* 955 */,
 /* 956 */,
 /* 957 */,
 /* 958 */,
 /* 959 */,
 /* 960 */
 /***/ (function(module, exports) {
 
-module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySource): This is the text that appears in the\n# context menu to copy the selected source of file open.\ncopySource=Copy\ncopySource.accesskey=y\n\n# LOCALIZATION NOTE (copySourceUri2): This is the text that appears in the\n# context menu to copy the source URI of file open.\ncopySourceUri2=Copy source URI\ncopySourceUri2.accesskey=u\n\n# LOCALIZATION NOTE (setDirectoryRoot): This is the text that appears in the\n# context menu to set a directory as root directory\nsetDirectoryRoot.label=Set directory root\nsetDirectoryRoot.accesskey=r\n\n# LOCALIZATION NOTE (copyFunction): This is the text that appears in the\n# context menu to copy the function the user selected\ncopyFunction.label=Copy function\ncopyFunction.accesskey=F\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy stack trace\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step in %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step out %S\n\n# LOCALIZATION NOTE (workersHeader): The text to display in the events\n# header.\nworkersHeader=Workers\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display.\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (projectTextSearch.key): A key shortcut to open the\n# full project text search for searching all the files the debugger has seen.\nprojectTextSearch.key=CmdOrCtrl+Shift+F\n\n# LOCALIZATION NOTE (functionSearch.key): A key shortcut to open the\n# modal for searching functions in a file.\nfunctionSearch.key=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE (toggleBreakpoint.key): A key shortcut to toggle\n# breakpoints.\ntoggleBreakpoint.key=CmdOrCtrl+B\n\n# LOCALIZATION NOTE (toggleCondPanel.key): A key shortcut to toggle\n# the conditional breakpoint panel.\ntoggleCondPanel.key=CmdOrCtrl+Shift+B\n\n# LOCALIZATION NOTE (stepOut.key): A key shortcut to\n# step out.\nstepOut.key=Shift+F11\n\n# LOCALIZATION NOTE (shortcuts.header.editor): Sections header in\n# the shortcuts modal for keyboard shortcuts related to editing.\nshortcuts.header.editor=Editor\n\n# LOCALIZATION NOTE (shortcuts.header.stepping): Sections header in\n# the shortcuts modal for keyboard shortcuts related to stepping.\nshortcuts.header.stepping=Stepping\n\n# LOCALIZATION NOTE (shortcuts.header.search): Sections header in\n# the shortcuts modal for keyboard shortcuts related to search.\nshortcuts.header.search=Search\n\n# LOCALIZATION NOTE (projectTextSearch.placeholder): A placeholder shown\n# when searching across all of the files in a project.\nprojectTextSearch.placeholder=Find in files…\n\n# LOCALIZATION NOTE (projectTextSearch.noResults): The center pane Text Search\n# message when the query did not match any text of all files in a project.\nprojectTextSearch.noResults=No results found\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf2.label=Enable\nbreakpointMenuItem.enableSelf2.accesskey=E\nbreakpointMenuItem.disableSelf2.label=Disable\nbreakpointMenuItem.disableSelf2.accesskey=D\nbreakpointMenuItem.deleteSelf2.label=Remove\nbreakpointMenuItem.deleteSelf2.accesskey=R\nbreakpointMenuItem.enableOthers2.label=Enable others\nbreakpointMenuItem.enableOthers2.accesskey=o\nbreakpointMenuItem.disableOthers2.label=Disable others\nbreakpointMenuItem.disableOthers2.accesskey=s\nbreakpointMenuItem.deleteOthers2.label=Remove others\nbreakpointMenuItem.deleteOthers2.accesskey=h\nbreakpointMenuItem.enableAll2.label=Enable all\nbreakpointMenuItem.enableAll2.accesskey=b\nbreakpointMenuItem.disableAll2.label=Disable all\nbreakpointMenuItem.disableAll2.accesskey=k\nbreakpointMenuItem.deleteAll2.label=Remove all\nbreakpointMenuItem.deleteAll2.accesskey=a\nbreakpointMenuItem.removeCondition2.label=Remove condition\nbreakpointMenuItem.removeCondition2.accesskey=c\nbreakpointMenuItem.addCondition2.label=Add condition\nbreakpointMenuItem.addCondition2.accesskey=A\nbreakpointMenuItem.editCondition2.label=Edit condition\nbreakpointMenuItem.editCondition2.accesskey=n\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.enableSelf.accesskey=E\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.disableSelf.accesskey=D\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.deleteSelf.accesskey=R\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.enableOthers.accesskey=o\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.disableOthers.accesskey=s\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.deleteOthers.accesskey=h\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.enableAll.accesskey=b\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.disableAll.accesskey=k\nbreakpointMenuItem.deleteAll=Remove all breakpoints\nbreakpointMenuItem.deleteAll.accesskey=a\nbreakpointMenuItem.removeCondition.label=Remove breakpoint condition\nbreakpointMenuItem.removeCondition.accesskey=c\nbreakpointMenuItem.editCondition.label=Edit breakpoint condition\nbreakpointMenuItem.editCondition.accesskey=n\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=No results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.continueToHere.label): Editor gutter context\n# menu item for jumping to a new paused location\neditor.continueToHere.label=Continue to here\neditor.continueToHere.accesskey=H\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable breakpoint\neditor.disableBreakpoint.accesskey=D\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add conditional breakpoint\neditor.addConditionalBreakpoint.accesskey=c\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\neditor.jumpToMappedLocation1.accesskey=m\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable framework grouping\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable framework grouping\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=Generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add watch expression\nexpressions.label=Add watch expression\nexpressions.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close other tabs\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in tree\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty print source\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox source\nsourceFooter.blackbox.accesskey=B\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed source\n\n# LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated\n# with a code coverage button\nsourceFooter.codeCoverage=Code coverage\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (outline.header): Outline left sidebar header\noutline.header=Outline\n\n# LOCALIZATION NOTE (outline.noFunctions): Outline text when there are no functions to display\noutline.noFunctions=No functions\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (welcome.findInFiles): The center pane welcome panel's\n# search prompt. e.g. cmd+f to search for files. On windows, it's ctrl+shift+f, on\n# a mac we use the unicode character.\nwelcome.findInFiles=%S to find in files\n\n# LOCALIZATION NOTE (welcome.searchFunction): Label displayed in the welcome\n# panel. %S is replaced by the keyboard shortcut to search for functions.\nwelcome.searchFunction=%S to search for functions in file\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults2=No results found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText3): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText3=Error loading this URI: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder\n# text displayed when the user searches for specific lines in a file\ngotoLineModal.placeholder=Go to line…\ngotoLineModal.key=CmdOrCtrl+Shift+;\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.modifiersLabel): A label\n# preceding the group of modifiers\nsymbolSearch.searchModifier.modifiersLabel=Modifiers:\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n\n# LOCALIZATION NOTE (anonymous): The text that is displayed when the\n# display name is null.\nanonymous=(anonymous)\n\n# LOCALIZATION NOTE (shortcuts.toggleBreakpoint): text describing\n# keyboard shortcut action for toggling breakpoint\nshortcuts.toggleBreakpoint=Toggle Breakpoint\nshortcuts.toggleBreakpoint.accesskey=B\n\n# LOCALIZATION NOTE (shortcuts.toggleCondPanel): text describing\n# keyboard shortcut action for toggling conditional panel keyboard\nshortcuts.toggleCondPanel=Toggle Conditional Panel\n\n# LOCALIZATION NOTE (shortcuts.pauseOrResume): text describing\n# keyboard shortcut action for pause of resume\nshortcuts.pauseOrResume=Pause/Resume\n\n# LOCALIZATION NOTE (shortcuts.stepOver): text describing\n# keyboard shortcut action for stepping over\nshortcuts.stepOver=Step Over\n\n# LOCALIZATION NOTE (shortcuts.stepIn): text describing\n# keyboard shortcut action for stepping in\nshortcuts.stepIn=Step In\n\n# LOCALIZATION NOTE (shortcuts.stepOut): text describing\n# keyboard shortcut action for stepping out\nshortcuts.stepOut=Step Out\n\n# LOCALIZATION NOTE (shortcuts.fileSearch): text describing\n# keyboard shortcut action for source file search\nshortcuts.fileSearch=Source File Search\n\n# LOCALIZATION NOTE (shortcuts.searchAgain): text describing\n# keyboard shortcut action for searching again\nshortcuts.searchAgain=Search Again\n\n# LOCALIZATION NOTE (shortcuts.projectSearch): text describing\n# keyboard shortcut action for full project search\nshortcuts.projectSearch=Full Project Search\n\n# LOCALIZATION NOTE (shortcuts.functionSearch): text describing\n# keyboard shortcut action for function search\nshortcuts.functionSearch=Function Search\n\n# LOCALIZATION NOTE (shortcuts.buttonName): text describing\n# keyboard shortcut button text\nshortcuts.buttonName=Keyboard shortcuts\n"
+module.exports = "# This Source Code Form is subject to the terms of the Mozilla Public\n# License, v. 2.0. If a copy of the MPL was not distributed with this\n# file, You can obtain one at http://mozilla.org/MPL/2.0/.\n\n# LOCALIZATION NOTE These strings are used inside the Debugger\n# which is available from the Web Developer sub-menu -> 'Debugger'.\n# The correct localization of this file might be to keep it in\n# English, or another language commonly spoken among web developers.\n# You want to make that choice consistent across the developer tools.\n# A good criteria is the language in which you'd find the best\n# documentation on web development on the web.\n\n# LOCALIZATION NOTE (collapsePanes): This is the tooltip for the button\n# that collapses the left and right panes in the debugger UI.\ncollapsePanes=Collapse panes\n\n# LOCALIZATION NOTE (copySource): This is the text that appears in the\n# context menu to copy the selected source of file open.\ncopySource=Copy\ncopySource.accesskey=y\n\n# LOCALIZATION NOTE (copySourceUri2): This is the text that appears in the\n# context menu to copy the source URI of file open.\ncopySourceUri2=Copy source URI\ncopySourceUri2.accesskey=u\n\n# LOCALIZATION NOTE (setDirectoryRoot): This is the text that appears in the\n# context menu to set a directory as root directory\nsetDirectoryRoot.label=Set directory root\nsetDirectoryRoot.accesskey=r\n\n# LOCALIZATION NOTE (copyFunction): This is the text that appears in the\n# context menu to copy the function the user selected\ncopyFunction.label=Copy function\ncopyFunction.accesskey=F\n\n# LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the\n# context menu to copy the stack trace methods, file names and row number.\ncopyStackTrace=Copy stack trace\ncopyStackTrace.accesskey=c\n\n# LOCALIZATION NOTE (expandPanes): This is the tooltip for the button\n# that expands the left and right panes in the debugger UI.\nexpandPanes=Expand panes\n\n# LOCALIZATION NOTE (pauseButtonTooltip): The tooltip that is displayed for the pause\n# button when the debugger is in a running state.\npauseButtonTooltip=Pause %S\n\n# LOCALIZATION NOTE (pausePendingButtonTooltip): The tooltip that is displayed for\n# the pause button after it's been clicked but before the next JavaScript to run.\npausePendingButtonTooltip=Waiting for next execution\n\n# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause\n# button when the debugger is in a paused state.\nresumeButtonTooltip=Resume %S\n\n# LOCALIZATION NOTE (stepOverTooltip): The label that is displayed on the\n# button that steps over a function call.\nstepOverTooltip=Step over %S\n\n# LOCALIZATION NOTE (stepInTooltip): The label that is displayed on the\n# button that steps into a function call.\nstepInTooltip=Step in %S\n\n# LOCALIZATION NOTE (stepOutTooltip): The label that is displayed on the\n# button that steps out of a function call.\nstepOutTooltip=Step out %S\n\n# LOCALIZATION NOTE (workersHeader): The text to display in the events\n# header.\nworkersHeader=Workers\n\n# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list\n# when there are no workers.\nnoWorkersText=This page has no workers.\n\n# LOCALIZATION NOTE (noSourcesText): The text to display in the sources list\n# when there are no sources.\nnoSourcesText=This page has no sources.\n\n# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab\n# when there are no events.\nnoEventListenersText=No event listeners to display.\n\n# LOCALIZATION NOTE (eventListenersHeader): The text to display in the events\n# header.\neventListenersHeader=Event listeners\n\n# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab\n# when there are no stack frames.\nnoStackFramesText=No stack frames to display\n\n# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when\n# the user hovers over the checkbox used to toggle an event breakpoint.\neventCheckboxTooltip=Toggle breaking on this event\n\n# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab\n# for every event item, between the event type and event selector.\neventOnSelector=on\n\n# LOCALIZATION NOTE (eventInSource): The text to display in the events tab\n# for every event item, between the event selector and listener's owner source.\neventInSource=in\n\n# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when\n# an event is listened on more than one target node.\neventNodes=%S nodes\n\n# LOCALIZATION NOTE (eventNative): The text to display in the events tab when\n# a listener is added from plugins, thus getting translated to native code.\neventNative=[native code]\n\n# LOCALIZATION NOTE (*Events): The text to display in the events tab for\n# each group of sub-level event entries.\nanimationEvents=Animation\naudioEvents=Audio\nbatteryEvents=Battery\nclipboardEvents=Clipboard\ncompositionEvents=Composition\ndeviceEvents=Device\ndisplayEvents=Display\ndragAndDropEvents=Drag and Drop\ngamepadEvents=Gamepad\nindexedDBEvents=IndexedDB\ninteractionEvents=Interaction\nkeyboardEvents=Keyboard\nmediaEvents=HTML5 Media\nmouseEvents=Mouse\nmutationEvents=Mutation\nnavigationEvents=Navigation\npointerLockEvents=Pointer Lock\nsensorEvents=Sensor\nstorageEvents=Storage\ntimeEvents=Time\ntouchEvents=Touch\notherEvents=Other\n\n# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when\n# the user hovers over the checkbox used to toggle blackboxing its associated\n# source.\nblackboxCheckboxTooltip2=Toggle blackboxing\n\n# LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for\n# searching all the source files the debugger has seen.\nsources.search.key2=CmdOrCtrl+P\n\n# LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the\n# search for searching all the source files the debugger has seen.\nsources.search.alt.key=CmdOrCtrl+O\n\n# LOCALIZATION NOTE (projectTextSearch.key): A key shortcut to open the\n# full project text search for searching all the files the debugger has seen.\nprojectTextSearch.key=CmdOrCtrl+Shift+F\n\n# LOCALIZATION NOTE (functionSearch.key): A key shortcut to open the\n# modal for searching functions in a file.\nfunctionSearch.key=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE (toggleBreakpoint.key): A key shortcut to toggle\n# breakpoints.\ntoggleBreakpoint.key=CmdOrCtrl+B\n\n# LOCALIZATION NOTE (toggleCondPanel.key): A key shortcut to toggle\n# the conditional breakpoint panel.\ntoggleCondPanel.key=CmdOrCtrl+Shift+B\n\n# LOCALIZATION NOTE (stepOut.key): A key shortcut to\n# step out.\nstepOut.key=Shift+F11\n\n# LOCALIZATION NOTE (shortcuts.header.editor): Sections header in\n# the shortcuts modal for keyboard shortcuts related to editing.\nshortcuts.header.editor=Editor\n\n# LOCALIZATION NOTE (shortcuts.header.stepping): Sections header in\n# the shortcuts modal for keyboard shortcuts related to stepping.\nshortcuts.header.stepping=Stepping\n\n# LOCALIZATION NOTE (shortcuts.header.search): Sections header in\n# the shortcuts modal for keyboard shortcuts related to search.\nshortcuts.header.search=Search\n\n# LOCALIZATION NOTE (projectTextSearch.placeholder): A placeholder shown\n# when searching across all of the files in a project.\nprojectTextSearch.placeholder=Find in files…\n\n# LOCALIZATION NOTE (projectTextSearch.noResults): The center pane Text Search\n# message when the query did not match any text of all files in a project.\nprojectTextSearch.noResults=No results found\n\n# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger\n# does not have any sources.\nsources.noSourcesAvailable=This page has no sources\n\n# LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search\n# for searching within a the currently opened files in the editor\nsourceSearch.search.key2=CmdOrCtrl+F\n\n# LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in\n# the source search input bar\nsourceSearch.search.placeholder=Search in file…\n\n# LOCALIZATION NOTE (sourceSearch.search.again.key2): Key shortcut to highlight\n# the next occurrence of the last search triggered from a source search\nsourceSearch.search.again.key2=CmdOrCtrl+G\n\n# LOCALIZATION NOTE (sourceSearch.search.againPrev.key2): Key shortcut to highlight\n# the previous occurrence of the last search triggered from a source search\nsourceSearch.search.againPrev.key2=CmdOrCtrl+Shift+G\n\n# LOCALIZATION NOTE (sourceSearch.resultsSummary1): Shows a summary of\n# the number of matches for autocomplete\nsourceSearch.resultsSummary1=%d results\n\n# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the\n# global search results when there are no matching strings after filtering.\nnoMatchingStringsText=No matches found\n\n# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the\n# filter text box when it is empty and the scripts container is selected.\nemptySearchText=Search scripts (%S)\n\n# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that\n# appears in the filter text box for the variables view container.\nemptyVariablesFilterText=Filter variables\n\n# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that\n# appears in the filter text box for the editor's variables view bubble.\nemptyPropertiesFilterText=Filter properties\n\n# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the\n# filter panel popup for the filter scripts operation.\nsearchPanelFilter=Filter scripts (%S)\n\n# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the\n# filter panel popup for the global search operation.\nsearchPanelGlobal=Search in all files (%S)\n\n# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the\n# filter panel popup for the function search operation.\nsearchPanelFunction=Search for function definition (%S)\n\n# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the\n# filter panel popup for the token search operation.\nsearchPanelToken=Find in this file (%S)\n\n# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the\n# filter panel popup for the line search operation.\nsearchPanelGoToLine=Go to line (%S)\n\n# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the\n# filter panel popup for the variables search operation.\nsearchPanelVariable=Filter variables (%S)\n\n# LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that\n# are displayed in the breakpoints menu item popup.\nbreakpointMenuItem.setConditional=Configure conditional breakpoint\nbreakpointMenuItem.enableSelf2.label=Enable\nbreakpointMenuItem.enableSelf2.accesskey=E\nbreakpointMenuItem.disableSelf2.label=Disable\nbreakpointMenuItem.disableSelf2.accesskey=D\nbreakpointMenuItem.deleteSelf2.label=Remove\nbreakpointMenuItem.deleteSelf2.accesskey=R\nbreakpointMenuItem.enableOthers2.label=Enable others\nbreakpointMenuItem.enableOthers2.accesskey=o\nbreakpointMenuItem.disableOthers2.label=Disable others\nbreakpointMenuItem.disableOthers2.accesskey=s\nbreakpointMenuItem.deleteOthers2.label=Remove others\nbreakpointMenuItem.deleteOthers2.accesskey=h\nbreakpointMenuItem.enableAll2.label=Enable all\nbreakpointMenuItem.enableAll2.accesskey=b\nbreakpointMenuItem.disableAll2.label=Disable all\nbreakpointMenuItem.disableAll2.accesskey=k\nbreakpointMenuItem.deleteAll2.label=Remove all\nbreakpointMenuItem.deleteAll2.accesskey=a\nbreakpointMenuItem.removeCondition2.label=Remove condition\nbreakpointMenuItem.removeCondition2.accesskey=c\nbreakpointMenuItem.addCondition2.label=Add condition\nbreakpointMenuItem.addCondition2.accesskey=A\nbreakpointMenuItem.editCondition2.label=Edit condition\nbreakpointMenuItem.editCondition2.accesskey=n\nbreakpointMenuItem.enableSelf=Enable breakpoint\nbreakpointMenuItem.enableSelf.accesskey=E\nbreakpointMenuItem.disableSelf=Disable breakpoint\nbreakpointMenuItem.disableSelf.accesskey=D\nbreakpointMenuItem.deleteSelf=Remove breakpoint\nbreakpointMenuItem.deleteSelf.accesskey=R\nbreakpointMenuItem.enableOthers=Enable others\nbreakpointMenuItem.enableOthers.accesskey=o\nbreakpointMenuItem.disableOthers=Disable others\nbreakpointMenuItem.disableOthers.accesskey=s\nbreakpointMenuItem.deleteOthers=Remove others\nbreakpointMenuItem.deleteOthers.accesskey=h\nbreakpointMenuItem.enableAll=Enable all breakpoints\nbreakpointMenuItem.enableAll.accesskey=b\nbreakpointMenuItem.disableAll=Disable all breakpoints\nbreakpointMenuItem.disableAll.accesskey=k\nbreakpointMenuItem.deleteAll=Remove all breakpoints\nbreakpointMenuItem.deleteAll.accesskey=a\nbreakpointMenuItem.removeCondition.label=Remove breakpoint condition\nbreakpointMenuItem.removeCondition.accesskey=c\nbreakpointMenuItem.editCondition.label=Edit breakpoint condition\nbreakpointMenuItem.editCondition.accesskey=n\n\n# LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.\nbreakpoints.header=Breakpoints\n\n# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are\n# no breakpoints present\nbreakpoints.none=No breakpoints\n\n# LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.enable=Enable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip\n# when hovering over the 'disable breakpoints' switch button in right sidebar\nbreakpoints.disable=Disable breakpoints\n\n# LOCALIZATION NOTE (breakpoints.removeBreakpointTooltip): The tooltip that is displayed\n# for remove breakpoint button in right sidebar\nbreakpoints.removeBreakpointTooltip=Remove breakpoint\n\n# LOCALIZATION NOTE (callStack.header): Call Stack right sidebar pane header.\ncallStack.header=Call stack\n\n# LOCALIZATION NOTE (callStack.notPaused): Call Stack right sidebar pane\n# message when not paused.\ncallStack.notPaused=Not paused\n\n# LOCALIZATION NOTE (callStack.collapse): Call Stack right sidebar pane\n# message to hide some of the frames that are shown.\ncallStack.collapse=Collapse rows\n\n# LOCALIZATION NOTE (callStack.expand): Call Stack right sidebar pane\n# message to show more of the frames.\ncallStack.expand=Expand rows\n\n# LOCALIZATION NOTE (editor.searchResults): Editor Search bar message\n# for the summarizing the selected search result. e.g. 5 of 10 results.\neditor.searchResults=%d of %d results\n\n# LOCALIZATION NOTE (sourceSearch.singleResult): Copy shown when there is one result.\neditor.singleResult=1 result\n\n# LOCALIZATION NOTE (editor.noResults): Editor Search bar message\n# for when no results found.\neditor.noResults=No results\n\n# LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar\n# tooltip for traversing to the Next Result\neditor.searchResults.nextResult=Next result\n\n# LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar\n# tooltip for traversing to the Previous Result\neditor.searchResults.prevResult=Previous result\n\n# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for\n# toggling search type buttons(function search, variable search)\neditor.searchTypeToggleTitle=Search for:\n\n# LOCALIZATION NOTE (editor.continueToHere.label): Editor gutter context\n# menu item for jumping to a new paused location\neditor.continueToHere.label=Continue to here\neditor.continueToHere.accesskey=H\n\n# LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item\n# for adding a breakpoint on a line.\neditor.addBreakpoint=Add breakpoint\n\n# LOCALIZATION NOTE (editor.disableBreakpoint): Editor gutter context menu item\n# for disabling a breakpoint on a line.\neditor.disableBreakpoint=Disable breakpoint\neditor.disableBreakpoint.accesskey=D\n\n# LOCALIZATION NOTE (editor.enableBreakpoint): Editor gutter context menu item\n# for enabling a breakpoint on a line.\neditor.enableBreakpoint=Enable breakpoint\n\n# LOCALIZATION NOTE (editor.removeBreakpoint): Editor gutter context menu item\n# for removing a breakpoint on a line.\neditor.removeBreakpoint=Remove breakpoint\n\n# LOCALIZATION NOTE (editor.editBreakpoint): Editor gutter context menu item\n# for setting a breakpoint condition on a line.\neditor.editBreakpoint=Edit breakpoint\n\n# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context\n# menu item for adding a breakpoint condition on a line.\neditor.addConditionalBreakpoint=Add conditional breakpoint\neditor.addConditionalBreakpoint.accesskey=c\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Placeholder text for\n# input element inside ConditionalPanel component\neditor.conditionalPanel.placeholder=This breakpoint will pause when the expression is true\n\n# LOCALIZATION NOTE (editor.conditionalPanel.placeholder): Tooltip text for\n# close button inside ConditionalPanel component\neditor.conditionalPanel.close=Cancel edit breakpoint and close\n\n# LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item\n# for navigating to a source mapped location\neditor.jumpToMappedLocation1=Jump to %S location\neditor.jumpToMappedLocation1.accesskey=m\n\n# LOCALIZATION NOTE (framework.disableGrouping): This is the text that appears in the\n# context menu to disable framework grouping.\nframework.disableGrouping=Disable framework grouping\nframework.disableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (framework.enableGrouping): This is the text that appears in the\n# context menu to enable framework grouping.\nframework.enableGrouping=Enable framework grouping\nframework.enableGrouping.accesskey=u\n\n# LOCALIZATION NOTE (generated): Source Map term for a server source location\ngenerated=Generated\n\n# LOCALIZATION NOTE (original): Source Map term for a debugger UI source location\noriginal=original\n\n# LOCALIZATION NOTE (expressions.placeholder): Placeholder text for expression\n# input element\nexpressions.placeholder=Add watch expression\nexpressions.label=Add watch expression\nexpressions.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeTab): Editor source tab context menu item\n# for closing the selected tab below the mouse.\nsourceTabs.closeTab=Close tab\nsourceTabs.closeTab.accesskey=c\n\n# LOCALIZATION NOTE (sourceTabs.closeOtherTabs): Editor source tab context menu item\n# for closing the other tabs.\nsourceTabs.closeOtherTabs=Close other tabs\nsourceTabs.closeOtherTabs.accesskey=o\n\n# LOCALIZATION NOTE (sourceTabs.closeTabsToEnd): Editor source tab context menu item\n# for closing the tabs to the end (the right for LTR languages) of the selected tab.\nsourceTabs.closeTabsToEnd=Close tabs to the right\nsourceTabs.closeTabsToEnd.accesskey=e\n\n# LOCALIZATION NOTE (sourceTabs.closeAllTabs): Editor source tab context menu item\n# for closing all tabs.\nsourceTabs.closeAllTabs=Close all tabs\nsourceTabs.closeAllTabs.accesskey=a\n\n# LOCALIZATION NOTE (sourceTabs.revealInTree): Editor source tab context menu item\n# for revealing source in tree.\nsourceTabs.revealInTree=Reveal in tree\nsourceTabs.revealInTree.accesskey=r\n\n# LOCALIZATION NOTE (sourceTabs.prettyPrint): Editor source tab context menu item\n# for pretty printing the source.\nsourceTabs.prettyPrint=Pretty print source\nsourceTabs.prettyPrint.accesskey=p\n\n# LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.blackbox=Blackbox source\nsourceFooter.blackbox.accesskey=B\n\n# LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated\n# with the blackbox button\nsourceFooter.unblackbox=Unblackbox source\nsourceFooter.unblackbox.accesskey=b\n\n# LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated\n# with a blackboxed source\nsourceFooter.blackboxed=Blackboxed source\n\n# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated\n# with a mapped source. %S is replaced by the source map origin.\nsourceFooter.mappedSource=(From %S)\n\n# LOCALIZATION NOTE (sourceFooter.mappedSourceTooltip): Tooltip text associated\n# with a mapped source. %S is replaced by the source map origin.\nsourceFooter.mappedSourceTooltip=(Source mapped from %S)\n\n# LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated\n# with a code coverage button\nsourceFooter.codeCoverage=Code coverage\n\n# LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed\n# for close tab button in source tabs.\nsourceTabs.closeTabButtonTooltip=Close tab\n\n# LOCALIZATION NOTE (scopes.header): Scopes right sidebar pane header.\nscopes.header=Scopes\n\n# LOCALIZATION NOTE (scopes.notAvailable): Scopes right sidebar pane message\n# for when the debugger is paused, but there isn't pause data.\nscopes.notAvailable=Scopes unavailable\n\n# LOCALIZATION NOTE (scopes.notPaused): Scopes right sidebar pane message\n# for when the debugger is not paused.\nscopes.notPaused=Not paused\n\n# LOCALIZATION NOTE (scopes.block): Refers to a block of code in\n# the scopes pane when the debugger is paused.\nscopes.block=Block\n\n# LOCALIZATION NOTE (sources.header): Sources left sidebar header\nsources.header=Sources\n\n# LOCALIZATION NOTE (outline.header): Outline left sidebar header\noutline.header=Outline\n\n# LOCALIZATION NOTE (outline.noFunctions): Outline text when there are no functions to display\noutline.noFunctions=No functions\n\n# LOCALIZATION NOTE (sources.search): Sources left sidebar prompt\n# e.g. Cmd+P to search. On a mac, we use the command unicode character.\n# On windows, it's ctrl.\nsources.search=%S to search\n\n# LOCALIZATION NOTE (watchExpressions.header): Watch Expressions right sidebar\n# pane header.\nwatchExpressions.header=Watch expressions\n\n# LOCALIZATION NOTE (watchExpressions.refreshButton): Watch Expressions header\n# button for refreshing the expressions.\nwatchExpressions.refreshButton=Refresh\n\n# LOCALIZATION NOTE (welcome.search): The center pane welcome panel's\n# search prompt. e.g. cmd+p to search for files. On windows, it's ctrl, on\n# a mac we use the unicode character.\nwelcome.search=%S to search for sources\n\n# LOCALIZATION NOTE (welcome.findInFiles): The center pane welcome panel's\n# search prompt. e.g. cmd+f to search for files. On windows, it's ctrl+shift+f, on\n# a mac we use the unicode character.\nwelcome.findInFiles=%S to find in files\n\n# LOCALIZATION NOTE (welcome.searchFunction): Label displayed in the welcome\n# panel. %S is replaced by the keyboard shortcut to search for functions.\nwelcome.searchFunction=%S to search for functions in file\n\n# LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search\n# prompt for searching for files.\nsourceSearch.search=Search sources…\n\n# LOCALIZATION NOTE (sourceSearch.noResults): The center pane Source Search\n# message when the query did not match any of the sources.\nsourceSearch.noResults2=No results found\n\n# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip\n# when the debugger will not pause on exceptions.\nignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions\n\n# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button\n# tooltip when the debugger will pause on uncaught exceptions.\npauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions\n\n# LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip\n# when the debugger will pause on all exceptions.\npauseOnExceptions=Pause on all exceptions. Click to ignore exceptions\n\n# LOCALIZATION NOTE (loadingText): The text that is displayed in the script\n# editor when the loading process has started but there is no file to display\n# yet.\nloadingText=Loading\\u2026\n\n# LOCALIZATION NOTE (errorLoadingText3): The text that is displayed in the debugger\n# viewer when there is an error loading a file\nerrorLoadingText3=Error loading this URI: %S\n\n# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the\n# watch expressions list to add a new item.\naddWatchExpressionText=Add watch expression\n\n# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the\n# variables view popup.\naddWatchExpressionButton=Watch\n\n# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the\n# variables pane when there are no variables to display.\nemptyVariablesText=No variables to display\n\n# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables\n# pane as a header for each variable scope (e.g. \"Global scope, \"With scope\",\n# etc.).\nscopeLabel=%S scope\n\n# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch\n# expressions scope. This text is displayed in the variables pane as a header for\n# the watch expressions scope.\nwatchExpressionsScopeLabel=Watch expressions\n\n# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text\n# is added to scopeLabel and displayed in the variables pane as a header for\n# the global scope.\nglobalScopeLabel=Global\n\n# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is\n# shown before the stack trace in an error.\nvariablesViewErrorStacktrace=Stack trace:\n\n# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed\n# when you have an object preview that does not show all of the elements. At the end of the list\n# you see \"N more...\" in the web console output.\n# This is a semi-colon list of plural forms.\n# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals\n# #1 number of remaining items in the object\n# example: 3 more…\nvariablesViewMoreObjects=#1 more…;#1 more…\n\n# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed\n# in the variables list on an item with an editable name.\nvariablesEditableNameTooltip=Double click to edit\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in the variables list on an item with an editable value.\nvariablesEditableValueTooltip=Click to change value\n\n# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed\n# in the variables list on an item which can be removed.\nvariablesCloseButtonTooltip=Click to remove\n\n# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed\n# in the variables list on a getter or setter which can be edited.\nvariablesEditButtonTooltip=Click to set value\n\n# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed\n# in a tooltip on the \"open in inspector\" button in the the variables list for a\n# DOMNode item.\nvariablesDomNodeValueTooltip=Click to select the node in the inspector\n\n# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed\n# in the variables list on certain variables or properties as tooltips.\n# Expanations of what these represent can be found at the following links:\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen\n# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed\n# It's probably best to keep these in English.\nconfigurableTooltip=configurable\nenumerableTooltip=enumerable\nwritableTooltip=writable\nfrozenTooltip=frozen\nsealedTooltip=sealed\nextensibleTooltip=extensible\noverriddenTooltip=overridden\nWebIDLTooltip=WebIDL\n\n# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed\n# in the variables list as a separator between the name and value.\nvariablesSeparatorLabel=:\n\n# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed\n# in the watch expressions list as a separator between the code and evaluation.\nwatchExpressionsSeparatorLabel2=\\u0020→\n\n# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed\n# in the functions search panel as a separator between function's inferred name\n# and its real name (if available).\nfunctionSearchSeparatorLabel=←\n\n# LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder\n# text displayed when the user searches for specific lines in a file\ngotoLineModal.placeholder=Go to line…\ngotoLineModal.key=CmdOrCtrl+Shift+;\n\n# LOCALIZATION NOTE(symbolSearch.search.functionsPlaceholder): The placeholder\n# text displayed when the user searches for functions in a file\nsymbolSearch.search.functionsPlaceholder=Search functions…\n\n# LOCALIZATION NOTE(symbolSearch.search.variablesPlaceholder): The placeholder\n# text displayed when the user searches for variables in a file\nsymbolSearch.search.variablesPlaceholder=Search variables…\n\n# LOCALIZATION NOTE(symbolSearch.search.key2): The Key Shortcut for\n# searching for a function or variable\nsymbolSearch.search.key2=CmdOrCtrl+Shift+O\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.modifiersLabel): A label\n# preceding the group of modifiers\nsymbolSearch.searchModifier.modifiersLabel=Modifiers:\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.regex): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.regex=Regex\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.caseSensitive=Case sensitive\n\n# LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option\n# when searching text in a file\nsymbolSearch.searchModifier.wholeWord=Whole word\n\n# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears\n# as a description in the notification panel popup, when multiple debuggers are\n# open in separate tabs and the user tries to resume them in the wrong order.\n# The substitution parameter is the URL of the last paused window that must be\n# resumed first.\nresumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S\n\nvariablesViewOptimizedOut=(optimized away)\nvariablesViewUninitialized=(uninitialized)\nvariablesViewMissingArgs=(unavailable)\n\nanonymousSourcesLabel=Anonymous sources\n\nexperimental=This is an experimental feature\n\n# LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed\n# in a info block explaining how the debugger is currently paused due to a `debugger`\n# statement in the code\nwhyPaused.debuggerStatement=Paused on debugger statement\n\n# LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a breakpoint\nwhyPaused.breakpoint=Paused on breakpoint\n\n# LOCALIZATION NOTE (whyPaused.exception): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an exception\nwhyPaused.exception=Paused on exception\n\n# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed\n# in a info block explaining how the debugger is currently paused while stepping\n# in or out of the stack\nwhyPaused.resumeLimit=Paused while stepping\n\n# LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# dom event\nwhyPaused.pauseOnDOMEvents=Paused on event listener\n\n# LOCALIZATION NOTE (whyPaused.breakpointConditionThrown): The text that is displayed\n# in an info block when evaluating a conditional breakpoint throws an error\nwhyPaused.breakpointConditionThrown=Error with conditional breakpoint\n\n# LOCALIZATION NOTE (whyPaused.xhr): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# xml http request\nwhyPaused.xhr=Paused on XMLHttpRequest\n\n# LOCALIZATION NOTE (whyPaused.promiseRejection): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# promise rejection\nwhyPaused.promiseRejection=Paused on promise rejection\n\n# LOCALIZATION NOTE (whyPaused.assert): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an\n# assert\nwhyPaused.assert=Paused on assertion\n\n# LOCALIZATION NOTE (whyPaused.debugCommand): The text that is displayed\n# in a info block explaining how the debugger is currently paused on a\n# debugger statement\nwhyPaused.debugCommand=Paused on debugged function\n\n# LOCALIZATION NOTE (whyPaused.other): The text that is displayed\n# in a info block explaining how the debugger is currently paused on an event\n# listener breakpoint set\nwhyPaused.other=Debugger paused\n\n# LOCALIZATION NOTE (ctrl): The text that is used for documenting\n# keyboard shortcuts that use the control key\nctrl=Ctrl\n\n# LOCALIZATION NOTE (anonymous): The text that is displayed when the\n# display name is null.\nanonymous=(anonymous)\n\n# LOCALIZATION NOTE (shortcuts.toggleBreakpoint): text describing\n# keyboard shortcut action for toggling breakpoint\nshortcuts.toggleBreakpoint=Toggle Breakpoint\nshortcuts.toggleBreakpoint.accesskey=B\n\n# LOCALIZATION NOTE (shortcuts.toggleCondPanel): text describing\n# keyboard shortcut action for toggling conditional panel keyboard\nshortcuts.toggleCondPanel=Toggle Conditional Panel\n\n# LOCALIZATION NOTE (shortcuts.pauseOrResume): text describing\n# keyboard shortcut action for pause of resume\nshortcuts.pauseOrResume=Pause/Resume\n\n# LOCALIZATION NOTE (shortcuts.stepOver): text describing\n# keyboard shortcut action for stepping over\nshortcuts.stepOver=Step Over\n\n# LOCALIZATION NOTE (shortcuts.stepIn): text describing\n# keyboard shortcut action for stepping in\nshortcuts.stepIn=Step In\n\n# LOCALIZATION NOTE (shortcuts.stepOut): text describing\n# keyboard shortcut action for stepping out\nshortcuts.stepOut=Step Out\n\n# LOCALIZATION NOTE (shortcuts.fileSearch): text describing\n# keyboard shortcut action for source file search\nshortcuts.fileSearch=Source File Search\n\n# LOCALIZATION NOTE (shortcuts.searchAgain): text describing\n# keyboard shortcut action for searching again\nshortcuts.searchAgain=Search Again\n\n# LOCALIZATION NOTE (shortcuts.projectSearch): text describing\n# keyboard shortcut action for full project search\nshortcuts.projectSearch=Full Project Search\n\n# LOCALIZATION NOTE (shortcuts.functionSearch): text describing\n# keyboard shortcut action for function search\nshortcuts.functionSearch=Function Search\n\n# LOCALIZATION NOTE (shortcuts.buttonName): text describing\n# keyboard shortcut button text\nshortcuts.buttonName=Keyboard shortcuts\n"
 
 /***/ }),
 /* 961 */,
 /* 962 */,
 /* 963 */,
 /* 964 */,
 /* 965 */
 /***/ (function(module, exports) {
@@ -17420,16 +17420,18 @@ var _sourceSearch = __webpack_require__(
 var sourceSearchUtils = _interopRequireWildcard(_sourceSearch);
 
 var _wasm = __webpack_require__(1401);
 
 var _ui = __webpack_require__(1439);
 
 var _devtoolsSourceEditor = __webpack_require__(1386);
 
+var _devtoolsSourceMap = __webpack_require__(1360);
+
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 const { findNext, findPrev } = sourceSearchUtils;
 
 function shouldShowPrettyPrint(selectedSource) {
   if (!selectedSource) {
     return false;
   }
@@ -17437,18 +17439,20 @@ function shouldShowPrettyPrint(selectedS
   selectedSource = selectedSource.toJS();
   return (0, _source.shouldPrettyPrint)(selectedSource);
 }
 
 function shouldShowFooter(selectedSource, horizontal) {
   if (!horizontal) {
     return true;
   }
-
-  return shouldShowPrettyPrint(selectedSource);
+  if (!selectedSource) {
+    return false;
+  }
+  return shouldShowPrettyPrint(selectedSource) || (0, _devtoolsSourceMap.isOriginalId)(selectedSource.get("id"));
 }
 
 function traverseResults(e, ctx, query, dir, modifiers) {
   e.stopPropagation();
   e.preventDefault();
 
   if (dir == "prev") {
     findPrev(ctx, query, true, modifiers);
@@ -18395,32 +18399,35 @@ var _extends = Object.assign || function
  */
 
 exports.initialState = initialState;
 exports.removeSourceFromTabList = removeSourceFromTabList;
 exports.removeSourcesFromTabList = removeSourcesFromTabList;
 exports.getNewSelectedSourceId = getNewSelectedSourceId;
 exports.getSource = getSource;
 exports.getSourceByURL = getSourceByURL;
+exports.getGeneratedSource = getGeneratedSource;
 exports.getPendingSelectedLocation = getPendingSelectedLocation;
 exports.getPrettySource = getPrettySource;
 exports.getSourceInSources = getSourceInSources;
 
 var _immutable = __webpack_require__(146);
 
 var I = _interopRequireWildcard(_immutable);
 
 var _reselect = __webpack_require__(993);
 
 var _makeRecord = __webpack_require__(1361);
 
 var _makeRecord2 = _interopRequireDefault(_makeRecord);
 
 var _source = __webpack_require__(1356);
 
+var _devtoolsSourceMap = __webpack_require__(1360);
+
 var _prefs = __webpack_require__(226);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
 
 function initialState() {
   return (0, _makeRecord2.default)({
@@ -18662,16 +18669,23 @@ const getSourcesState = state => state.s
 function getSource(state, id) {
   return getSourceInSources(getSources(state), id);
 }
 
 function getSourceByURL(state, url) {
   return getSourceByUrlInSources(state.sources.sources, url);
 }
 
+function getGeneratedSource(state, source) {
+  if (!(0, _devtoolsSourceMap.isOriginalId)(source.id)) {
+    return null;
+  }
+  return getSource(state, (0, _devtoolsSourceMap.originalToGeneratedId)(source.id));
+}
+
 function getPendingSelectedLocation(state) {
   return state.sources.pendingSelectedLocation;
 }
 
 function getPrettySource(state, id) {
   const source = getSource(state, id);
   if (!source) {
     return;
@@ -19876,16 +19890,19 @@ const libraryMap = [{
   pattern: /node_modules\/express/
 }, {
   label: "Pug",
   pattern: /node_modules\/pug/
 }, {
   label: "ExtJS",
   pattern: /\/ext-all[\.\-]/
 }, {
+  label: "MobX",
+  pattern: /mobx/i
+}, {
   label: "Underscore",
   pattern: /underscore/i
 }, {
   label: "Lodash",
   pattern: /lodash/i
 }, {
   label: "Ember",
   pattern: /ember/i
@@ -34024,16 +34041,17 @@ const svg = {
   worker: __webpack_require__(372),
   "sad-face": __webpack_require__(1347),
   refresh: __webpack_require__(1348),
   webpack: __webpack_require__(1001),
   node: __webpack_require__(1002),
   express: __webpack_require__(1003),
   pug: __webpack_require__(1004),
   extjs: __webpack_require__(1043),
+  mobx: __webpack_require__(1733),
   marko: __webpack_require__(1649),
   nextjs: __webpack_require__(1650),
   showSources: __webpack_require__(1044),
   showOutline: __webpack_require__(1045),
   nuxtjs: __webpack_require__(1651)
 };
 
 function Svg({ name, className, onClick, "aria-label": ariaLabel }) {
@@ -35290,25 +35308,23 @@ Object.defineProperty(exports, "__esModu
 var _react = __webpack_require__(0);
 
 var _react2 = _interopRequireDefault(_react);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
-var _redux = __webpack_require__(3);
-
 var _reactRedux = __webpack_require__(1189);
 
 var _selectors = __webpack_require__(1352);
 
-var _actions = __webpack_require__(1354);
-
-var _actions2 = _interopRequireDefault(_actions);
+var _sourceTree = __webpack_require__(1532);
+
+var _sources = __webpack_require__(1373);
 
 var _ManagedTree = __webpack_require__(1404);
 
 var _ManagedTree2 = _interopRequireDefault(_ManagedTree);
 
 var _Svg = __webpack_require__(1359);
 
 var _Svg2 = _interopRequireDefault(_Svg);
@@ -35535,41 +35551,49 @@ class SourcesTree extends _react.Compone
         " ",
         item.name,
         " "
       )
     );
   }
 
   render() {
-    const { setExpandedState, expanded } = this.props;
+    const expanded = this.props.expanded;
     const {
       focusedItem,
       sourceTree,
       parentMap,
       listItems,
       highlightItems
     } = this.state;
 
+    const onExpand = (item, expandedState) => {
+      this.props.setExpandedState(expandedState);
+    };
+
+    const onCollapse = (item, expandedState) => {
+      this.props.setExpandedState(expandedState);
+    };
+
     const isEmpty = sourceTree.contents.length === 0;
     const treeProps = {
       key: isEmpty ? "empty" : "full",
       getParent: item => parentMap.get(item),
       getChildren: item => (0, _sourcesTree.nodeHasChildren)(item) ? item.contents : [],
       getRoots: () => sourceTree.contents,
       getPath: this.getPath,
       itemHeight: 21,
       autoExpandDepth: expanded ? 0 : 1,
       autoExpandAll: false,
       onFocus: this.focusItem,
       listItems,
       highlightItems,
       expanded,
-      onExpand: (item, expandedState) => setExpandedState(expandedState),
-      onCollapse: (item, expandedState) => setExpandedState(expandedState),
+      onExpand,
+      onCollapse,
       renderItem: this.renderItem
     };
 
     const tree = _react2.default.createElement(_ManagedTree2.default, treeProps);
 
     if (isEmpty) {
       return _react2.default.createElement(
         "div",
@@ -35602,26 +35626,35 @@ class SourcesTree extends _react.Compone
 
 
 // Redux
 /* 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/>. */
 
 // React
-exports.default = (0, _reactRedux.connect)(state => {
+
+
+const mapStateToProps = state => {
   return {
     shownSource: (0, _selectors.getShownSource)(state),
     selectedSource: (0, _selectors.getSelectedSource)(state),
     debuggeeUrl: (0, _selectors.getDebuggeeUrl)(state),
     expanded: (0, _selectors.getExpandedState)(state),
     projectRoot: (0, _selectors.getProjectDirectoryRoot)(state),
     sources: (0, _selectors.getSources)(state)
   };
-}, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourcesTree);
+};
+
+const actionCreators = {
+  setExpandedState: _sourceTree.setExpandedState,
+  selectSource: _sources.selectSource
+};
+
+exports.default = (0, _reactRedux.connect)(mapStateToProps, actionCreators)(SourcesTree);
 
 /***/ }),
 /* 1554 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
 
@@ -36242,26 +36275,32 @@ var _selectors = __webpack_require__(135
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
 var _devtoolsConfig = __webpack_require__(1355);
 
 var _source = __webpack_require__(1356);
 
+var _sources = __webpack_require__(1369);
+
 var _editor = __webpack_require__(1358);
 
 var _PaneToggle = __webpack_require__(1407);
 
 var _PaneToggle2 = _interopRequireDefault(_PaneToggle);
 
 __webpack_require__(1322);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
+/* 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/>. */
+
 class SourceFooter extends _react.PureComponent {
   prettyPrintButton() {
     const { selectedSource, togglePrettyPrint } = this.props;
     const sourceLoaded = selectedSource && (0, _source.isLoaded)(selectedSource.toJS());
 
     if (!(0, _editor.shouldShowPrettyPrint)(selectedSource)) {
       return;
     }
@@ -36366,39 +36405,69 @@ class SourceFooter extends _react.PureCo
       { className: "commands" },
       this.prettyPrintButton(),
       this.blackBoxButton(),
       this.blackBoxSummary(),
       this.coverageButton()
     );
   }
 
+  renderSourceSummary() {
+    const { mappedSource, jumpToMappedLocation, selectedSource } = this.props;
+    if (mappedSource) {
+      const bundleSource = mappedSource.toJS();
+      const filename = (0, _source.getFilename)(bundleSource);
+      const tooltip = L10N.getFormatStr("sourceFooter.mappedSourceTooltip", filename);
+      const title = L10N.getFormatStr("sourceFooter.mappedSource", filename);
+      const mappedSourceLocation = {
+        sourceId: selectedSource.get("id"),
+        line: 1,
+        column: 1
+      };
+      return _react2.default.createElement(
+        "button",
+        {
+          className: "mapped-source",
+          onClick: () => jumpToMappedLocation(mappedSourceLocation),
+          title: tooltip
+        },
+        _react2.default.createElement(
+          "span",
+          null,
+          title
+        )
+      );
+    }
+    return null;
+  }
+
   render() {
     const { selectedSource, horizontal } = this.props;
 
     if (!(0, _editor.shouldShowFooter)(selectedSource, horizontal)) {
       return null;
     }
 
     return _react2.default.createElement(
       "div",
       { className: "source-footer" },
       this.renderCommands(),
+      this.renderSourceSummary(),
       this.renderToggleButton()
     );
   }
-} /* 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/>. */
+}
 
 exports.default = (0, _reactRedux.connect)(state => {
   const selectedSource = (0, _selectors.getSelectedSource)(state);
   const selectedId = selectedSource && selectedSource.get("id");
+  const source = selectedSource.toJS();
   return {
     selectedSource,
+    mappedSource: (0, _sources.getGeneratedSource)(state, source),
     prettySource: (0, _selectors.getPrettySource)(state, selectedId),
     endPanelCollapsed: (0, _selectors.getPaneCollapse)(state, "end")
   };
 }, dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(SourceFooter);
 
 /***/ }),
 /* 1556 */
 /***/ (function(module, exports, __webpack_require__) {
@@ -39841,44 +39910,42 @@ class Breakpoints extends _react.Compone
     if (nextProps.selectedSource && !(0, _source.isLoaded)(nextProps.selectedSource.toJS())) {
       return false;
     }
 
     return true;
   }
 
   render() {
-    const { breakpoints, selectedSource, editor, sourceMetaData } = this.props;
+    const { breakpoints, selectedSource, editor } = this.props;
 
     if (!selectedSource || !breakpoints || selectedSource.get("isBlackBoxed")) {
       return null;
     }
 
     return _react2.default.createElement(
       "div",
       null,
       breakpoints.valueSeq().map(bp => {
         return _react2.default.createElement(_Breakpoint2.default, {
           key: (0, _breakpoint.makeLocationId)(bp.location),
           breakpoint: bp,
           selectedSource: selectedSource,
-          sourceMetaData: sourceMetaData,
           editor: editor
         });
       })
     );
   }
 } /* 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/>. */
 
 exports.default = (0, _reactRedux.connect)(state => ({
   breakpoints: (0, _visibleBreakpoints2.default)(state),
-  selectedSource: (0, _selectors.getSelectedSource)(state),
-  sourceMetaData: (0, _selectors.getSourceMetaData)(state, (0, _selectors.getSelectedSource)(state).id)
+  selectedSource: (0, _selectors.getSelectedSource)(state)
 }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
 
 /***/ }),
 /* 1589 */
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -39929,34 +39996,32 @@ function makeMarker(isDisabled) {
 class Breakpoint extends _react.Component {
 
   constructor() {
     super();
     this.addBreakpoint = this.addBreakpoint.bind(this);
   }
 
   addBreakpoint() {
-    const { breakpoint, editor, selectedSource, sourceMetaData } = this.props;
+    const { breakpoint, editor, selectedSource } = this.props;
 
     // Hidden Breakpoints are never rendered on the client
     if (breakpoint.hidden) {
       return;
     }
 
     // NOTE: we need to wait for the breakpoint to be loaded
     // to get the generated location
     if (!selectedSource || breakpoint.loading) {
       return;
     }
 
     const sourceId = selectedSource.get("id");
     const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);
 
-    (0, _editor.showSourceText)(editor, selectedSource.toJS(), sourceMetaData);
-
     editor.codeMirror.setGutterMarker(line, "breakpoints", makeMarker(breakpoint.disabled));
 
     editor.codeMirror.addLineClass(line, "line", "new-breakpoint");
     if (breakpoint.condition) {
       editor.codeMirror.addLineClass(line, "line", "has-condition");
     } else {
       editor.codeMirror.removeLineClass(line, "line", "has-condition");
     }
@@ -41319,33 +41384,30 @@ class SecondaryPanes extends _react.Comp
       buttons: this.watchExpressionHeaderButtons(),
       component: _Expressions2.default,
       opened: true
     };
   }
 
   getStartItems() {
     const scopesContent = this.props.horizontal ? this.getScopeItem() : null;
-    const isPaused = () => !!this.props.pauseData;
-
     const items = [{
       header: L10N.getStr("breakpoints.header"),
       className: "breakpoints-pane",
       buttons: this.renderBreakpointsToggle(),
       component: _Breakpoints2.default,
       opened: true
     }, {
       header: L10N.getStr("callStack.header"),
       className: "call-stack-pane",
       component: _Frames2.default,
       opened: _prefs.prefs.callStackVisible,
       onToggle: opened => {
         _prefs.prefs.callStackVisible = opened;
-      },
-      shouldOpen: isPaused
+      }
     }, scopesContent];
 
     if ((0, _devtoolsConfig.isEnabled)("eventListeners")) {
       items.push({
         header: L10N.getStr("eventListenersHeader"),
         className: "event-listeners-pane",
         component: _EventListeners2.default
       });
@@ -46017,11 +46079,44 @@ const Badge = ({ children }) => _react2.
 exports.default = Badge;
 
 /***/ }),
 /* 1705 */
 /***/ (function(module, exports) {
 
 // removed by extract-text-webpack-plugin
 
+/***/ }),
+/* 1706 */,
+/* 1707 */,
+/* 1708 */,
+/* 1709 */,
+/* 1710 */,
+/* 1711 */,
+/* 1712 */,
+/* 1713 */,
+/* 1714 */,
+/* 1715 */,
+/* 1716 */,
+/* 1717 */,
+/* 1718 */,
+/* 1719 */,
+/* 1720 */,
+/* 1721 */,
+/* 1722 */,
+/* 1723 */,
+/* 1724 */,
+/* 1725 */,
+/* 1726 */,
+/* 1727 */,
+/* 1728 */,
+/* 1729 */,
+/* 1730 */,
+/* 1731 */,
+/* 1732 */,
+/* 1733 */
+/***/ (function(module, exports) {
+
+module.exports = "<svg version=\"1.1\" id=\"Layer_1\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" x=\"0px\" y=\"0px\" viewBox=\"0 0 1200 1200\" style=\"enable-background:new 0 0 1200 1200;\" xml:space=\"preserve\"><style type=\"text/css\"> .st0{clip-path:url(#SVGID_2_);} .st1{fill:url(#SVGID_3_);} .st2{fill:url(#SVGID_4_);} .st3{opacity:0.28;fill:#C24411;} .st4{fill:#FFFFFF;} </style><g><defs><path id=\"SVGID_1_\" d=\"M1088.7,1165.5H111.3c-42.4,0-76.7-34.4-76.7-76.7V111.3c0-42.4,34.4-76.7,76.7-76.7h977.4 c42.4,0,76.7,34.4,76.7,76.7v977.4C1165.5,1131.1,1131.1,1165.5,1088.7,1165.5z\"></path></defs><clipPath id=\"SVGID_2_\"><use xlink:href=\"#SVGID_1_\" style=\"overflow:visible;\"></use></clipPath><g class=\"st0\"><linearGradient id=\"SVGID_3_\" gradientUnits=\"userSpaceOnUse\" x1=\"426.6738\" y1=\"482.4993\" x2=\"1284.413\" y2=\"1239.9835\"><stop offset=\"0\" style=\"stop-color:#F77122\"></stop><stop offset=\"1\" style=\"stop-color:#D6560A\"></stop></linearGradient><polygon class=\"st1\" points=\"573.6,1896.7 -87.9,1286.6 1204.5,-115 1866,495 \"></polygon><linearGradient id=\"SVGID_4_\" gradientUnits=\"userSpaceOnUse\" x1=\"1128.9907\" y1=\"-135.8044\" x2=\"-496.2911\" y2=\"788.1552\"><stop offset=\"0\" style=\"stop-color:#D6560A\"></stop><stop offset=\"1\" style=\"stop-color:#F77122\"></stop></linearGradient><polygon class=\"st2\" points=\"-66.9,1307.6 -644.1,775.5 648.4,-626.1 1225.5,-94 \"></polygon><rect x=\"-143.7\" y=\"-143.6\" transform=\"matrix(0.9658 -0.2591 0.2591 0.9658 -201.3159 92.1307)\" class=\"st3\" width=\"785\" height=\"1906.5\"></rect></g></g><g><path class=\"st4\" d=\"M384.9,900.1H245.4v-45.3h85.6V345.2h-85.6v-45.3h139.5V900.1z\"></path><path class=\"st4\" d=\"M745.7,463c-16.1,60.4-35,118.2-56.7,173.6c-21.7,55.4-42.7,105-62.9,148.8H574 c-20.3-43.8-41.2-93.4-62.9-148.8c-21.7-55.4-40.6-113.2-56.7-173.6h62c5,20.3,10.8,41.9,17.7,64.8s14,45.7,21.7,68.2 c7.6,22.5,15.4,44.3,23.2,65.4c7.8,21.1,15.3,39.9,22.3,56.4c7-16.5,14.5-35.3,22.3-56.4c7.8-21.1,15.6-42.9,23.2-65.4 c7.6-22.5,14.9-45.3,21.7-68.2s12.7-44.5,17.7-64.8H745.7z\"></path><path class=\"st4\" d=\"M815.1,299.9h139.5v45.3h-85.6v509.6h85.6v45.3H815.1V299.9z\"></path></g></svg>"
+
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -27462,76 +27462,18 @@ module.exports = function(module) {
 		module.webpackPolyfill = 1;
 	}
 	return module;
 };
 
 
 /***/ }),
 /* 794 */,
-/* 795 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var createToPairs = __webpack_require__(812),
-    keys = __webpack_require__(205);
-
-/**
- * Creates an array of own enumerable string keyed-value pairs for `object`
- * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
- * entries are returned.
- *
- * @static
- * @memberOf _
- * @since 4.0.0
- * @alias entries
- * @category Object
- * @param {Object} object The object to query.
- * @returns {Array} Returns the key-value pairs.
- * @example
- *
- * function Foo() {
- *   this.a = 1;
- *   this.b = 2;
- * }
- *
- * Foo.prototype.c = 3;
- *
- * _.toPairs(new Foo);
- * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
- */
-var toPairs = createToPairs(keys);
-
-module.exports = toPairs;
-
-
-/***/ }),
-/* 796 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var arrayMap = __webpack_require__(110);
-
-/**
- * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
- * of key-value pairs for `object` corresponding to the property names of `props`.
- *
- * @private
- * @param {Object} object The object to query.
- * @param {Array} props The property names to get values for.
- * @returns {Object} Returns the key-value pairs.
- */
-function baseToPairs(object, props) {
-  return arrayMap(props, function(key) {
-    return [key, object[key]];
-  });
-}
-
-module.exports = baseToPairs;
-
-
-/***/ }),
+/* 795 */,
+/* 796 */,
 /* 797 */,
 /* 798 */,
 /* 799 */
 /***/ (function(module, exports, __webpack_require__) {
 
 var baseIsEqualDeep = __webpack_require__(832),
     isObjectLike = __webpack_require__(14);
 
@@ -27706,17 +27648,17 @@ module.exports = matchesStrictComparable
 /* 807 */,
 /* 808 */,
 /* 809 */,
 /* 810 */,
 /* 811 */,
 /* 812 */
 /***/ (function(module, exports, __webpack_require__) {
 
-var baseToPairs = __webpack_require__(796),
+var baseToPairs = __webpack_require__(1757),
     getTag = __webpack_require__(198),
     mapToArray = __webpack_require__(203),
     setToPairs = __webpack_require__(813);
 
 /** `Object#toString` result references. */
 var mapTag = '[object Map]',
     setTag = '[object Set]';
 
@@ -36611,17 +36553,17 @@ self.onmessage = workerHandler({
 
 Object.defineProperty(exports, "__esModule", {
   value: true
 });
 exports.getVariablesInLocalScope = getVariablesInLocalScope;
 exports.getVariablesInScope = getVariablesInScope;
 exports.isExpressionInScope = isExpressionInScope;
 
-var _toPairs = __webpack_require__(795);
+var _toPairs = __webpack_require__(1756);
 
 var _toPairs2 = _interopRequireDefault(_toPairs);
 
 var _uniq = __webpack_require__(561);
 
 var _uniq2 = _interopRequireDefault(_uniq);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -41817,11 +41759,123 @@ function extendsComponent(classes) {
     if (classObj.parent.name === "Component" || classObj.parent.name === "PureComponent") {
       result = true;
     }
   });
 
   return result;
 }
 
+/***/ }),
+/* 1704 */,
+/* 1705 */,
+/* 1706 */,
+/* 1707 */,
+/* 1708 */,
+/* 1709 */,
+/* 1710 */,
+/* 1711 */,
+/* 1712 */,
+/* 1713 */,
+/* 1714 */,
+/* 1715 */,
+/* 1716 */,
+/* 1717 */,
+/* 1718 */,
+/* 1719 */,
+/* 1720 */,
+/* 1721 */,
+/* 1722 */,
+/* 1723 */,
+/* 1724 */,
+/* 1725 */,
+/* 1726 */,
+/* 1727 */,
+/* 1728 */,
+/* 1729 */,
+/* 1730 */,
+/* 1731 */,
+/* 1732 */,
+/* 1733 */,
+/* 1734 */,
+/* 1735 */,
+/* 1736 */,
+/* 1737 */,
+/* 1738 */,
+/* 1739 */,
+/* 1740 */,
+/* 1741 */,
+/* 1742 */,
+/* 1743 */,
+/* 1744 */,
+/* 1745 */,
+/* 1746 */,
+/* 1747 */,
+/* 1748 */,
+/* 1749 */,
+/* 1750 */,
+/* 1751 */,
+/* 1752 */,
+/* 1753 */,
+/* 1754 */,
+/* 1755 */,
+/* 1756 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var createToPairs = __webpack_require__(812),
+    keys = __webpack_require__(205);
+
+/**
+ * Creates an array of own enumerable string keyed-value pairs for `object`
+ * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
+ * entries are returned.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @alias entries
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the key-value pairs.
+ * @example
+ *
+ * function Foo() {
+ *   this.a = 1;
+ *   this.b = 2;
+ * }
+ *
+ * Foo.prototype.c = 3;
+ *
+ * _.toPairs(new Foo);
+ * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
+ */
+var toPairs = createToPairs(keys);
+
+module.exports = toPairs;
+
+
+/***/ }),
+/* 1757 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var arrayMap = __webpack_require__(110);
+
+/**
+ * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
+ * of key-value pairs for `object` corresponding to the property names of `props`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} props The property names to get values for.
+ * @returns {Object} Returns the key-value pairs.
+ */
+function baseToPairs(object, props) {
+  return arrayMap(props, function(key) {
+    return [key, object[key]];
+  });
+}
+
+module.exports = baseToPairs;
+
+
 /***/ })
 /******/ ]);
 });
\ No newline at end of file
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-debugger-buttons.js
@@ -38,17 +38,17 @@ add_task(async function() {
   const dbg = await initDebugger("doc-debugger-statements.html");
 
   await reload(dbg);
   await waitForPaused(dbg);
   await waitForLoadedSource(dbg, "debugger-statements.html");
   assertPausedLocation(dbg);
 
   // resume
-  await clickResume(dbg)
+  await clickResume(dbg);
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
 
   // step over
   await clickStepOver(dbg);
   assertPausedLocation(dbg);
 
   // step into
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-editor-select.js
@@ -42,10 +42,13 @@ add_task(async function() {
   let longSrc = findSource(dbg, "long.js");
   await addBreakpoint(dbg, longSrc, 66);
 
   invokeInTab("testModel");
   await waitForPaused(dbg);
   await waitForLoadedSource(dbg, "long.js");
 
   assertPausedLocation(dbg);
-  ok(isVisibleInEditor(dbg, findElement(dbg, "breakpoint")), "Breakpoint is visible");
+  ok(
+    isVisibleInEditor(dbg, findElement(dbg, "breakpoint")),
+    "Breakpoint is visible"
+  );
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-expressions-error.js
@@ -42,40 +42,39 @@ async function addExpression(dbg, input)
   pressKey(dbg, "Enter");
   await waitForDispatch(dbg, "EVALUATE_EXPRESSION");
 }
 
 async function editExpression(dbg, input) {
   info("updating the expression");
   dblClickElement(dbg, "expressionNode", 1);
   // Position cursor reliably at the end of the text.
-  const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION")
+  const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION");
   pressKey(dbg, "End");
   type(dbg, input);
   pressKey(dbg, "Enter");
   await evaluation;
 }
 
 /*
  * When we add a bad expression, we'll pause,
  * resume, and wait for the expression to finish being evaluated.
  */
 async function addBadExpression(dbg, input) {
-  const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION")
+  const evaluation = waitForDispatch(dbg, "EVALUATE_EXPRESSION");
 
   findElementWithSelector(dbg, expressionSelectors.input).focus();
   type(dbg, input);
   pressKey(dbg, "Enter");
 
   await waitForPaused(dbg);
 
   ok(dbg.selectors.isEvaluatingExpression(dbg.getState()));
   await resume(dbg);
   await evaluation;
-
 }
 
 add_task(async function() {
   const dbg = await initDebugger("doc-script-switching.html");
 
   await togglePauseOnExceptions(dbg, true, false);
 
   // add a good expression, 2 bad expressions, and another good one
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-pretty-print.js
@@ -27,13 +27,13 @@ add_task(async function() {
   await stepOver(dbg);
 
   assertPausedLocation(dbg);
 
   await resume(dbg);
 
   // The pretty-print button should go away in the pretty-printed
   // source.
-  ok(!findElement(dbg, "editorFooter"), "Footer is hidden");
+  ok(!findElement(dbg, "prettyPrintButton"), "Pretty Print Button is hidden");
 
   await selectSource(dbg, "math.min.js");
-  ok(findElement(dbg, "editorFooter"), "Footer is hidden");
+  ok(findElement(dbg, "prettyPrintButton"), "Pretty Print Button is visible");
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-sourcemaps2.js
@@ -1,15 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function assertBpInGutter(dbg, lineNumber) {
   const el = findElement(dbg, "breakpoint");
   const bpLineNumber = +el.querySelector(".CodeMirror-linenumber").innerText;
-  is(bpLineNumber, lineNumber, "Breakpoint is on the correct line in the gutter");
+  is(
+    bpLineNumber,
+    lineNumber,
+    "Breakpoint is on the correct line in the gutter"
+  );
 }
 
 // Tests loading sourcemapped sources, setting breakpoints, and
 // stepping in them.
 
 // This source map does not have source contents, so it's fetched separately
 add_task(async function() {
   // NOTE: the CORS call makes the test run times inconsistent
@@ -33,9 +37,17 @@ add_task(async function() {
     "Breakpoint has correct line"
   );
 
   assertBpInGutter(dbg, 4);
   invokeInTab("logMessage");
 
   await waitForPaused(dbg);
   assertPausedLocation(dbg);
+
+  // Tests the existence of the sourcemap link in the original source.
+  ok(findElement(dbg, "sourceMapLink"), "Sourcemap link in original source");
+  await selectSource(dbg, "main.min.js");
+  ok(
+    !findElement(dbg, "sourceMapLink"),
+    "No Sourcemap link exists in generated source"
+  );
 });
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-wasm-sourcemaps.js
@@ -21,16 +21,17 @@ add_task(async function() {
   await waitForSource(dbg, "wasm-sourcemaps/average.c");
   await addBreakpoint(dbg, "wasm-sourcemaps/average.c", 12);
 
   clickElement(dbg, "resume");
 
   await waitForPaused(dbg);
   await waitForLoadedSource(dbg, "average.c");
   assertPausedLocation(dbg);
+  toggleCallStack(dbg);
 
   const frames = findAllElements(dbg, "frames");
   const firstFrameTitle = frames[0].querySelector(".title").textContent;
   is(firstFrameTitle, "(wasmcall)", "It shall be a wasm call");
   const firstFrameLocation = frames[0].querySelector(".location").textContent;
   is(
     firstFrameLocation.includes("average.c"),
     true,
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -290,28 +290,31 @@ function assertPausedLocation(dbg) {
 function assertDebugLine(dbg, line) {
   // Check the debug line
   const lineInfo = getCM(dbg).lineInfo(line - 1);
   const source = dbg.selectors.getSelectedSource(dbg.getState());
   if (source && source.get("loadedState") == "loading") {
     const url = source.get("url");
     ok(
       false,
-      `Looks like the source ${url} is still loading. Try adding waitForLoadedSource in the test.`
+      `Looks like the source ${
+        url
+      } is still loading. Try adding waitForLoadedSource in the test.`
     );
     return;
   }
 
   ok(
     lineInfo.wrapClass.includes("new-debug-line"),
     "Line is highlighted as paused"
   );
 
-  const debugLine = findElementWithSelector(dbg, ".new-debug-line")
-                    || findElementWithSelector(dbg, ".new-debug-line-error");
+  const debugLine =
+    findElementWithSelector(dbg, ".new-debug-line") ||
+    findElementWithSelector(dbg, ".new-debug-line-error");
 
   ok(isVisibleInEditor(dbg, debugLine), "debug line is visible");
 
   const markedSpans = lineInfo.handle.markedSpans;
   if (markedSpans && markedSpans.length > 0) {
     const marker = markedSpans[0].marker;
     ok(
       marker.className.includes("debug-expression"),
@@ -548,18 +551,21 @@ function waitForLoadedSource(dbg, url) {
     "loaded source"
   );
 }
 
 function waitForLoadedSources(dbg) {
   return waitForState(
     dbg,
     state => {
-      const sources = dbg.selectors.getSources(state).valueSeq().toJS()
-      return !sources.some(source => source.loadedState == "loading")
+      const sources = dbg.selectors
+        .getSources(state)
+        .valueSeq()
+        .toJS();
+      return !sources.some(source => source.loadedState == "loading");
     },
     "loaded source"
   );
 }
 /**
  * Selects the source.
  *
  * @memberof mochitest/actions
@@ -830,17 +836,16 @@ function pressKey(dbg, keyName) {
   const { code, modifiers } = keyEvent;
   return EventUtils.synthesizeKey(code, modifiers || {}, dbg.win);
 }
 
 function type(dbg, string) {
   string.split("").forEach(char => EventUtils.synthesizeKey(char, {}, dbg.win));
 }
 
-
 /*
  * Checks to see if the inner element is visible inside the editor.
  *
  * @memberof mochitest/helpers
  * @param {Object} dbg
  * @param {HTMLElement} inner element
  * @return {boolean}
  * @static
@@ -869,34 +874,38 @@ function isVisible(outerEl, innerEl) {
   if (!innerEl || !outerEl) {
     return false;
   }
 
   const innerRect = innerEl.getBoundingClientRect();
   const outerRect = outerEl.getBoundingClientRect();
 
   const verticallyVisible =
-    (innerRect.top >= outerRect.top || innerRect.bottom <= outerRect.bottom)
-    || (innerRect.top < outerRect.top && innerRect.bottom > outerRect.bottom);
+    innerRect.top >= outerRect.top ||
+    innerRect.bottom <= outerRect.bottom ||
+    (innerRect.top < outerRect.top && innerRect.bottom > outerRect.bottom);
 
   const horizontallyVisible =
-    (innerRect.left >= outerRect.left || innerRect.right <= outerRect.right)
-    || (innerRect.left < outerRect.left && innerRect.right > outerRect.right);
+    innerRect.left >= outerRect.left ||
+    innerRect.right <= outerRect.right ||
+    (innerRect.left < outerRect.left && innerRect.right > outerRect.right);
 
   const visible = verticallyVisible && horizontallyVisible;
   return visible;
 }
 
 const selectors = {
   callStackHeader: ".call-stack-pane ._header",
   callStackBody: ".call-stack-pane .pane",
   expressionNode: i =>
     `.expressions-list .expression-container:nth-child(${i}) .object-label`,
   expressionValue: i =>
-    `.expressions-list .expression-container:nth-child(${i}) .object-delimiter + *`,
+    `.expressions-list .expression-container:nth-child(${
+      i
+    }) .object-delimiter + *`,
   expressionClose: i =>
     `.expressions-list .expression-container:nth-child(${i}) .close`,
   expressionNodes: ".expressions-list .tree-node",
   scopesHeader: ".scopes-pane ._header",
   breakpointItem: i => `.breakpoints-list .breakpoint:nth-child(${i})`,
   scopeNode: i => `.scopes-list .tree-node:nth-child(${i}) .object-label`,
   scopeValue: i =>
     `.scopes-list .tree-node:nth-child(${i}) .object-delimiter + *`,
@@ -909,17 +918,18 @@ const selectors = {
   highlightLine: ".CodeMirror-code > .highlight-line",
   codeMirror: ".CodeMirror",
   resume: ".resume.active",
   sourceTabs: ".source-tabs",
   stepOver: ".stepOver.active",
   stepOut: ".stepOut.active",
   stepIn: ".stepIn.active",
   toggleBreakpoints: ".breakpoints-toggle",
-  prettyPrintButton: ".prettyPrint",
+  prettyPrintButton: ".source-footer .prettyPrint",
+  sourceMapLink: ".source-footer .mapped-source",
   sourcesFooter: ".sources-panel .source-footer",
   editorFooter: ".editor-pane .source-footer",
   sourceNode: i => `.sources-list .tree-node:nth-child(${i})`,
   sourceNodes: ".sources-list .tree-node",
   sourceArrow: i => `.sources-list .tree-node:nth-child(${i}) .arrow`,
   resultItems: ".result-list .result-item",
   fileMatch: ".managed-tree .result",
   popup: ".popover",
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/components/LiveText.js
@@ -0,0 +1,45 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript 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/. */
+
+"use strict";
+
+define(function (require, exports, module) {
+  const { Component } = require("devtools/client/shared/vendor/react");
+  const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+  const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
+  const { pre } = require("devtools/client/shared/vendor/react-dom-factories");
+
+  /**
+   * This object represents a live DOM text node in a <pre>.
+   */
+  class LiveText extends Component {
+    static get propTypes() {
+      return {
+        data: PropTypes.instanceOf(Text),
+      };
+    }
+
+    componentDidMount() {
+      this.componentDidUpdate();
+    }
+
+    componentDidUpdate() {
+      let el = findDOMNode(this);
+      if (el.firstChild === this.props.data) {
+        return;
+      }
+      el.textContent = "";
+      el.append(this.props.data);
+    }
+
+    render() {
+      return pre({className: "data"});
+    }
+  }
+
+  // Exports from this module
+  exports.LiveText = LiveText;
+});
--- a/devtools/client/jsonview/components/MainTabbedArea.js
+++ b/devtools/client/jsonview/components/MainTabbedArea.js
@@ -17,17 +17,17 @@ define(function (require, exports, modul
 
   /**
    * This object represents the root application template
    * responsible for rendering the basic tab layout.
    */
   class MainTabbedArea extends Component {
     static get propTypes() {
       return {
-        jsonText: PropTypes.string,
+        jsonText: PropTypes.instanceOf(Text),
         tabActive: PropTypes.number,
         actions: PropTypes.object,
         headers: PropTypes.object,
         searchFilter: PropTypes.string,
         json: PropTypes.oneOfType([
           PropTypes.string,
           PropTypes.object,
           PropTypes.array,
@@ -37,18 +37,18 @@ define(function (require, exports, modul
         expandedNodes: PropTypes.instanceOf(Set),
       };
     }
 
     constructor(props) {
       super(props);
 
       this.state = {
-        json: {},
-        headers: {},
+        json: props.json,
+        expandedNodes: props.expandedNodes,
         jsonText: props.jsonText,
         tabActive: props.tabActive
       };
 
       this.onTabChanged = this.onTabChanged.bind(this);
     }
 
     onTabChanged(index) {
@@ -59,27 +59,28 @@ define(function (require, exports, modul
       return (
         Tabs({
           tabActive: this.state.tabActive,
           onAfterChange: this.onTabChanged},
           TabPanel({
             className: "json",
             title: JSONView.Locale.$STR("jsonViewer.tab.JSON")},
             JsonPanel({
-              data: this.props.json,
+              data: this.state.json,
               expandedNodes: this.props.expandedNodes,
               actions: this.props.actions,
               searchFilter: this.state.searchFilter
             })
           ),
           TabPanel({
             className: "rawdata",
             title: JSONView.Locale.$STR("jsonViewer.tab.RawData")},
             TextPanel({
-              isValidJson: !(this.props.json instanceof Error),
+              isValidJson: !(this.state.json instanceof Error) &&
+                           document.readyState != "loading",
               data: this.state.jsonText,
               actions: this.props.actions
             })
           ),
           TabPanel({
             className: "headers",
             title: JSONView.Locale.$STR("jsonViewer.tab.Headers")},
             HeadersPanel({
--- a/devtools/client/jsonview/components/TextPanel.js
+++ b/devtools/client/jsonview/components/TextPanel.js
@@ -7,48 +7,46 @@
 "use strict";
 
 define(function (require, exports, module) {
   const { Component } = require("devtools/client/shared/vendor/react");
   const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
   const dom = require("devtools/client/shared/vendor/react-dom-factories");
   const { createFactories } = require("devtools/client/shared/react-utils");
   const { TextToolbar } = createFactories(require("./TextToolbar"));
-
-  const { div, pre } = dom;
+  const { LiveText } = createFactories(require("./LiveText"));
+  const { div } = dom;
 
   /**
    * This template represents the 'Raw Data' panel displaying
    * JSON as a text received from the server.
    */
   class TextPanel extends Component {
     static get propTypes() {
       return {
         isValidJson: PropTypes.bool,
         actions: PropTypes.object,
-        data: PropTypes.string
+        data: PropTypes.instanceOf(Text),
       };
     }
 
     constructor(props) {
       super(props);
       this.state = {};
     }
 
     render() {
       return (
         div({className: "textPanelBox tab-panel-inner"},
           TextToolbar({
             actions: this.props.actions,
             isValidJson: this.props.isValidJson
           }),
           div({className: "panelContent"},
-            pre({className: "data"},
-              this.props.data
-            )
+            LiveText({data: this.props.data})
           )
         )
       );
     }
   }
 
   // Exports from this module
   exports.TextPanel = TextPanel;
--- a/devtools/client/jsonview/components/moz.build
+++ b/devtools/client/jsonview/components/moz.build
@@ -9,13 +9,14 @@ DIRS += [
 ]
 
 DevToolsModules(
     'Headers.js',
     'HeadersPanel.js',
     'HeadersToolbar.js',
     'JsonPanel.js',
     'JsonToolbar.js',
+    'LiveText.js',
     'MainTabbedArea.js',
     'SearchBox.js',
     'TextPanel.js',
     'TextToolbar.js'
 )
--- a/devtools/client/jsonview/converter-child.js
+++ b/devtools/client/jsonview/converter-child.js
@@ -163,16 +163,18 @@ function exportData(win, request) {
   let data = Cu.createObjectIn(win, {
     defineAs: "JSONView"
   });
 
   data.debug = debug;
 
   data.json = new win.Text();
 
+  data.readyState = "uninitialized";
+
   let Locale = {
     $STR: key => {
       try {
         return jsonViewStrings.GetStringFromName(key);
       } catch (err) {
         console.error(err);
         return undefined;
       }
@@ -236,27 +238,26 @@ function initialHTML(doc) {
       "dir": Services.locale.isAppLocaleRTL ? "rtl" : "ltr"
     }, [
       element("head", {}, [
         element("link", {
           rel: "stylesheet",
           type: "text/css",
           href: baseURI + "css/main.css",
         }),
-        element("script", {
-          src: baseURI + "lib/require.js",
-          "data-main": baseURI + "viewer-config.js",
-          defer: true,
-        })
       ]),
       element("body", {}, [
         element("div", {"id": "content"}, [
           element("div", {"id": "json"})
-        ])
-      ])
+        ]),
+        element("script", {
+          src: baseURI + "lib/require.js",
+          "data-main": baseURI + "viewer-config.js",
+        }),
+      ]),
     ]).outerHTML;
 }
 
 // We insert the received data into a text node, which should be appended into
 // the #json element so that the JSON is still displayed even if JS is disabled.
 // However, the HTML parser is not synchronous, so this function uses a mutation
 // observer to detect the creation of the element. Then the text node is appended.
 function insertJsonData(win, json) {
--- a/devtools/client/jsonview/json-viewer.js
+++ b/devtools/client/jsonview/json-viewer.js
@@ -14,51 +14,36 @@ define(function (require, exports, modul
 
   const AUTO_EXPAND_MAX_SIZE = 100 * 1024;
   const AUTO_EXPAND_MAX_LEVEL = 7;
 
   let prettyURL;
 
   // Application state object.
   let input = {
-    jsonText: JSONView.json.textContent,
+    jsonText: JSONView.json,
     jsonPretty: null,
     headers: JSONView.headers,
     tabActive: 0,
     prettified: false
   };
 
-  try {
-    input.json = JSON.parse(input.jsonText);
-  } catch (err) {
-    input.json = err;
-  }
-
-  // Expand the document by default if its size isn't bigger than 100KB.
-  if (!(input.json instanceof Error) && input.jsonText.length <= AUTO_EXPAND_MAX_SIZE) {
-    input.expandedNodes = TreeViewClass.getExpandedNodes(
-      input.json,
-      {maxLevel: AUTO_EXPAND_MAX_LEVEL}
-    );
-  } else {
-    input.expandedNodes = new Set();
-  }
-
   /**
    * Application actions/commands. This list implements all commands
    * available for the JSON viewer.
    */
   input.actions = {
     onCopyJson: function () {
-      copyString(input.prettified ? input.jsonPretty : input.jsonText);
+      let text = input.prettified ? input.jsonPretty : input.jsonText;
+      copyString(text.textContent);
     },
 
     onSaveJson: function () {
       if (input.prettified && !prettyURL) {
-        prettyURL = URL.createObjectURL(new window.Blob([input.jsonPretty]));
+        prettyURL = URL.createObjectURL(new window.Blob([input.jsonPretty.textContent]));
       }
       dispatchEvent("save", input.prettified ? prettyURL : null);
     },
 
     onCopyHeaders: function () {
       let value = "";
       let isWinNT = document.documentElement.getAttribute("platform") === "win";
       let eol = isWinNT ? "\r\n" : "\n";
@@ -88,17 +73,17 @@ define(function (require, exports, modul
       if (input.json instanceof Error) {
         // Cannot prettify invalid JSON
         return;
       }
       if (input.prettified) {
         theApp.setState({jsonText: input.jsonText});
       } else {
         if (!input.jsonPretty) {
-          input.jsonPretty = JSON.stringify(input.json, null, "  ");
+          input.jsonPretty = new Text(JSON.stringify(input.json, null, "  "));
         }
         theApp.setState({jsonText: input.jsonPretty});
       }
 
       input.prettified = !input.prettified;
     },
   };
 
@@ -134,16 +119,57 @@ define(function (require, exports, modul
     window.dispatchEvent(contentMessageEvent);
   }
 
   /**
    * Render the main application component. It's the main tab bar displayed
    * at the top of the window. This component also represents ReacJS root.
    */
   let content = document.getElementById("content");
+  let promise = (async function parseJSON() {
+    if (document.readyState == "loading") {
+      // If the JSON has not been loaded yet, render the Raw Data tab first.
+      input.json = {};
+      input.expandedNodes = new Set();
+      input.tabActive = 1;
+      return new Promise(resolve => {
+        document.addEventListener("DOMContentLoaded", resolve, {once: true});
+      }).then(parseJSON).then(() => {
+        // Now update the state and switch to the JSON tab.
+        theApp.setState({
+          tabActive: 0,
+          json: input.json,
+          expandedNodes: input.expandedNodes,
+        });
+      });
+    }
+
+    // If the JSON has been loaded, parse it immediately before loading the app.
+    let jsonString = input.jsonText.textContent;
+    try {
+      input.json = JSON.parse(jsonString);
+    } catch (err) {
+      input.json = err;
+    }
+
+    // Expand the document by default if its size isn't bigger than 100KB.
+    if (!(input.json instanceof Error) && jsonString.length <= AUTO_EXPAND_MAX_SIZE) {
+      input.expandedNodes = TreeViewClass.getExpandedNodes(
+        input.json,
+        {maxLevel: AUTO_EXPAND_MAX_LEVEL}
+      );
+    }
+    return undefined;
+  })();
+
   let theApp = render(MainTabbedArea(input), content);
 
-  // Send notification event to the window. Can be useful for
+  // Send readyState change notification event to the window. Can be useful for
   // tests as well as extensions.
-  let event = new CustomEvent("JSONViewInitialized", {});
-  JSONView.initialized = true;
-  window.dispatchEvent(event);
+  JSONView.readyState = "interactive";
+  window.dispatchEvent(new CustomEvent("AppReadyStateChange"));
+
+  promise.then(() => {
+    // Another readyState change notification event.
+    JSONView.readyState = "complete";
+    window.dispatchEvent(new CustomEvent("AppReadyStateChange"));
+  });
 });
--- a/devtools/client/jsonview/test/browser.ini
+++ b/devtools/client/jsonview/test/browser.ini
@@ -17,38 +17,41 @@ support-files =
   simple_json.json
   simple_json.json^headers^
   valid_json.json
   valid_json.json^headers^
   !/devtools/client/commandline/test/head.js
   !/devtools/client/framework/test/head.js
   !/devtools/client/framework/test/shared-head.js
 
+[browser_json_refresh.js]
 [browser_jsonview_bug_1380828.js]
-[browser_jsonview_ignore_charset.js]
+[browser_jsonview_chunked_json.js]
+support-files =
+  chunked_json.sjs
 [browser_jsonview_content_type.js]
 [browser_jsonview_copy_headers.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsonview_copy_json.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsonview_copy_rawdata.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_jsonview_csp_json.js]
 [browser_jsonview_empty_object.js]
 [browser_jsonview_encoding.js]
 [browser_jsonview_filter.js]
+[browser_jsonview_ignore_charset.js]
 [browser_jsonview_invalid_json.js]
 [browser_jsonview_manifest.js]
 [browser_jsonview_nojs.js]
 [browser_jsonview_nul.js]
 [browser_jsonview_object-type.js]
 [browser_jsonview_row_selection.js]
 [browser_jsonview_save_json.js]
 support-files =
   !/toolkit/content/tests/browser/common/mockTransfer.js
+[browser_jsonview_serviceworker.js]
+[browser_jsonview_slash.js]
 [browser_jsonview_theme.js]
-[browser_jsonview_slash.js]
 [browser_jsonview_valid_json.js]
-[browser_json_refresh.js]
-[browser_jsonview_serviceworker.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/browser_jsonview_chunked_json.js
@@ -0,0 +1,84 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_JSON_URL = URL_ROOT + "chunked_json.sjs";
+
+add_task(async function () {
+  info("Test chunked JSON started");
+
+  await addJsonViewTab(TEST_JSON_URL, {
+    appReadyState: "interactive",
+    docReadyState: "loading",
+  });
+
+  is(await getElementCount(".rawdata.is-active"), 1,
+    "The Raw Data tab is selected.");
+
+  // Write some text and check that it is displayed.
+  await write("[");
+  await checkText();
+
+  // Repeat just in case.
+  await write("1,");
+  await checkText();
+
+  is(await getElementCount("button.prettyprint"), 0,
+    "There is no pretty print button during load");
+
+  await selectJsonViewContentTab("json");
+  is(await getElementText(".jsonPanelBox > .panelContent"), "", "There is no JSON tree");
+
+  await selectJsonViewContentTab("headers");
+  ok(await getElementText(".headersPanelBox .netInfoHeadersTable"),
+    "The headers table has been filled.");
+
+  // Write some text without being in Raw Data, then switch tab and check.
+  await write("2");
+  await selectJsonViewContentTab("rawdata");
+  await checkText();
+
+  // Another text check.
+  await write("]");
+  await checkText();
+
+  // Close the connection.
+  let appReady = waitForContentMessage("Test:JsonView:AppReadyStateChange");
+  await server("close");
+  await appReady;
+
+  is(await getElementCount(".json.is-active"), 1, "The JSON tab is selected.");
+
+  is(await getElementCount(".jsonPanelBox .treeTable .treeRow"), 2,
+    "There is a tree with 2 rows.");
+
+  await selectJsonViewContentTab("rawdata");
+  await checkText();
+
+  is(await getElementCount("button.prettyprint"), 1, "There is a pretty print button.");
+  await clickJsonNode("button.prettyprint");
+  await checkText(JSON.stringify(JSON.parse(data), null, 2));
+});
+
+let data = " ";
+async function write(text) {
+  data += text;
+  let dataReceived = waitForContentMessage("Test:JsonView:NewDataReceived");
+  await server("write", text);
+  await dataReceived;
+}
+async function checkText(text = data) {
+  is(await getElementText(".textPanelBox .data"), text, "Got the right text.");
+}
+
+function server(action, value) {
+  return new Promise(resolve => {
+    let xhr = new XMLHttpRequest();
+    xhr.open("GET", TEST_JSON_URL + "?" + action + "=" + value);
+    xhr.addEventListener("load", resolve, {once: true});
+    xhr.send();
+  });
+}
--- a/devtools/client/jsonview/test/browser_jsonview_nojs.js
+++ b/devtools/client/jsonview/test/browser_jsonview_nojs.js
@@ -1,25 +1,25 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-add_task(function* () {
+add_task(async function () {
   info("Test JSON without JavaScript started.");
 
   let oldPref = SpecialPowers.getBoolPref("javascript.enabled");
   SpecialPowers.setBoolPref("javascript.enabled", false);
 
   const TEST_JSON_URL = "data:application/json,[1,2,3]";
-  yield addJsonViewTab(TEST_JSON_URL, 0).catch(() => {
-    info("JSON Viewer did not load");
-    return executeInContent("Test:JsonView:GetElementVisibleText", {selector: "html"})
-    .then(result => {
-      info("Checking visible text contents.");
-      is(result.text, "[1,2,3]", "The raw source should be visible.");
-    });
-  });
+
+  // "uninitialized" will be the last app readyState because JS is disabled.
+  await addJsonViewTab(TEST_JSON_URL, {appReadyState: "uninitialized"});
+
+  info("Checking visible text contents.");
+  let {text} = await executeInContent("Test:JsonView:GetElementVisibleText",
+    {selector: "html"});
+  is(text, "[1,2,3]", "The raw source should be visible.");
 
   SpecialPowers.setBoolPref("javascript.enabled", oldPref);
 });
new file mode 100644
--- /dev/null
+++ b/devtools/client/jsonview/test/chunked_json.sjs
@@ -0,0 +1,38 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const key = "json-viewer-chunked-response";
+function setResponse(response) {
+  setObjectState(key, response);
+}
+function getResponse() {
+  let response;
+  getObjectState(key, v => { response = v });
+  return response;
+}
+
+function handleRequest(request, response) {
+  let {queryString} = request;
+  if (!queryString) {
+    response.processAsync();
+    setResponse(response);
+    response.setHeader("Content-Type", "application/json");
+    // Write something so that the JSON viewer app starts loading.
+    response.write(" ");
+    return;
+  }
+  let [command, value] = queryString.split('=');
+  switch (command) {
+    case "write":
+      getResponse().write(value);
+      break;
+    case "close":
+      getResponse().finish();
+      setResponse(null);
+      break;
+  }
+  response.setHeader("Content-Type", "text/plain");
+  response.write("ok");
+}
--- a/devtools/client/jsonview/test/doc_frame_script.js
+++ b/devtools/client/jsonview/test/doc_frame_script.js
@@ -20,29 +20,38 @@ EventUtils._EU_Ci = Components.interface
 EventUtils._EU_Cc = Components.classes; // eslint-disable-line
 EventUtils.navigator = content.navigator;
 EventUtils.KeyboardEvent = content.KeyboardEvent;
 
 Services.scriptloader.loadSubScript(
   "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
 /**
- * When the JSON View is done rendering it triggers custom event
- * "JSONViewInitialized", then the Test:TestPageProcessingDone message
- * will be sent to the parent process for tests to wait for this event
- * if needed.
+ * When the ready state of the JSON View app changes, it triggers custom event
+ * "AppReadyStateChange", then the "Test:JsonView:AppReadyStateChange" message
+ * will be sent to the parent process for tests to wait for this event if needed.
  */
-content.addEventListener("JSONViewInitialized", () => {
-  sendAsyncMessage("Test:JsonView:JSONViewInitialized");
+content.addEventListener("AppReadyStateChange", () => {
+  sendAsyncMessage("Test:JsonView:AppReadyStateChange");
 });
 
-content.addEventListener("load", () => {
-  sendAsyncMessage("Test:JsonView:load");
+/**
+ * Analogous for the standard "readystatechange" event of the document.
+ */
+content.document.addEventListener("readystatechange", () => {
+  sendAsyncMessage("Test:JsonView:DocReadyStateChange");
 });
 
+/**
+ * Send a message whenever the server sends a new chunk of JSON data.
+ */
+new content.MutationObserver(function (mutations, observer) {
+  sendAsyncMessage("Test:JsonView:NewDataReceived");
+}).observe(content.wrappedJSObject.JSONView.json, {characterData: true});
+
 addMessageListener("Test:JsonView:GetElementCount", function (msg) {
   let {selector} = msg.data;
   let nodeList = content.document.querySelectorAll(selector);
   sendAsyncMessage(msg.name, {count: nodeList.length});
 });
 
 addMessageListener("Test:JsonView:GetElementText", function (msg) {
   let {selector} = msg.data;
--- a/devtools/client/jsonview/test/head.js
+++ b/devtools/client/jsonview/test/head.js
@@ -21,63 +21,99 @@ registerCleanupFunction(() => {
 });
 
 // XXX move some API into devtools/framework/test/shared-head.js
 
 /**
  * Add a new test tab in the browser and load the given url.
  * @param {String} url
  *   The url to be loaded in the new tab.
- * @param {Number} timeout [optional]
- *   The maximum number of milliseconds allowed before the initialization of the
- *   JSON Viewer once the tab has been loaded. If exceeded, the initialization
- *   will be considered to have failed, and the returned promise will be rejected.
- *   If this parameter is not passed or is negative, it will be ignored.
+ *
+ * @param {Object} [optional]
+ *   An object with the following optional properties:
+ *   - appReadyState: The readyState of the JSON Viewer app that you want to
+ *     wait for. Its value can be one of:
+ *      - "uninitialized": The converter has started the request.
+ *        If JavaScript is disabled, there will be no more readyState changes.
+ *      - "loading": RequireJS started loading the scripts for the JSON Viewer.
+ *        If the load timeouts, there will be no more readyState changes.
+ *      - "interactive": The JSON Viewer app loaded, but possibly not all the JSON
+ *        data has been received.
+ *      - "complete" (default): The app is fully loaded with all the JSON.
+ *   - docReadyState: The standard readyState of the document that you want to
+ *     wait for. Its value can be one of:
+ *      - "loading": The JSON data has not been completely loaded (but the app might).
+ *      - "interactive": All the JSON data has been received.
+ *      - "complete" (default): Since there aren't sub-resources like images,
+ *        behaves as "interactive". Note the app might not be loaded yet.
  */
-async function addJsonViewTab(url, timeout = -1) {
+async function addJsonViewTab(url, {
+  appReadyState = "complete",
+  docReadyState = "complete",
+} = {}) {
   info("Adding a new JSON tab with URL: '" + url + "'");
-
-  let tab = await addTab(url);
+  let tabLoaded = addTab(url);
+  let tab = gBrowser.selectedTab;
   let browser = tab.linkedBrowser;
+  await Promise.race([tabLoaded, new Promise(resolve => {
+    browser.webProgress.addProgressListener({
+      QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener",
+                                             "nsISupportsWeakReference"]),
+      onLocationChange(webProgress) {
+        // Fires when the tab is ready but before completely loaded.
+        webProgress.removeProgressListener(this);
+        resolve();
+      },
+    }, Ci.nsIWebProgress.NOTIFY_LOCATION);
+  })]);
 
   // Load devtools/shared/frame-script-utils.js
   getFrameScript();
-
-  // Load frame script with helpers for JSON View tests.
   let rootDir = getRootDirectory(gTestPath);
-  let frameScriptUrl = rootDir + "doc_frame_script.js";
-  browser.messageManager.loadFrameScript(frameScriptUrl, false);
 
-  // Check if there is a JSONView object.
-  if (!content.window.wrappedJSObject.JSONView) {
-    throw new Error("JSON Viewer did not load.");
-  }
+  let data = {rootDir, appReadyState, docReadyState};
+  // eslint-disable-next-line no-shadow
+  await ContentTask.spawn(browser, data, async function (data) {
+    // Check if there is a JSONView object.
+    let {JSONView} = content.window.wrappedJSObject;
+    if (!JSONView) {
+      throw new Error("The JSON Viewer did not load.");
+    }
 
-  // Resolve if the JSONView is fully loaded.
-  if (content.window.wrappedJSObject.JSONView.initialized) {
-    return tab;
-  }
+    // Load frame script with helpers for JSON View tests.
+    let frameScriptUrl = data.rootDir + "doc_frame_script.js";
+    Services.scriptloader.loadSubScript(frameScriptUrl, {}, "UTF-8");
 
-  // Otherwise wait for an initialization event, possibly with a time limit.
-  const onJSONViewInitialized =
-    waitForContentMessage("Test:JsonView:JSONViewInitialized")
-    .then(() => tab);
-
-  if (!(timeout >= 0)) {
-    return onJSONViewInitialized;
-  }
+    let docReadyStates = ["loading", "interactive", "complete"];
+    let docReadyIndex = docReadyStates.indexOf(data.docReadyState);
+    let appReadyStates = ["uninitialized", ...docReadyStates];
+    let appReadyIndex = appReadyStates.indexOf(data.appReadyState);
+    if (docReadyIndex < 0 || appReadyIndex < 0) {
+      throw new Error("Invalid app or doc readyState parameter.");
+    }
 
-  if (content.window.document.readyState !== "complete") {
-    await waitForContentMessage("Test:JsonView:load");
-  }
+    // Wait until the document readyState suffices.
+    let {document} = content.window;
+    while (docReadyStates.indexOf(document.readyState) < docReadyIndex) {
+      info(`DocReadyState is "${document.readyState}". Await "${data.docReadyState}"`);
+      await new Promise(resolve => {
+        document.addEventListener("readystatechange", resolve, {once: true});
+      });
+    }
 
-  let onTimeout = new Promise((_, reject) =>
-    setTimeout(() => reject(new Error("JSON Viewer did not load.")), timeout));
+    // Wait until the app readyState suffices.
+    while (appReadyStates.indexOf(JSONView.readyState) < appReadyIndex) {
+      info(`AppReadyState is "${JSONView.readyState}". Await "${data.appReadyState}"`);
+      await new Promise(resolve => {
+        content.addEventListener("AppReadyStateChange", resolve, {once: true});
+      });
+    }
+  });
 
-  return Promise.race([onJSONViewInitialized, onTimeout]);
+  return tab;
 }
 
 /**
  * Expanding a node in the JSON tree
  */
 function clickJsonNode(selector) {
   info("Expanding node: '" + selector + "'");
 
--- a/devtools/client/jsonview/viewer-config.js
+++ b/devtools/client/jsonview/viewer-config.js
@@ -2,16 +2,20 @@
 /* vim: set ft=javascript 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/. */
 /* global requirejs */
 
 "use strict";
 
+// Send readyState change notification event to the window. It's useful for tests.
+JSONView.readyState = "loading";
+window.dispatchEvent(new CustomEvent("AppReadyStateChange"));
+
 /**
  * RequireJS configuration for JSON Viewer.
  *
  * ReactJS library is shared among DevTools. The minified (production) version
  * of the library is always available, and is used by default.
  *
  * In order to use the developer version you need to specify the following
  * in your .mozconfig (see also bug 1181646):
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -465,16 +465,24 @@ sourceFooter.blackbox.accesskey=B
 # with the blackbox button
 sourceFooter.unblackbox=Unblackbox source
 sourceFooter.unblackbox.accesskey=b
 
 # LOCALIZATION NOTE (sourceFooter.blackboxed): Text associated
 # with a blackboxed source
 sourceFooter.blackboxed=Blackboxed source
 
+# LOCALIZATION NOTE (sourceFooter.mappedSource): Text associated
+# with a mapped source. %S is replaced by the source map origin.
+sourceFooter.mappedSource=(From %S)
+
+# LOCALIZATION NOTE (sourceFooter.mappedSourceTooltip): Tooltip text associated
+# with a mapped source. %S is replaced by the source map origin.
+sourceFooter.mappedSourceTooltip=(Source mapped from %S)
+
 # LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated
 # with a code coverage button
 sourceFooter.codeCoverage=Code coverage
 
 # LOCALIZATION NOTE (sourceTabs.closeTabButtonTooltip): The tooltip that is displayed
 # for close tab button in source tabs.
 sourceTabs.closeTabButtonTooltip=Close tab
 
--- a/devtools/client/netmonitor/src/components/CookiesPanel.js
+++ b/devtools/client/netmonitor/src/components/CookiesPanel.js
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { createFactory } = require("devtools/client/shared/vendor/react");
+const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { L10N } = require("../utils/l10n");
 const { sortObjectKeys } = require("../utils/sort-utils");
 
 // Component
 const PropertiesView = createFactory(require("./PropertiesView"));
 
@@ -23,79 +23,103 @@ const SECTION_NAMES = [
   RESPONSE_COOKIES,
   REQUEST_COOKIES,
 ];
 
 /*
  * Cookies panel component
  * This tab lists full details of any cookies sent with the request or response
  */
-function CookiesPanel({
-  request,
-  openLink,
-}) {
-  let {
-    requestCookies = { cookies: [] },
-    responseCookies = { cookies: [] },
-  } = request;
+class CookiesPanel extends Component {
+  static get propTypes() {
+    return {
+      connector: PropTypes.object.isRequired,
+      openLink: PropTypes.func,
+      request: PropTypes.object.isRequired,
+    };
+  }
 
-  requestCookies = requestCookies.cookies || requestCookies;
-  responseCookies = responseCookies.cookies || responseCookies;
-
-  if (!requestCookies.length && !responseCookies.length) {
-    return div({ className: "empty-notice" },
-      COOKIES_EMPTY_TEXT
-    );
+  componentDidMount() {
+    this.maybeFetchCookies(this.props);
   }
 
-  let object = {};
-
-  if (responseCookies.length) {
-    object[RESPONSE_COOKIES] = sortObjectKeys(getProperties(responseCookies));
+  componentWillReceiveProps(nextProps) {
+    this.maybeFetchCookies(nextProps);
   }
 
-  if (requestCookies.length) {
-    object[REQUEST_COOKIES] = sortObjectKeys(getProperties(requestCookies));
+  /**
+   * When switching to another request, lazily fetch request cookies
+   * from the backend. The panel will first be empty and then display the content.
+   */
+  maybeFetchCookies(props) {
+    if (props.request.requestCookiesAvailable && !props.request.requestCookies) {
+      props.connector.requestData(props.request.id, "requestCookies");
+    }
+    if (props.request.responseCookiesAvailable && !props.request.responseCookies) {
+      props.connector.requestData(props.request.id, "responseCookies");
+    }
   }
 
-  return (
-    div({ className: "panel-container" },
-      PropertiesView({
-        object,
-        filterPlaceHolder: COOKIES_FILTER_TEXT,
-        sectionNames: SECTION_NAMES,
-        openLink,
-      })
-    )
-  );
-}
-
-CookiesPanel.displayName = "CookiesPanel";
-
-CookiesPanel.propTypes = {
-  request: PropTypes.object.isRequired,
-  openLink: PropTypes.func,
-};
+  /**
+   * Mapping array to dict for TreeView usage.
+   * Since TreeView only support Object(dict) format.
+   *
+   * @param {Object[]} arr - key-value pair array like cookies or params
+   * @returns {Object}
+   */
+  getProperties(arr) {
+    return arr.reduce((map, obj) => {
+      // Generally cookies object contains only name and value properties and can
+      // be rendered as name: value pair.
+      // When there are more properties in cookies object such as extra or path,
+      // We will pass the object to display these extra information
+      if (Object.keys(obj).length > 2) {
+        map[obj.name] = Object.assign({}, obj);
+        delete map[obj.name].name;
+      } else {
+        map[obj.name] = obj.value;
+      }
+      return map;
+    }, {});
+  }
 
-/**
- * Mapping array to dict for TreeView usage.
- * Since TreeView only support Object(dict) format.
- *
- * @param {Object[]} arr - key-value pair array like cookies or params
- * @returns {Object}
- */
-function getProperties(arr) {
-  return arr.reduce((map, obj) => {
-    // Generally cookies object contains only name and value properties and can
-    // be rendered as name: value pair.
-    // When there are more properties in cookies object such as extra or path,
-    // We will pass the object to display these extra information
-    if (Object.keys(obj).length > 2) {
-      map[obj.name] = Object.assign({}, obj);
-      delete map[obj.name].name;
-    } else {
-      map[obj.name] = obj.value;
+  render() {
+    let {
+      request: {
+        requestCookies = { cookies: [] },
+        responseCookies = { cookies: [] },
+      },
+      openLink,
+    } = this.props;
+
+    requestCookies = requestCookies.cookies || requestCookies;
+    responseCookies = responseCookies.cookies || responseCookies;
+
+    if (!requestCookies.length && !responseCookies.length) {
+      return div({ className: "empty-notice" },
+        COOKIES_EMPTY_TEXT
+      );
     }
-    return map;
-  }, {});
+
+    let object = {};
+
+    if (responseCookies.length) {
+      object[RESPONSE_COOKIES] = sortObjectKeys(this.getProperties(responseCookies));
+    }
+
+    if (requestCookies.length) {
+      object[REQUEST_COOKIES] = sortObjectKeys(this.getProperties(requestCookies));
+    }
+
+    return (
+      div({ className: "panel-container" },
+        PropertiesView({
+          object,
+          filterPlaceHolder: COOKIES_FILTER_TEXT,
+          sectionNames: SECTION_NAMES,
+          openLink,
+        })
+      )
+    );
+  }
 }
 
 module.exports = CookiesPanel;
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -90,17 +90,17 @@ class MonitorPanel extends Component {
 
     let initialWidth = Services.prefs.getIntPref(
         "devtools.netmonitor.panes-network-details-width");
     let initialHeight = Services.prefs.getIntPref(
         "devtools.netmonitor.panes-network-details-height");
 
     return (
       div({ className: "monitor-panel" },
-        Toolbar(),
+        Toolbar({ connector }),
         SplitBox({
           className: "devtools-responsive-container",
           initialWidth: `${initialWidth}px`,
           initialHeight: `${initialHeight}px`,
           minSize: "50px",
           maxSize: "80%",
           splitterSize: "1px",
           startPanel: RequestList({ isEmpty, connector }),
--- a/devtools/client/netmonitor/src/components/RequestListColumnCookies.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnCookies.js
@@ -8,28 +8,46 @@ const { Component } = require("devtools/
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { div } = dom;
 
 class RequestListColumnCookies extends Component {
   static get propTypes() {
     return {
+      connector: PropTypes.object.isRequired,
       item: PropTypes.object.isRequired,
     };
   }
 
+  componentDidMount() {
+    this.maybeFetchRequestCookies(this.props);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.maybeFetchRequestCookies(nextProps);
+  }
+
   shouldComponentUpdate(nextProps) {
     let { requestCookies: currRequestCookies = { cookies: [] } } = this.props.item;
     let { requestCookies: nextRequestCookies = { cookies: [] } } = nextProps.item;
     currRequestCookies = currRequestCookies.cookies || currRequestCookies;
     nextRequestCookies = nextRequestCookies.cookies || nextRequestCookies;
     return currRequestCookies !== nextRequestCookies;
   }
 
+  /**
+   * Lazily fetch request cookies from the backend.
+   */
+  maybeFetchRequestCookies(props) {
+    if (props.item.requestCookiesAvailable && !props.requestCookies) {
+      props.connector.requestData(props.item.id, "requestCookies");
+    }
+  }
+
   render() {
     let { requestCookies = { cookies: [] } } = this.props.item;
     requestCookies = requestCookies.cookies || requestCookies;
     let requestCookiesLength = requestCookies.length > 0 ? requestCookies.length : "";
     return (
       div({
         className: "requests-list-column requests-list-cookies",
         title: requestCookiesLength
--- a/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.js
+++ b/devtools/client/netmonitor/src/components/RequestListColumnSetCookies.js
@@ -8,28 +8,46 @@ const { Component } = require("devtools/
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const { div } = dom;
 
 class RequestListColumnSetCookies extends Component {
   static get propTypes() {
     return {
+      connector: PropTypes.object.isRequired,
       item: PropTypes.object.isRequired,
     };
   }
 
+  componentDidMount() {
+    this.maybeFetchResponseCookies(this.props);
+  }
+
+  componentWillReceiveProps(nextProps) {
+    this.maybeFetchResponseCookies(nextProps);
+  }
+
   shouldComponentUpdate(nextProps) {
     let { responseCookies: currResponseCookies = { cookies: [] } } = this.props.item;
     let { responseCookies: nextResponseCookies = { cookies: [] } } = nextProps.item;
     currResponseCookies = currResponseCookies.cookies || currResponseCookies;
     nextResponseCookies = nextResponseCookies.cookies || nextResponseCookies;
     return currResponseCookies !== nextResponseCookies;
   }
 
+  /**
+   * Lazily fetch response cookies from the backend.
+   */
+  maybeFetchResponseCookies(props) {
+    if (props.item.responseCookiesAvailable && !props.responseCookies) {
+      props.connector.requestData(props.item.id, "responseCookies");
+    }
+  }
+
   render() {
     let { responseCookies = { cookies: [] } } = this.props.item;
     responseCookies = responseCookies.cookies || responseCookies;
     let responseCookiesLength = responseCookies.length > 0 ? responseCookies.length : "";
     return (
       div({
         className: "requests-list-column requests-list-set-cookies",
         title: responseCookiesLength
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -206,16 +206,17 @@ class RequestListContent extends Compone
    * scrolled to bottom, but allow scrolling up with the selection.
    */
   onFocusedNodeChange() {
     this.shouldScrollBottom = false;
   }
 
   render() {
     const {
+      connector,
       columns,
       displayedRequests,
       firstRequestStartedMillis,
       onCauseBadgeMouseDown,
       onItemMouseDown,
       onSecurityIconMouseDown,
       onWaterfallMouseDown,
       scale,
@@ -231,16 +232,17 @@ class RequestListContent extends Compone
             tabIndex: 0,
             onKeyDown: this.onKeyDown,
             style: { "--timings-scale": scale, "--timings-rev-scale": 1 / scale }
           },
             RequestListHeader(),
             displayedRequests.map((item, index) => RequestListItem({
               firstRequestStartedMillis,
               fromCache: item.status === "304" || item.fromCache,
+              connector,
               columns,
               item,
               index,
               isSelected: item.id === (selectedRequest && selectedRequest.id),
               key: item.id,
               onContextMenu: this.onContextMenu,
               onFocusedNodeChange: this.onFocusedNodeChange,
               onMouseDown: () => onItemMouseDown(item.id),
--- a/devtools/client/netmonitor/src/components/RequestListItem.js
+++ b/devtools/client/netmonitor/src/components/RequestListItem.js
@@ -52,31 +52,34 @@ const UPDATED_REQ_ITEM_PROPS = [
   "method",
   "url",
   "remoteAddress",
   "cause",
   "contentSize",
   "transferredSize",
   "startedMillis",
   "totalTime",
+  "requestCookies",
+  "responseCookies",
 ];
 
 const UPDATED_REQ_PROPS = [
   "firstRequestStartedMillis",
   "index",
   "isSelected",
   "waterfallWidth",
 ];
 
 /**
  * Render one row in the request list.
  */
 class RequestListItem extends Component {
   static get propTypes() {
     return {
+      connector: PropTypes.object.isRequired,
       columns: PropTypes.object.isRequired,
       item: PropTypes.object.isRequired,
       index: PropTypes.number.isRequired,
       isSelected: PropTypes.bool.isRequired,
       firstRequestStartedMillis: PropTypes.number.isRequired,
       fromCache: PropTypes.bool,
       onCauseBadgeMouseDown: PropTypes.func.isRequired,
       onContextMenu: PropTypes.func.isRequired,
@@ -106,16 +109,17 @@ class RequestListItem extends Component 
       if (this.props.onFocusedNodeChange) {
         this.props.onFocusedNodeChange();
       }
     }
   }
 
   render() {
     let {
+      connector,
       columns,
       item,
       index,
       isSelected,
       firstRequestStartedMillis,
       fromCache,
       onContextMenu,
       onMouseDown,
@@ -142,18 +146,18 @@ class RequestListItem extends Component 
         columns.get("file") && RequestListColumnFile({ item }),
         columns.get("protocol") && RequestListColumnProtocol({ item }),
         columns.get("scheme") && RequestListColumnScheme({ item }),
         columns.get("domain") && RequestListColumnDomain({ item,
                                                            onSecurityIconMouseDown }),
         columns.get("remoteip") && RequestListColumnRemoteIP({ item }),
         columns.get("cause") && RequestListColumnCause({ item, onCauseBadgeMouseDown }),
         columns.get("type") && RequestListColumnType({ item }),
-        columns.get("cookies") && RequestListColumnCookies({ item }),
-        columns.get("setCookies") && RequestListColumnSetCookies({ item }),
+        columns.get("cookies") && RequestListColumnCookies({ connector, item }),
+        columns.get("setCookies") && RequestListColumnSetCookies({ connector, item }),
         columns.get("transferred") && RequestListColumnTransferredSize({ item }),
         columns.get("contentSize") && RequestListColumnContentSize({ item }),
         columns.get("startTime") &&
           RequestListColumnStartTime({ item, firstRequestStartedMillis }),
         columns.get("endTime") &&
           RequestListColumnEndTime({ item, firstRequestStartedMillis }),
         columns.get("responseTime") &&
           RequestListColumnResponseTime({ item, firstRequestStartedMillis }),
--- a/devtools/client/netmonitor/src/components/TabboxPanel.js
+++ b/devtools/client/netmonitor/src/components/TabboxPanel.js
@@ -63,17 +63,21 @@ function TabboxPanel({
           openLink,
           request,
         }),
       ),
       TabPanel({
         id: PANELS.COOKIES,
         title: COOKIES_TITLE,
       },
-        CookiesPanel({ request, openLink }),
+        CookiesPanel({
+          connector,
+          openLink,
+          request,
+        }),
       ),
       TabPanel({
         id: PANELS.PARAMS,
         title: PARAMS_TITLE,
       },
         ParamsPanel({ connector, openLink, request }),
       ),
       TabPanel({
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -51,16 +51,17 @@ const DISABLE_CACHE_LABEL = L10N.getStr(
  * Network monitor toolbar component.
  *
  * Toolbar contains a set of useful tools to control network requests
  * as well as set of filters for filtering the content.
  */
 class Toolbar extends Component {
   static get propTypes() {
     return {
+      connector: PropTypes.object.isRequired,
       toggleRecording: PropTypes.func.isRequired,
       recording: PropTypes.bool.isRequired,
       clearRequests: PropTypes.func.isRequired,
       requestFilterTypes: PropTypes.object.isRequired,
       setRequestFilterText: PropTypes.func.isRequired,
       networkDetailsToggleDisabled: PropTypes.bool.isRequired,
       networkDetailsOpen: PropTypes.bool.isRequired,
       toggleNetworkDetails: PropTypes.func.isRequired,
@@ -73,16 +74,17 @@ class Toolbar extends Component {
       toggleRequestFilterType: PropTypes.func.isRequired,
       filteredRequests: PropTypes.array.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
     this.autocompleteProvider = this.autocompleteProvider.bind(this);
+    this.onSearchBoxFocus = this.onSearchBoxFocus.bind(this);
     this.toggleRequestFilterType = this.toggleRequestFilterType.bind(this);
     this.updatePersistentLogsEnabled = this.updatePersistentLogsEnabled.bind(this);
     this.updateBrowserCacheDisabled = this.updateBrowserCacheDisabled.bind(this);
   }
 
   componentDidMount() {
     Services.prefs.addObserver(DEVTOOLS_ENABLE_PERSISTENT_LOG_PREF,
                                this.updatePersistentLogsEnabled);
@@ -125,16 +127,25 @@ class Toolbar extends Component {
     this.props.disableBrowserCache(
       Services.prefs.getBoolPref(DEVTOOLS_DISABLE_CACHE_PREF));
   }
 
   autocompleteProvider(filter) {
     return autocompleteProvider(filter, this.props.filteredRequests);
   }
 
+  onSearchBoxFocus() {
+    let { connector, filteredRequests } = this.props;
+
+    // Fetch responseCookies for building autocomplete list
+    filteredRequests.forEach((request) => {
+      connector.requestData(request.id, "responseCookies");
+    });
+  }
+
   render() {
     let {
       toggleRecording,
       clearRequests,
       requestFilterTypes,
       setRequestFilterText,
       networkDetailsToggleDisabled,
       networkDetailsOpen,
@@ -233,16 +244,17 @@ class Toolbar extends Component {
         span({ className: "devtools-toolbar-group" },
           SearchBox({
             delay: FILTER_SEARCH_DELAY,
             keyShortcut: SEARCH_KEY_SHORTCUT,
             placeholder: SEARCH_PLACE_HOLDER,
             type: "filter",
             ref: "searchbox",
             onChange: setRequestFilterText,
+            onFocus: this.onSearchBoxFocus,
             autocompleteProvider: this.autocompleteProvider,
           }),
           button({
             className: toggleDetailButtonClass,
             title: toggleDetailButtonTitle,
             disabled: networkDetailsToggleDisabled,
             tabIndex: "0",
             onClick: toggleNetworkDetails,
--- a/devtools/client/netmonitor/src/connector/firefox-connector.js
+++ b/devtools/client/netmonitor/src/connector/firefox-connector.js
@@ -63,28 +63,29 @@ class FirefoxConnector {
     }
 
     this.displayCachedEvents();
   }
 
   async disconnect() {
     this.actions.batchReset();
 
-    // The timeline front wasn't initialized and started if the server wasn't
-    // recent enough to emit the markers we were interested in.
-    if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
-      this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
-      await this.timelineFront.destroy();
-    }
-
     this.removeListeners();
 
-    this.tabTarget.off("will-navigate");
+    if (this.tabTarget) {
+      // The timeline front wasn't initialized and started if the server wasn't
+      // recent enough to emit the markers we were interested in.
+      if (this.tabTarget.getTrait("documentLoadingMarkers") && this.timelineFront) {
+        this.timelineFront.off("doc-loading", this.onDocLoadingMarker);
+        await this.timelineFront.destroy();
+      }
 
-    this.tabTarget = null;
+      this.tabTarget.off("will-navigate");
+      this.tabTarget = null;
+    }
     this.webConsoleClient = null;
     this.timelineFront = null;
     this.dataProvider = null;
     this.panel = null;
   }
 
   pause() {
     this.removeListeners();
@@ -98,19 +99,23 @@ class FirefoxConnector {
     this.tabTarget.on("close", this.disconnect);
     this.webConsoleClient.on("networkEvent",
       this.dataProvider.onNetworkEvent);
     this.webConsoleClient.on("networkEventUpdate",
       this.dataProvider.onNetworkEventUpdate);
   }
 
   removeListeners() {
-    this.tabTarget.off("close");
-    this.webConsoleClient.off("networkEvent");
-    this.webConsoleClient.off("networkEventUpdate");
+    if (this.tabTarget) {
+      this.tabTarget.off("close");
+    }
+    if (this.webConsoleClient) {
+      this.webConsoleClient.off("networkEvent");
+      this.webConsoleClient.off("networkEventUpdate");
+    }
   }
 
   willNavigate() {
     if (!Services.prefs.getBoolPref("devtools.netmonitor.persistlog")) {
       this.actions.batchReset();
       this.actions.clearRequests();
     } else {
       // If the log is persistent, just clear all accumulated timing markers.
@@ -311,14 +316,20 @@ class FirefoxConnector {
    * @param {number} sourceLine source line number
    */
   viewSourceInDebugger(sourceURL, sourceLine) {
     if (this.toolbox) {
       this.toolbox.viewSourceInDebugger(sourceURL, sourceLine);
     }
   }
 
+  /**
+   * Fetch networkEventUpdate websocket message from back-end when
+   * data provider is connected.
+   * @param {object} request network request instance
+   * @param {string} type NetworkEventUpdate type
+   */
   requestData(request, type) {
     return this.dataProvider.requestData(request, type);
   }
 }
 
 module.exports = new FirefoxConnector();
--- a/devtools/client/netmonitor/src/connector/firefox-data-provider.js
+++ b/devtools/client/netmonitor/src/connector/firefox-data-provider.js
@@ -2,19 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 /* eslint-disable block-scoped-var */
 
 "use strict";
 
 const { EVENTS } = require("../constants");
 const { CurlUtils } = require("devtools/client/shared/curl");
-const {
-  fetchHeaders,
-} = require("../utils/request-utils");
+const { fetchHeaders } = require("../utils/request-utils");
 
 /**
  * This object is responsible for fetching additional HTTP
  * data from the backend over RDP protocol.
  *
  * The object also keeps track of RDP requests in-progress,
  * so it's possible to determine whether all has been fetched
  * or not.
@@ -175,16 +173,20 @@ class FirefoxDataProvider {
       // two new-line characters at the end.
       const headersSize = headers.reduce((acc, { name, value }) => {
         return acc + name.length + value.length + 2;
       }, 0);
 
       requestPostData.postData.text = postData;
       payload.requestPostData = Object.assign({}, requestPostData);
       payload.requestHeadersFromUploadStream = { headers, headersSize };
+
+      // Lock down requestPostDataAvailable once we fetch data from back-end.
+      // Using this as flag to prevent fetching arrived data again.
+      payload.requestPostDataAvailable = false;
     }
     return payload;
   }
 
   async fetchResponseCookies(responseCookies) {
     let payload = {};
     if (responseCookies) {
       let resCookies = [];
@@ -197,16 +199,20 @@ class FirefoxDataProvider {
           resCookies.push(Object.assign({}, cookie, {
             value: await this.getLongString(cookie.value),
           }));
         }
         if (resCookies.length) {
           payload.responseCookies = resCookies;
         }
       }
+
+      // Lock down responseCookiesAvailable once we fetch data from back-end.
+      // Using this as flag to prevent fetching arrived data again.
+      payload.responseCookiesAvailable = false;
     }
     return payload;
   }
 
   async fetchRequestCookies(requestCookies) {
     let payload = {};
     if (requestCookies) {
       let reqCookies = [];
@@ -219,16 +225,20 @@ class FirefoxDataProvider {
           reqCookies.push(Object.assign({}, cookie, {
             value: await this.getLongString(cookie.value),
           }));
         }
         if (reqCookies.length) {
           payload.requestCookies = reqCookies;
         }
       }
+
+      // Lock down requestCookiesAvailable once we fetch data from back-end.
+      // Using this as flag to prevent fetching arrived data again.
+      payload.requestCookiesAvailable = false;
     }
     return payload;
   }
 
   /**
    * Access a payload item from payload queue.
    *
    * @param {string} id request id
@@ -260,22 +270,19 @@ class FirefoxDataProvider {
     }
 
     let { payload } = this.getRequestFromQueue(id);
 
     // The payload is ready when all values in the record are true.
     // Note that we never fetch response header/cookies for request with security issues.
     // Bug 1404917 should simplify this heuristic by making all these field be lazily
     // fetched, only on-demand.
-    return record.requestHeaders && record.requestCookies && record.eventTimings &&
-      (
-        (record.responseHeaders && record.responseCookies) ||
-        payload.securityState === "broken" ||
-        (!payload.status && payload.responseContentAvailable)
-      );
+    return record.requestHeaders && record.eventTimings &&
+      (record.responseHeaders || payload.securityState === "broken" ||
+        (!payload.status && payload.responseContentAvailable));
   }
 
   /**
    * Merge upcoming networkEventUpdate payload into existing one.
    *
    * @param {string} id request id
    * @param {object} payload request data payload
    */
@@ -337,19 +344,17 @@ class FirefoxDataProvider {
         url,
       },
       startedDateTime,
     } = networkInfo;
 
     // Create tracking record for this request.
     this.rdpRequestMap.set(actor, {
       requestHeaders: false,
-      requestCookies: false,
       responseHeaders: false,
-      responseCookies: false,
       eventTimings: false,
     });
 
     this.addRequest(actor, {
       cause,
       fromCache,
       fromServiceWorker,
       isXHR,
@@ -376,27 +381,25 @@ class FirefoxDataProvider {
     // When we pause and resume, we may receive `networkEventUpdate` for a request
     // that started during the pause and we missed its `networkEvent`.
     if (!this.rdpRequestMap.has(actor)) {
       return;
     }
 
     switch (updateType) {
       case "requestHeaders":
-      case "requestCookies":
       case "responseHeaders":
-      case "responseCookies":
         this.requestPayloadData(actor, updateType);
         break;
+      case "requestCookies":
+      case "responseCookies":
       case "requestPostData":
-        this.updateRequest(actor, {
-          // This field helps knowing when/if requestPostData property is available
-          // and can be requested via `requestData`
-          requestPostDataAvailable: true
-        });
+        // This field helps knowing when/if updateType property is available
+        // and can be requested via `requestData`
+        this.updateRequest(actor, { [`${updateType}Available`]: true });
         break;
       case "securityInfo":
         this.updateRequest(actor, { securityState: networkInfo.securityInfo });
         break;
       case "responseStart":
         this.updateRequest(actor, {
           httpVersion: networkInfo.response.httpVersion,
           remoteAddress: networkInfo.response.remoteAddress,
@@ -549,17 +552,17 @@ class FirefoxDataProvider {
 
     let response = await new Promise((resolve, reject) => {
       // Do a RDP request to fetch data from the actor.
       if (typeof this.webConsoleClient[clientMethodName] === "function") {
         // Make sure we fetch the real actor data instead of cloned actor
         // e.g. CustomRequestPanel will clone a request with additional '-clone' actor id
         this.webConsoleClient[clientMethodName](actor.replace("-clone", ""), (res) => {
           if (res.error) {
-            console.error(res.error);
+            console.error(res.message);
           }
           resolve(res);
         });
       } else {
         reject(new Error(`Error: No such client method '${clientMethodName}'!`));
       }
     });
 
@@ -586,22 +589,22 @@ class FirefoxDataProvider {
     });
   }
 
   /**
    * Handles additional information received for a "requestCookies" packet.
    *
    * @param {object} response the message received from the server.
    */
-  onRequestCookies(response) {
-    return this.updateRequest(response.from, {
+  async onRequestCookies(response) {
+    let payload = await this.updateRequest(response.from, {
       requestCookies: response
-    }).then(() => {
-      emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
     });
+    emit(EVENTS.RECEIVED_REQUEST_COOKIES, response.from);
+    return payload.requestCookies;
   }
 
   /**
    * Handles additional information received for a "requestPostData" packet.
    *
    * @param {object} response the message received from the server.
    */
   async onRequestPostData(response) {
@@ -638,22 +641,22 @@ class FirefoxDataProvider {
     });
   }
 
   /**
    * Handles additional information received for a "responseCookies" packet.
    *
    * @param {object} response the message received from the server.
    */
-  onResponseCookies(response) {
-    return this.updateRequest(response.from, {
+  async onResponseCookies(response) {
+    let payload = await this.updateRequest(response.from, {
       responseCookies: response
-    }).then(() => {
-      emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
     });
+    emit(EVENTS.RECEIVED_RESPONSE_COOKIES, response.from);
+    return payload.responseCookies;
   }
 
   /**
    * Handles additional information received via "getResponseContent" request.
    *
    * @param {object} response the message received from the server.
    */
   async onResponseContent(response) {
--- a/devtools/client/netmonitor/src/constants.js
+++ b/devtools/client/netmonitor/src/constants.js
@@ -118,20 +118,22 @@ const UPDATE_PROPS = [
   "transferredSize",
   "totalTime",
   "eventTimings",
   "headersSize",
   "customQueryValue",
   "requestHeaders",
   "requestHeadersFromUploadStream",
   "requestCookies",
+  "requestCookiesAvailable",
   "requestPostData",
   "requestPostDataAvailable",
   "responseHeaders",
   "responseCookies",
+  "responseCookiesAvailable",
   "responseContent",
   "responseContentAvailable",
   "formDataSections",
   "stacktrace",
 ];
 
 const PANELS = {
   COOKIES: "cookies",
--- a/devtools/client/netmonitor/src/har/har-builder.js
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -263,17 +263,16 @@ HarBuilder.prototype = {
     }
 
     // If we are dealing with URL encoded body, parse parameters.
     if (CurlUtils.isUrlEncodedRequest({
       headers: requestHeaders.headers,
       postDataText: postData.text,
     })) {
       postData.mimeType = "application/x-www-form-urlencoded";
-
       // Extract form parameters and produce nice HAR array.
       let formDataSections = await getFormDataSections(
         requestHeaders,
         requestHeadersFromUploadStream,
         requestPostData,
         this._options.getString,
       );
 
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
@@ -17,17 +17,17 @@ add_task(function* () {
   let RequestListContextMenu = windowRequire(
     "devtools/client/netmonitor/src/request-list-context-menu");
   let { getSortedRequests } = windowRequire(
     "devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   // Execute one POST request on the page and wait till its done.
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.executeTest();
   });
   yield wait;
 
   // Copy HAR into the clipboard (asynchronous).
   let contextMenu = new RequestListContextMenu({ connector });
   let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
--- a/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
+++ b/devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
@@ -42,17 +42,17 @@ function* throttleUploadTest(actuallyThr
   info("sending throttle request");
   yield new Promise((resolve) => {
     connector.setPreferences(request, (response) => {
       resolve(response);
     });
   });
 
   // Execute one POST request on the page and wait till its done.
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, { size }, function* (args) {
     content.wrappedJSObject.executeTest2(args.size);
   });
   yield wait;
 
   // Copy HAR into the clipboard (asynchronous).
   let contextMenu = new RequestListContextMenu({ connector });
   let jsonString = yield contextMenu.copyAllAsHar(getSortedRequests(store.getState()));
--- a/devtools/client/netmonitor/src/reducers/sort.js
+++ b/devtools/client/netmonitor/src/reducers/sort.js
@@ -1,34 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const I = require("devtools/client/shared/vendor/immutable");
 const { SORT_BY } = require("../constants");
 
-const Sort = I.Record({
-  // null means: sort by "waterfall", but don't highlight the table header
-  type: null,
-  ascending: true,
-});
+function Sort() {
+  return {
+    // null means: sort by "waterfall", but don't highlight the table header
+    type: null,
+    ascending: true,
+  };
+}
 
 function sortReducer(state = new Sort(), action) {
   switch (action.type) {
     case SORT_BY: {
-      return state.withMutations(st => {
-        if (action.sortType == st.type) {
-          st.ascending = !st.ascending;
-        } else {
-          st.type = action.sortType;
-          st.ascending = true;
-        }
-      });
+      state = { ...state };
+      if (action.sortType == state.type) {
+        state.ascending = !state.ascending;
+      } else {
+        state.type = action.sortType;
+        state.ascending = true;
+      }
+      return state;
     }
     default:
       return state;
   }
 }
 
 module.exports = {
   Sort,
--- a/devtools/client/netmonitor/src/reducers/timing-markers.js
+++ b/devtools/client/netmonitor/src/reducers/timing-markers.js
@@ -1,47 +1,47 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const I = require("devtools/client/shared/vendor/immutable");
 const {
   ADD_TIMING_MARKER,
   CLEAR_TIMING_MARKERS,
   CLEAR_REQUESTS,
 } = require("../constants");
 
-const TimingMarkers = I.Record({
-  firstDocumentDOMContentLoadedTimestamp: -1,
-  firstDocumentLoadTimestamp: -1,
-});
+function TimingMarkers() {
+  return {
+    firstDocumentDOMContentLoadedTimestamp: -1,
+    firstDocumentLoadTimestamp: -1,
+  };
+}
 
 function addTimingMarker(state, action) {
+  state = { ...state };
+
   if (action.marker.name === "document::DOMContentLoaded" &&
       state.firstDocumentDOMContentLoadedTimestamp === -1) {
-    return state.set("firstDocumentDOMContentLoadedTimestamp",
-                     action.marker.unixTime / 1000);
+    state.firstDocumentDOMContentLoadedTimestamp = action.marker.unixTime / 1000;
+    return state;
   }
 
   if (action.marker.name === "document::Load" &&
       state.firstDocumentLoadTimestamp === -1) {
-    return state.set("firstDocumentLoadTimestamp",
-                     action.marker.unixTime / 1000);
+    state.firstDocumentLoadTimestamp = action.marker.unixTime / 1000;
+    return state;
   }
 
   return state;
 }
 
 function clearTimingMarkers(state) {
-  return state.withMutations(st => {
-    st.remove("firstDocumentDOMContentLoadedTimestamp");
-    st.remove("firstDocumentLoadTimestamp");
-  });
+  return new TimingMarkers();
 }
 
 function timingMarkers(state = new TimingMarkers(), action) {
   switch (action.type) {
     case ADD_TIMING_MARKER:
       return addTimingMarker(state, action);
 
     case CLEAR_REQUESTS:
--- a/devtools/client/netmonitor/src/request-list-context-menu.js
+++ b/devtools/client/netmonitor/src/request-list-context-menu.js
@@ -38,16 +38,17 @@ class RequestListContextMenu {
     let copySubmenu = [];
     let {
       id,
       isCustom,
       method,
       mimeType,
       httpVersion,
       requestHeaders,
+      requestPostData,
       requestPostDataAvailable,
       responseHeaders,
       responseContentAvailable,
       url,
     } = selectedRequest || {};
     let {
       cloneSelectedRequest,
       openStatistics,
@@ -68,17 +69,17 @@ class RequestListContextMenu {
       visible: !!(selectedRequest && getUrlQuery(url)),
       click: () => this.copyUrlParams(url),
     });
 
     copySubmenu.push({
       id: "request-list-context-copy-post-data",
       label: L10N.getStr("netmonitor.context.copyPostData"),
       accesskey: L10N.getStr("netmonitor.context.copyPostData.accesskey"),
-      visible: !!(selectedRequest && requestPostDataAvailable),
+      visible: !!(selectedRequest && (requestPostDataAvailable || requestPostData)),
       click: () => this.copyPostData(id),
     });
 
     copySubmenu.push({
       id: "request-list-context-copy-as-curl",
       label: L10N.getStr("netmonitor.context.copyAsCurl"),
       accesskey: L10N.getStr("netmonitor.context.copyAsCurl.accesskey"),
       visible: !!selectedRequest,
--- a/devtools/client/netmonitor/src/selectors/timing-markers.js
+++ b/devtools/client/netmonitor/src/selectors/timing-markers.js
@@ -1,13 +1,13 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 function getDisplayedTimingMarker(state, marker) {
-  return state.timingMarkers.get(marker) - state.requests.firstStartedMillis;
+  return state.timingMarkers.marker - state.requests.firstStartedMillis;
 }
 
 module.exports = {
   getDisplayedTimingMarker,
 };
--- a/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
+++ b/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { FILTER_FLAGS } = require("../constants");
 
-/*
+/**
  * Generates a value for the given filter
  * ie. if flag = status-code, will generate "200" from the given request item.
  * For flags related to cookies, it might generate an array based on the request
  * ie. ["cookie-name-1", "cookie-name-2", ...]
  *
  * @param {string} flag - flag specified in filter, ie. "status-code"
  * @param {object} request - Network request item
  * @return {string|Array} - The output is a string or an array based on the request
@@ -65,17 +65,17 @@ function getAutocompleteValuesForFlag(fl
     case "method":
     default:
       values.push(request[flag]);
   }
 
   return values;
 }
 
-/*
+/**
  * For a given lastToken passed ie. "is:", returns an array of populated flag
  * values for consumption in autocompleteProvider
  * ie. ["is:cached", "is:running", "is:from-cache"]
  *
  * @param {string} lastToken - lastToken parsed from filter input, ie "is:"
  * @param {object} requests - List of requests from which values are generated
  * @return {Array} - array of autocomplete values
  */
--- a/devtools/client/netmonitor/test/browser_net_autoscroll.js
+++ b/devtools/client/netmonitor/test/browser_net_autoscroll.js
@@ -70,18 +70,16 @@ add_task(function* () {
     return waitUntil(() => !!document.querySelector(".requests-list-contents"));
   }
 
   function* waitForRequestsToOverflowContainer() {
     info("Waiting for enough requests to overflow the container");
     while (true) {
       info("Waiting for one network request");
       yield waitForNetworkEvents(monitor, 1);
-      console.log(requestsContainer.scrollHeight);
-      console.log(requestsContainer.clientHeight);
       if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
         info("The list is long enough, returning");
         return;
       }
     }
   }
 
   function scrolledToBottom(element) {
--- a/devtools/client/netmonitor/test/browser_net_cause.js
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -97,41 +97,41 @@ add_task(function* () {
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   let wait = waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
   tab.linkedBrowser.loadURI(CAUSE_URL);
   yield wait;
 
-  // Fetch stack-trace data from the backend and wait till
-  // all packets are received.
   let requests = getSortedRequests(store.getState());
   yield Promise.all(requests.map(requestItem =>
     connector.requestData(requestItem.id, "stackTrace")));
 
   is(store.getState().requests.requests.size, EXPECTED_REQUESTS.length,
     "All the page events should be recorded.");
 
-  EXPECTED_REQUESTS.forEach((spec, i) => {
+  EXPECTED_REQUESTS.forEach(async (spec, i) => {
     let { method, url, causeType, causeUri, stack } = spec;
 
     let requestItem = getSortedRequests(store.getState()).get(i);
     verifyRequestItemTarget(
       document,
       getDisplayedRequests(store.getState()),
       requestItem,
       method,
       url,
       { cause: { type: causeType, loadingDocumentUri: causeUri } }
     );
 
     let stacktrace = requestItem.stacktrace;
     let stackLen = stacktrace ? stacktrace.length : 0;
 
+    await waitUntil(() => !!requestItem.stacktrace);
+
     if (stack) {
       ok(stacktrace, `Request #${i} has a stacktrace`);
       ok(stackLen > 0,
         `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
 
       // if "stack" is array, check the details about the top stack frames
       if (Array.isArray(stack)) {
         stack.forEach((frame, j) => {
--- a/devtools/client/netmonitor/test/browser_net_charts-01.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-01.js
@@ -3,22 +3,27 @@
 
 "use strict";
 
 /**
  * Makes sure Pie Charts have the right internal structure.
  */
 
 add_task(function* () {
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
+
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let pie = Chart.Pie(document, {
     width: 100,
     height: 100,
     data: [{
       size: 1,
       label: "foo"
     }, {
       size: 2,
--- a/devtools/client/netmonitor/test/browser_net_charts-02.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-02.js
@@ -6,22 +6,26 @@
 /**
  * Makes sure Pie Charts have the right internal structure when
  * initialized with empty data.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let pie = Chart.Pie(document, {
     data: null,
     width: 100,
     height: 100
   });
 
   let node = pie.node;
   let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -5,22 +5,26 @@
 
 /**
  * Makes sure Table Charts have the right internal structure.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let table = Chart.Table(document, {
     title: "Table title",
     data: [{
       label1: 1,
       label2: 11.1
     }, {
       label1: 2,
       label2: 12.2
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -6,22 +6,26 @@
 /**
  * Makes sure Pie Charts have the right internal structure when
  * initialized with empty data.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let table = Chart.Table(document, {
     title: "Table title",
     data: null,
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     },
     header: {
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -5,22 +5,26 @@
 
 /**
  * Makes sure Pie+Table Charts have the right internal structure.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let chart = Chart.PieTable(document, {
     title: "Table title",
     data: [{
       size: 1,
       label: 11.1
     }, {
       size: 2,
       label: 12.2
--- a/devtools/client/netmonitor/test/browser_net_charts-06.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-06.js
@@ -5,22 +5,26 @@
 
 /**
  * Makes sure Pie Charts correctly handle empty source data.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let pie = Chart.Pie(document, {
     data: [],
     width: 100,
     height: 100
   });
 
   let node = pie.node;
   let slices = node.querySelectorAll(".pie-chart-slice.chart-colored-blob");
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -5,22 +5,26 @@
 
 /**
  * Makes sure Table Charts correctly handle empty source data.
  */
 
 add_task(function* () {
   let { L10N } = require("devtools/client/netmonitor/src/utils/l10n");
 
-  let { monitor } = yield initNetMonitor(SIMPLE_URL);
+  let { monitor, tab } = yield initNetMonitor(SIMPLE_URL);
   info("Starting test... ");
 
   let { document, windowRequire } = monitor.panelWin;
   let { Chart } = windowRequire("devtools/client/shared/widgets/Chart");
 
+  let wait = waitForNetworkEvents(monitor, 1);
+  tab.linkedBrowser.loadURI(SIMPLE_URL);
+  yield wait;
+
   let table = Chart.Table(document, {
     data: [],
     totals: {
       label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
       label2: value => "World " + L10N.numberWithDecimals(value, 2)
     },
     header: {
       label1: "",
--- a/devtools/client/netmonitor/test/browser_net_complex-params.js
+++ b/devtools/client/netmonitor/test/browser_net_complex-params.js
@@ -13,17 +13,17 @@ add_task(function* () {
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let { L10N } = windowRequire("devtools/client/netmonitor/src/utils/l10n");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 1, 6);
+  let wait = waitForNetworkEvents(monitor, 7);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, "#params-panel .tree-section", 2);
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
--- a/devtools/client/netmonitor/test/browser_net_copy_params.js
+++ b/devtools/client/netmonitor/test/browser_net_copy_params.js
@@ -11,17 +11,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(PARAMS_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 1, 6);
+  let wait = waitForNetworkEvents(monitor, 7);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   yield testCopyUrlParamsHidden(0, false);
   yield testCopyUrlParams(0, "a");
   yield testCopyPostDataHidden(0, false);
--- a/devtools/client/netmonitor/test/browser_net_cors_requests.js
+++ b/devtools/client/netmonitor/test/browser_net_cors_requests.js
@@ -14,17 +14,17 @@ add_task(function* () {
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 1, 1);
+  let wait = waitForNetworkEvents(monitor, 2);
 
   info("Performing a CORS request");
   let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
   yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
     content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
   });
 
   info("Waiting until the requests appear in netmonitor");
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -20,17 +20,17 @@ add_task(function* () {
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
   let {
     getLongString,
     requestData,
   } = connector;
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 1, 3);
+  let wait = waitForNetworkEvents(monitor, 4);
   yield ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, function* (url) {
     content.wrappedJSObject.performRequests(url);
   });
   yield wait;
 
   let requests = {
     get: getSortedRequests(store.getState()).get(0),
     post: getSortedRequests(store.getState()).get(1),
--- a/devtools/client/netmonitor/test/browser_net_header-docs.js
+++ b/devtools/client/netmonitor/test/browser_net_header-docs.js
@@ -15,17 +15,17 @@ add_task(function* () {
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
   let { getHeadersURL } = require("devtools/client/netmonitor/src/utils/mdn-utils");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 2);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   EventUtils.sendMouseEvent({ type: "click" },
     document.querySelectorAll(".request-list-item")[0]);
 
--- a/devtools/client/netmonitor/test/browser_net_headers-alignment.js
+++ b/devtools/client/netmonitor/test/browser_net_headers-alignment.js
@@ -53,17 +53,15 @@ add_task(function* () {
     return waitUntil(() => !!document.querySelector(".requests-list-contents"));
   }
 
   function* waitForRequestsToOverflowContainer() {
     info("Waiting for enough requests to overflow the container");
     while (true) {
       info("Waiting for one network request");
       yield waitForNetworkEvents(monitor, 1);
-      console.log(requestsContainer.scrollHeight);
-      console.log(requestsContainer.clientHeight);
       if (requestsContainer.scrollHeight > requestsContainer.clientHeight) {
         info("The list is long enough, returning");
         return;
       }
     }
   }
 });
--- a/devtools/client/netmonitor/test/browser_net_params_sorted.js
+++ b/devtools/client/netmonitor/test/browser_net_params_sorted.js
@@ -11,17 +11,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 2);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, ".headers-overview");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
--- a/devtools/client/netmonitor/test/browser_net_post-data-01.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-01.js
@@ -20,17 +20,17 @@ add_task(function* () {
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getDisplayedRequests,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 2);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   let requestItems = document.querySelectorAll(".request-list-item");
   for (let requestItem of requestItems) {
     requestItem.scrollIntoView();
--- a/devtools/client/netmonitor/test/browser_net_post-data-02.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-02.js
@@ -14,17 +14,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_RAW_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
   wait = waitForDOM(document, "#params-panel .tree-section");
   EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/browser_net_post-data-03.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-03.js
@@ -14,17 +14,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_RAW_WITH_HEADERS_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
   wait = waitForDOM(document, "#headers-panel .tree-section .treeLabel", 3);
   EventUtils.sendMouseEvent({ type: "click" },
--- a/devtools/client/netmonitor/test/browser_net_post-data-04.js
+++ b/devtools/client/netmonitor/test/browser_net_post-data-04.js
@@ -14,17 +14,17 @@ add_task(function* () {
   let { tab, monitor } = yield initNetMonitor(POST_JSON_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   // Wait for all tree view updated by react
   wait = waitForDOM(document, "#params-panel .tree-section");
   EventUtils.sendMouseEvent({ type: "click" },
--- a/devtools/client/netmonitor/test/browser_net_raw_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_raw_headers.js
@@ -14,32 +14,30 @@ add_task(function* () {
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 2);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   wait = waitForDOM(document, ".headers-overview");
   EventUtils.sendMouseEvent({ type: "mousedown" },
     document.querySelectorAll(".request-list-item")[0]);
   yield wait;
 
-  let onRequestPostData = monitor.panelWin.once(EVENTS.RECEIVED_REQUEST_POST_DATA);
   wait = waitForDOM(document, ".raw-headers-container textarea", 2);
   EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
   yield wait;
-  yield onRequestPostData;
 
   testRawHeaderButtonStyle(true);
 
   testShowRawHeaders(getSortedRequests(store.getState()).get(0));
 
   EventUtils.sendMouseEvent({ type: "click" }, getRawHeadersButton());
 
   testRawHeaderButtonStyle(false);
--- a/devtools/client/netmonitor/test/browser_net_resend.js
+++ b/devtools/client/netmonitor/test/browser_net_resend.js
@@ -21,17 +21,17 @@ add_task(function* () {
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSelectedRequest,
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
-  let wait = waitForNetworkEvents(monitor, 0, 2);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
     content.wrappedJSObject.performRequests();
   });
   yield wait;
 
   let origItem = getSortedRequests(store.getState()).get(0);
 
   store.dispatch(Actions.selectRequest(origItem.id));
@@ -47,17 +47,17 @@ add_task(function* () {
   // edit the custom request
   yield editCustomForm();
 
   // FIXME: reread the customItem, it's been replaced by a new object (immutable!)
   customItem = getSelectedRequest(store.getState());
   testCustomItemChanged(customItem, origItem);
 
   // send the new request
-  wait = waitForNetworkEvents(monitor, 0, 1);
+  wait = waitForNetworkEvents(monitor, 1);
   store.dispatch(Actions.sendCustomRequest(connector));
   yield wait;
 
   let sentItem = getSelectedRequest(store.getState());
 
   yield testSentRequest(sentItem, origItem);
 
   // Ensure the UI shows the new request, selected, and that the detail panel was closed.
--- a/devtools/client/netmonitor/test/browser_net_resend_cors.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_cors.js
@@ -18,34 +18,34 @@ add_task(function* () {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   store.dispatch(Actions.batchEnable(false));
 
   let requestUrl = "http://test1.example.com" + CORS_SJS_PATH;
 
   info("Waiting for OPTIONS, then POST");
-  let wait = waitForNetworkEvents(monitor, 1, 1);
+  let wait = waitForNetworkEvents(monitor, 2);
   yield ContentTask.spawn(tab.linkedBrowser, requestUrl, function* (url) {
     content.wrappedJSObject.performRequests(url, "triggering/preflight", "post-data");
   });
   yield wait;
 
   const METHODS = ["OPTIONS", "POST"];
   const ITEMS = METHODS.map((val, i) => getSortedRequests(store.getState()).get(i));
 
   // Check the requests that were sent
   ITEMS.forEach((item, i) => {
     is(item.method, METHODS[i], `The ${item.method} request has the right method`);
     is(item.url, requestUrl, `The ${item.method} request has the right URL`);
   });
 
   // Resend both requests without modification. Wait for resent OPTIONS, then POST.
   // POST is supposed to have no preflight OPTIONS request this time (CORS is disabled)
-  let onRequests = waitForNetworkEvents(monitor, 1, 0);
+  let onRequests = waitForNetworkEvents(monitor, 1);
   ITEMS.forEach((item) => {
     info(`Selecting the ${item.method} request`);
     store.dispatch(Actions.selectRequest(item.id));
 
     info("Cloning the selected request into a custom clone");
     store.dispatch(Actions.cloneSelectedRequest());
 
     info("Sending the cloned request (without change)");
--- a/devtools/client/netmonitor/test/browser_net_resend_headers.js
+++ b/devtools/client/netmonitor/test/browser_net_resend_headers.js
@@ -25,17 +25,17 @@ add_task(function* () {
     { name: "Host", value: "fakehost.example.com" },
     { name: "User-Agent", value: "Testzilla" },
     { name: "Referer", value: "http://example.com/referrer" },
     { name: "Accept", value: "application/jarda"},
     { name: "Accept-Encoding", value: "compress, identity, funcoding" },
     { name: "Accept-Language", value: "cs-CZ" }
   ];
 
-  let wait = waitForNetworkEvents(monitor, 0, 1);
+  let wait = waitForNetworkEvents(monitor, 1);
   sendHTTPRequest({
     url: requestUrl,
     method: "POST",
     headers: requestHeaders,
     body: "Hello"
   });
   yield wait;
 
--- a/devtools/client/netmonitor/test/browser_net_throttle.js
+++ b/devtools/client/netmonitor/test/browser_net_throttle.js
@@ -11,17 +11,16 @@ add_task(function* () {
 });
 
 function* throttleTest(actuallyThrottle) {
   requestLongerTimeout(2);
 
   let { monitor } = yield initNetMonitor(SIMPLE_URL);
   let { store, windowRequire, connector } = monitor.panelWin;
   let { ACTIVITY_TYPE } = windowRequire("devtools/client/netmonitor/src/constants");
-  let { EVENTS } = windowRequire("devtools/client/netmonitor/src/constants");
   let { setPreferences, triggerActivity } = connector;
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
 
   info("Starting test... (actuallyThrottle = " + actuallyThrottle + ")");
 
   // When throttling, must be smaller than the length of the content
@@ -41,19 +40,19 @@ function* throttleTest(actuallyThrottle)
 
   info("sending throttle request");
   yield new Promise((resolve) => {
     setPreferences(request, response => {
       resolve(response);
     });
   });
 
-  let eventPromise = monitor.panelWin.once(EVENTS.RECEIVED_EVENT_TIMINGS);
+  let wait = waitForNetworkEvents(monitor, 1);
   yield triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_DISABLED);
-  yield eventPromise;
+  yield wait;
 
   yield waitUntil(() => {
     let requestItem = getSortedRequests(store.getState()).get(0);
     return requestItem && requestItem.eventTimings;
   });
 
   let requestItem = getSortedRequests(store.getState()).get(0);
   const reportedOneSecond = requestItem.eventTimings.timings.receive > 1000;
--- a/devtools/client/netmonitor/test/browser_net_timing-division.js
+++ b/devtools/client/netmonitor/test/browser_net_timing-division.js
@@ -3,22 +3,16 @@
 
 "use strict";
 
 /**
  * Tests if timing intervals are divided againts seconds when appropriate.
  */
 
 add_task(function* () {
-  // Make sure timing division can render properly
-  Services.prefs.setCharPref(
-    "devtools.netmonitor.visibleColumns",
-    "[\"waterfall\"]"
-  );
-
   let { tab, monitor } = yield initNetMonitor(CUSTOM_GET_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   let {
     getSortedRequests,
   } = windowRequire("devtools/client/netmonitor/src/selectors/index");
--- a/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
+++ b/devtools/client/netmonitor/test/browser_net_view-source-debugger.js
@@ -12,17 +12,17 @@ add_task(async function () {
 
   let { tab, monitor, toolbox } = await initNetMonitor(POST_DATA_URL);
   info("Starting test... ");
 
   let { document, store, windowRequire } = monitor.panelWin;
   let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
   store.dispatch(Actions.batchEnable(false));
 
-  let waitForContentRequests = waitForNetworkEvents(monitor, 0, 2);
+  let waitForContentRequests = waitForNetworkEvents(monitor, 2);
   await ContentTask.spawn(tab.linkedBrowser, {},
     () => content.wrappedJSObject.performRequests());
   await waitForContentRequests;
 
   info("Clicking stack-trace tab and waiting for stack-trace panel to open");
   let wait = waitForDOM(document, "#stack-trace-panel .frame-link", 4);
   // Click on the first request
   EventUtils.sendMouseEvent({ type: "mousedown" },
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -216,16 +216,55 @@ function waitForAllRequestsFinished(moni
       resolve();
     }
 
     window.on(EVENTS.NETWORK_EVENT, onRequest);
     window.on(EVENTS.PAYLOAD_READY, onTimings);
   });
 }
 
+let finishedQueue = {};
+let updatingTypes = [
+  "NetMonitor:NetworkEventUpdating:RequestCookies",
+  "NetMonitor:NetworkEventUpdating:ResponseCookies",
+];
+let updatedTypes = [
+  "NetMonitor:NetworkEventUpdated:RequestCookies",
+  "NetMonitor:NetworkEventUpdated:ResponseCookies",
+];
+
+// Start collecting all networkEventUpdate event when panel is opened.
+// removeTab() should be called once all corresponded RECEIVED_* events finished.
+function startNetworkEventUpdateObserver(panelWin) {
+  updatingTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+    let key = actor + "-" + event.replace("NetMonitor:NetworkEventUpdating:", "");
+    finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] + 1 : 1;
+  }));
+
+  updatedTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+    let key = actor + "-" + event.replace("NetMonitor:NetworkEventUpdated:", "");
+    finishedQueue[key]--;
+  }));
+}
+
+function* waitForAllNetworkUpdateEvents() {
+  function checkNetworkEventUpdateState() {
+    for (let key in finishedQueue) {
+      if (finishedQueue[key] > 0) {
+        return false;</