Merge autoland to mozilla-central. a=merge
authorMihai Alexandru Michis <malexandru@mozilla.com>
Fri, 02 Aug 2019 12:47:54 +0300
changeset 485971 5ced3811411e9b6f8f1ba02f518d0a5966a412af
parent 485970 b5f2fa86e69682a8cf1571f478c31c9afb26fba1 (current diff)
parent 485889 483c129b10e3d94850e3a6cc94545e11139a6343 (diff)
child 485972 d1b8995883080c8972bf7b07f933994622438254
child 485983 4391599b72686454ae7f1bda2952ab10cad4ee17
push id91567
push usercsabou@mozilla.com
push dateFri, 02 Aug 2019 16:05:38 +0000
treeherderautoland@cc5b3d1d6869 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone70.0a1
first release with
nightly linux32
5ced3811411e / 70.0a1 / 20190802094835 / files
nightly linux64
5ced3811411e / 70.0a1 / 20190802094835 / files
nightly mac
5ced3811411e / 70.0a1 / 20190802094835 / files
nightly win32
5ced3811411e / 70.0a1 / 20190802094835 / files
nightly win64
5ced3811411e / 70.0a1 / 20190802094835 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to mozilla-central. a=merge
testing/web-platform/meta/svg/types/scripted/event-handler-all-document-element-events.svg.ini
--- a/accessible/generic/HyperTextAccessible.cpp
+++ b/accessible/generic/HyperTextAccessible.cpp
@@ -28,22 +28,23 @@
 #include "nsPersistentProperties.h"
 #include "nsIScrollableFrame.h"
 #include "nsIServiceManager.h"
 #include "nsITextControlElement.h"
 #include "nsIMathMLFrame.h"
 #include "nsRange.h"
 #include "nsTextFragment.h"
 #include "mozilla/BinarySearch.h"
-#include "mozilla/dom/Element.h"
 #include "mozilla/EventStates.h"
-#include "mozilla/dom/Selection.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/TextEditor.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
+#include "mozilla/dom/Selection.h"
 #include "gfxSkipChars.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // HyperTextAccessible
@@ -250,23 +251,20 @@ uint32_t HyperTextAccessible::DOMPointTo
     }
   }
 
   // Get accessible for this findNode, or if that node isn't accessible, use the
   // accessible for the next DOM node which has one (based on forward depth
   // first search)
   Accessible* descendant = nullptr;
   if (findNode) {
-    nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
-    if (findContent && findContent->IsHTMLElement(nsGkAtoms::br) &&
-        findContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                              nsGkAtoms::mozeditorbogusnode,
-                                              nsGkAtoms::_true, eIgnoreCase)) {
-      // This <br> is the hacky "bogus node" used when there is no text in a
-      // control
+    dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(findNode);
+    if (brElement && brElement->IsPaddingForEmptyEditor()) {
+      // This <br> is the hacky "padding <br> element" used when there is no
+      // text in the editor.
       return 0;
     }
 
     descendant = mDoc->GetAccessible(findNode);
     if (!descendant && findNode->IsContent()) {
       Accessible* container = mDoc->GetContainerAccessible(findNode);
       if (container) {
         TreeWalker walker(container, findNode->AsContent(),
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -29,16 +29,22 @@ using namespace mozilla::a11y;
 // OuterDocAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 OuterDocAccessible::OuterDocAccessible(nsIContent* aContent,
                                        DocAccessible* aDoc)
     : AccessibleWrap(aContent, aDoc) {
   mType = eOuterDocType;
 
+#ifdef XP_WIN
+  if (DocAccessibleParent* remoteDoc = RemoteChildDoc()) {
+    remoteDoc->SendParentCOMProxy(this);
+  }
+#endif
+
   if (IPCAccessibilityActive()) {
     auto bridge = dom::BrowserBridgeChild::GetFrom(aContent);
     if (bridge) {
       // This is an iframe which will be rendered in another process.
       SendEmbedderAccessible(bridge);
     }
   }
 
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -762,17 +762,22 @@ void DocAccessibleParent::SendParentCOMP
   auto tab = static_cast<dom::BrowserParent*>(Manager());
   MOZ_ASSERT(tab);
   if (tab->IsDestroyed()) {
     return;
   }
 
   RefPtr<IAccessible> nativeAcc;
   aOuterDoc->GetNativeInterface(getter_AddRefs(nativeAcc));
-  MOZ_ASSERT(nativeAcc);
+  if (NS_WARN_IF(!nativeAcc)) {
+    // Couldn't get a COM proxy for the outer doc. That probably means it died,
+    // but the parent process hasn't received a message to remove it from the
+    // ProxyAccessible tree yet.
+    return;
+  }
 
   RefPtr<IDispatch> wrapped(
       mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(nativeAcc)));
 
   IDispatchHolder::COMPtrType ptr(mscom::ToProxyUniquePtr(std::move(wrapped)));
   IDispatchHolder holder(std::move(ptr));
   if (!PDocAccessibleParent::SendParentCOMProxy(holder)) {
     return;
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -213,16 +213,19 @@ class DocAccessibleParent : public Proxy
   DocAccessibleParent* ChildDocAt(size_t aIdx) {
     return LiveDocs().Get(mChildDocs[aIdx]);
   }
 
 #if defined(XP_WIN)
   void MaybeInitWindowEmulation();
 
   /**
+   * Note that an OuterDocAccessible can be created before the
+   * DocAccessibleParent or vice versa. Therefore, this must be conditionally
+   * called when either of these is created.
    * @param aOuterDoc The OuterDocAccessible to be returned as the parent of
    *        this document. Only GetNativeInterface() is called on this, so it
    *        may be a ProxyAccessibleWrap or similar.
    */
   void SendParentCOMProxy(Accessible* aOuterDoc);
 
   virtual mozilla::ipc::IPCResult RecvGetWindowedPluginIAccessible(
       const WindowsHandle& aHwnd, IAccessibleHolder* aPluginCOMProxy) override;
--- a/accessible/ipc/win/DocAccessibleChild.cpp
+++ b/accessible/ipc/win/DocAccessibleChild.cpp
@@ -41,17 +41,17 @@ void DocAccessibleChild::Shutdown() {
   }
 
   PushDeferredEvent(MakeUnique<SerializedShutdown>(this));
   DetachDocument();
 }
 
 ipc::IPCResult DocAccessibleChild::RecvParentCOMProxy(
     const IDispatchHolder& aParentCOMProxy) {
-  MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
+  MOZ_ASSERT(!aParentCOMProxy.IsNull());
   mParentProxy.reset(const_cast<IDispatchHolder&>(aParentCOMProxy).Release());
   SetConstructedInParentProcess();
 
   for (uint32_t i = 0, l = mDeferredEvents.Length(); i < l; ++i) {
     mDeferredEvents[i]->Dispatch();
   }
 
   mDeferredEvents.Clear();
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1542,20 +1542,19 @@ pref("toolkit.telemetry.ecosystemtelemet
 pref("browser.ping-centre.telemetry", true);
 pref("browser.ping-centre.log", false);
 pref("browser.ping-centre.staging.endpoint", "https://onyx_tiles.stage.mozaws.net/v3/links/ping-centre");
 pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.com/v3/links/ping-centre");
 
 // Enable GMP support in the addon manager.
 pref("media.gmp-provider.enabled", true);
 
+// Enable blocking access to storage from tracking resources by default.
+pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
 #ifdef EARLY_BETA_OR_EARLIER
-// Enable blocking access to storage from tracking resources only in nightly
-// and early beta. By default the value is 0: BEHAVIOR_ACCEPT
-pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
 // Enable fingerprinting blocking by default only in nightly and early beta.
 pref("privacy.trackingprotection.fingerprinting.enabled", true);
 #endif
 
 // Enable cryptomining blocking by default for all channels, only on desktop.
 pref("privacy.trackingprotection.cryptomining.enabled", true);
 
 pref("browser.contentblocking.database.enabled", true);
@@ -1735,22 +1734,18 @@ pref("extensions.pocket.enabled", true);
 pref("extensions.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("extensions.pocket.site", "getpocket.com");
 
 pref("signon.generation.available", true);
 pref("signon.generation.enabled", true);
 pref("signon.schemeUpgrades", true);
 pref("signon.privateBrowsingCapture.enabled", true);
 pref("signon.showAutoCompleteFooter", true);
-#ifdef NIGHTLY_BUILD
 pref("signon.management.page.enabled", true);
 pref("signon.management.overrideURI", "about:logins?filter=%DOMAIN%");
-#else
-pref("signon.management.page.enabled", false);
-#endif
 pref("signon.management.page.breach-alerts.enabled", false);
 #ifdef NIGHTLY_BUILD
 // Bug 1563330 tracks shipping this by default.
 pref("signon.showAutoCompleteOrigins", true);
 pref("signon.includeOtherSubdomainsInLookup", true);
 #endif
 pref("signon.management.page.faqURL", "https://lockwise.firefox.com/faq.html");
 pref("signon.management.page.feedbackURL",
old mode 100755
new mode 100644
copy from build/moz.configure/toolchain.configure
copy to build/moz.configure/lto-pgo.configure
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/lto-pgo.configure
@@ -1,1421 +1,16 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-imply_option('--enable-release', mozilla_official)
-imply_option('--enable-release', depends_if('MOZ_AUTOMATION')(lambda x: True))
-
-js_option('--enable-release',
-          default=milestone.is_release_or_beta,
-          help='{Build|Do not build} with more conservative, release '
-               'engineering-oriented options.{ This may slow down builds.|}')
-
-
-@depends('--enable-release')
-def developer_options(value):
-    if not value:
-        return True
-
-
-add_old_configure_assignment('DEVELOPER_OPTIONS', developer_options)
-set_config('DEVELOPER_OPTIONS', developer_options)
-
-# Code optimization
-# ==============================================================
-
-js_option('--disable-optimize',
-          nargs='?',
-          help='Disable optimizations via compiler flags')
-
-
-@depends('--enable-optimize', '--help')
-def moz_optimize(option, _):
-    flags = None
-
-    if len(option):
-        val = '2'
-        flags = option[0]
-    elif option:
-        val = '1'
-    else:
-        val = None
-
-    return namespace(
-        optimize=val,
-        flags=flags,
-    )
-
-
-set_config('MOZ_OPTIMIZE', moz_optimize.optimize)
-add_old_configure_assignment('MOZ_OPTIMIZE', moz_optimize.optimize)
-add_old_configure_assignment('MOZ_CONFIGURE_OPTIMIZE_FLAGS', moz_optimize.flags)
-
-# yasm detection
-# ==============================================================
-yasm = check_prog('YASM', ['yasm'], allow_missing=True)
-
-
-@depends_if(yasm)
-@checking('yasm version')
-def yasm_version(yasm):
-    version = check_cmd_output(
-        yasm, '--version',
-        onerror=lambda: die('Failed to get yasm version.')
-    ).splitlines()[0].split()[1]
-    return Version(version)
-
-
-@depends(yasm, target)
-def yasm_asflags(yasm, target):
-    if yasm:
-        asflags = {
-            ('OSX', 'x86'): ['-f', 'macho32'],
-            ('OSX', 'x86_64'): ['-f', 'macho64'],
-            ('WINNT', 'x86'): ['-f', 'win32'],
-            ('WINNT', 'x86_64'): ['-f', 'x64'],
-        }.get((target.os, target.cpu), None)
-        if asflags is None:
-            # We're assuming every x86 platform we support that's
-            # not Windows or Mac is ELF.
-            if target.cpu == 'x86':
-                asflags = ['-f', 'elf32']
-            elif target.cpu == 'x86_64':
-                asflags = ['-f', 'elf64']
-        if asflags:
-            asflags += ['-rnasm', '-pnasm']
-        return asflags
-
-
-set_config('YASM_ASFLAGS', yasm_asflags)
-
-
-# Android NDK
-# ==============================================================
-
-
-@depends('--disable-compile-environment', target)
-def compiling_android(compile_env, target):
-    return compile_env and target.os == 'Android'
-
-
-include('android-ndk.configure', when=compiling_android)
-
-with only_when(target_is_osx):
-    # MacOS deployment target version
-    # ==============================================================
-    # This needs to happen before any compilation test is done.
-
-    option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1,
-           default='10.9', help='Set the minimum MacOS version needed at runtime')
-
-
-    @depends('--enable-macos-target')
-    @imports(_from='os', _import='environ')
-    def macos_target(value):
-        if value:
-            # Ensure every compiler process we spawn uses this value.
-            environ['MACOSX_DEPLOYMENT_TARGET'] = value[0]
-            return value[0]
-
-
-    set_config('MACOSX_DEPLOYMENT_TARGET', macos_target)
-    add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target)
-
-
-@depends(host)
-def host_is_osx(host):
-    if host.os == 'OSX':
-        return True
-
-
-with only_when(host_is_osx | target_is_osx):
-    # MacOS SDK
-    # =========
-    js_option('--with-macos-sdk', env='MACOS_SDK_DIR', nargs=1,
-           help='Location of platform SDK to use')
-
-    @depends('--with-macos-sdk', host)
-    @imports(_from='os.path', _import='isdir')
-    @imports(_from='biplist', _import='readPlist')
-    def macos_sdk(sdk, host):
-        sdk_min_version = Version('10.11')
-        sdk_max_version = Version('10.14')
-
-        if sdk:
-            sdk = sdk[0]
-        elif host.os == 'OSX':
-            sdk = check_cmd_output('xcrun', '--show-sdk-path', onerror=lambda: '').rstrip()
-            if not sdk:
-                die('Could not find the macOS SDK. Please use --with-macos-sdk to give '
-                    'the path to a macOS SDK.')
-        else:
-            die('Need a macOS SDK when targeting macOS. Please use --with-macos-sdk '
-                'to give the path to a macOS SDK.')
-
-        if not isdir(sdk):
-            die('SDK not found in %s. When using --with-macos-sdk, you must specify a '
-                'valid SDK. SDKs are installed when the optional cross-development '
-                'tools are selected during the Xcode/Developer Tools installation.'
-                % sdk)
-        obj = readPlist(os.path.join(sdk, 'SDKSettings.plist'))
-        if not obj:
-            die('Error parsing SDKSettings.plist in the SDK directory: %s' % sdk)
-        if 'Version' not in obj:
-            die('Error finding Version information in SDKSettings.plist from the SDK: %s' % sdk)
-        version = Version(obj['Version'])
-        if version < sdk_min_version:
-            die('SDK version "%s" is too old. Please upgrade to at least %s. '
-                'You may need to point to it using --with-macos-sdk=<path> in your '
-                'mozconfig. Various SDK versions are available from '
-                'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_min_version))
-        if version > sdk_max_version:
-            die('SDK version "%s" is unsupported. Please downgrade to version '
-                '%s. You may need to point to it using --with-macos-sdk=<path> in '
-                'your mozconfig. Various SDK versions are available from '
-                'https://github.com/phracker/MacOSX-SDKs' % (version, sdk_max_version))
-        return sdk
-
-    set_config('MACOS_SDK_DIR', macos_sdk)
-
-
-with only_when(target_is_osx):
-    with only_when(cross_compiling):
-        option('--with-macos-private-frameworks',
-               env="MACOS_PRIVATE_FRAMEWORKS_DIR", nargs=1,
-               help='Location of private frameworks to use')
-
-        @depends_if('--with-macos-private-frameworks')
-        @imports(_from='os.path', _import='isdir')
-        def macos_private_frameworks(value):
-            if value and not isdir(value[0]):
-                die('PrivateFrameworks not found not found in %s. When using '
-                    '--with-macos-private-frameworks, you must specify a valid '
-                    'directory', value[0])
-            return value[0]
-
-    @depends(macos_private_frameworks)
-    def macos_private_frameworks(value):
-        if value:
-            return value
-        return '/System/Library/PrivateFrameworks'
-
-    set_config('MACOS_PRIVATE_FRAMEWORKS_DIR', macos_private_frameworks)
-
-
-with only_when(host_is_osx):
-    # Xcode state
-    # ===========
-    js_option('--disable-xcode-checks',
-	      help='Do not check that Xcode is installed and properly configured')
-
-
-    @depends(host, '--disable-xcode-checks')
-    def xcode_path(host, xcode_checks):
-	if host.kernel != 'Darwin' or not xcode_checks:
-	    return
-
-	# xcode-select -p prints the path to the installed Xcode. It
-	# should exit 0 and return non-empty result if Xcode is installed.
-
-	def bad_xcode_select():
-	    die('Could not find installed Xcode; install Xcode from the App '
-		'Store, run it once to perform initial configuration, and then '
-		'try again; in the rare case you wish to build without Xcode '
-		'installed, add the --disable-xcode-checks configure flag')
-
-	xcode_path = check_cmd_output('xcode-select', '--print-path',
-				      onerror=bad_xcode_select).strip()
-
-	if not xcode_path:
-	    bad_xcode_select()
-
-	# Now look for the Command Line Tools.
-	def no_cltools():
-	    die('Could not find installed Xcode Command Line Tools; '
-		'run `xcode-select --install` and follow the instructions '
-		'to install them then try again; if you wish to build without '
-		'Xcode Command Line Tools installed, '
-		'add the --disable-xcode-checks configure flag')
-
-	check_cmd_output('pkgutil', '--pkg-info',
-			 'com.apple.pkg.CLTools_Executables',
-			 onerror=no_cltools)
-
-	return xcode_path
-
-
-    set_config('XCODE_PATH', xcode_path)
-
-
-# Compiler wrappers
-# ==============================================================
-# Normally, we'd use js_option and automatically have those variables
-# propagated to js/src, but things are complicated by possible additional
-# wrappers in CC/CXX, and by other subconfigures that do not handle those
-# options and do need CC/CXX altered.
-option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
-       help='Enable compiling with wrappers such as distcc and ccache')
-
-js_option('--with-ccache', env='CCACHE', nargs='?',
-          help='Enable compiling with ccache')
-
-
-@depends_if('--with-ccache')
-def ccache(value):
-    if len(value):
-        return value
-    # If --with-ccache was given without an explicit value, we default to
-    # 'ccache'.
-    return 'ccache'
-
-
-ccache = check_prog('CCACHE', progs=(), input=ccache)
-
-js_option(env='CCACHE_PREFIX',
-          nargs=1,
-          help='Compiler prefix to use when using ccache')
-
-ccache_prefix = depends_if('CCACHE_PREFIX')(lambda prefix: prefix[0])
-set_config('CCACHE_PREFIX', ccache_prefix)
-
-# Distinguish ccache from sccache.
-
-
-@depends_if(ccache)
-def ccache_is_sccache(ccache):
-    return check_cmd_output(ccache, '--version').startswith('sccache')
-
-
-@depends(ccache, ccache_is_sccache)
-def using_ccache(ccache, ccache_is_sccache):
-    return ccache and not ccache_is_sccache
-
-
-@depends_if(ccache, ccache_is_sccache)
-def using_sccache(ccache, ccache_is_sccache):
-    return ccache and ccache_is_sccache
-
-
-set_config('MOZ_USING_CCACHE', using_ccache)
-set_config('MOZ_USING_SCCACHE', using_sccache)
-
-option(env='SCCACHE_VERBOSE_STATS',
-       help='Print verbose sccache stats after build')
-
-
-@depends(using_sccache, 'SCCACHE_VERBOSE_STATS')
-def sccache_verbose_stats(using_sccache, verbose_stats):
-    return using_sccache and bool(verbose_stats)
-
-
-set_config('SCCACHE_VERBOSE_STATS', sccache_verbose_stats)
-
-
-@depends('--with-compiler-wrapper', ccache)
-@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
-def compiler_wrapper(wrapper, ccache):
-    if wrapper:
-        raw_wrapper = wrapper[0]
-        wrapper = shell_split(raw_wrapper)
-        wrapper_program = find_program(wrapper[0])
-        if not wrapper_program:
-            die('Cannot find `%s` from the given compiler wrapper `%s`',
-                wrapper[0], raw_wrapper)
-        wrapper[0] = wrapper_program
-
-    if ccache:
-        if wrapper:
-            return tuple([ccache] + wrapper)
-        else:
-            return (ccache,)
-    elif wrapper:
-        return tuple(wrapper)
-
-
-@depends_if(compiler_wrapper)
-def using_compiler_wrapper(compiler_wrapper):
-    return True
-
-
-set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
-
-
-# GC rooting and hazard analysis.
-# ==============================================================
-option(env='MOZ_HAZARD', help='Build for the GC rooting hazard analysis')
-
-
-@depends('MOZ_HAZARD')
-def hazard_analysis(value):
-    if value:
-        return True
-
-
-set_config('MOZ_HAZARD', hazard_analysis)
-
-
-# Cross-compilation related things.
-# ==============================================================
-js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1,
-          help='Prefix for the target toolchain')
-
-
-@depends('--with-toolchain-prefix', target, cross_compiling)
-def toolchain_prefix(value, target, cross_compiling):
-    if value:
-        return tuple(value)
-    if cross_compiling:
-        return ('%s-' % target.toolchain, '%s-' % target.alias)
-
-
-@depends(toolchain_prefix, target)
-def first_toolchain_prefix(toolchain_prefix, target):
-    # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the
-    # command line/environment (in which case there's only one value in the tuple),
-    # or when cross-compiling for Android or OSX.
-    if toolchain_prefix and (target.os in ('Android', 'OSX') or len(toolchain_prefix) == 1):
-        return toolchain_prefix[0]
-
-
-set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix)
-add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix)
-
-
-# Compilers
+# PGO
 # ==============================================================
-include('compilers-util.configure')
-
-
-def try_preprocess(compiler, language, source):
-    return try_invoke_compiler(compiler, language, source, ['-E'])
-
-
-@imports(_from='mozbuild.configure.constants', _import='CompilerType')
-@imports(_from='mozbuild.configure.constants',
-         _import='CPU_preprocessor_checks')
-@imports(_from='mozbuild.configure.constants',
-         _import='kernel_preprocessor_checks')
-@imports(_from='mozbuild.configure.constants',
-         _import='OS_preprocessor_checks')
-@imports(_from='textwrap', _import='dedent')
-@imports(_from='__builtin__', _import='Exception')
-def get_compiler_info(compiler, language):
-    '''Returns information about the given `compiler` (command line in the
-    form of a list or tuple), in the given `language`.
-
-    The returned information includes:
-    - the compiler type (clang-cl, clang or gcc)
-    - the compiler version
-    - the compiler supported language
-    - the compiler supported language version
-    '''
-    # Note: We'd normally do a version check for clang, but versions of clang
-    # in Xcode have a completely different versioning scheme despite exposing
-    # the version with the same defines.
-    # So instead, we make things such that the version is missing when the
-    # clang used is below the minimum supported version (currently clang 4.0,
-    # or 5.0 on mac).
-    # We then only include the version information when the compiler matches
-    # the feature check, so that an unsupported version of clang would have
-    # no version number.
-    check = dedent('''\
-        #if defined(_MSC_VER) && defined(__clang__) && defined(_MT)
-        %COMPILER "clang-cl"
-        %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
-        #elif defined(__clang__)
-        %COMPILER "clang"
-        #  if defined(__APPLE__)
-        #    if __has_warning("-Wunguarded-availability")
-        %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
-        #    endif
-        #  elif __has_attribute(diagnose_if)
-        %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__
-        #  endif
-        #elif defined(__GNUC__)
-        %COMPILER "gcc"
-        %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
-        #endif
-
-        #if __cplusplus
-        %cplusplus __cplusplus
-        #elif __STDC_VERSION__
-        %STDC_VERSION __STDC_VERSION__
-        #endif
-    ''')
-
-    # While we're doing some preprocessing, we might as well do some more
-    # preprocessor-based tests at the same time, to check the toolchain
-    # matches what we want.
-    for name, preprocessor_checks in (
-        ('CPU', CPU_preprocessor_checks),
-        ('KERNEL', kernel_preprocessor_checks),
-        ('OS', OS_preprocessor_checks),
-    ):
-        for n, (value, condition) in enumerate(preprocessor_checks.iteritems()):
-            check += dedent('''\
-                #%(if)s %(condition)s
-                %%%(name)s "%(value)s"
-            ''' % {
-                'if': 'elif' if n else 'if',
-                'condition': condition,
-                'name': name,
-                'value': value,
-            })
-        check += '#endif\n'
-
-    # Also check for endianness. The advantage of living in modern times is
-    # that all the modern compilers we support now have __BYTE_ORDER__ defined
-    # by the preprocessor.
-    check += dedent('''\
-        #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-        %ENDIANNESS "little"
-        #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-        %ENDIANNESS "big"
-        #endif
-    ''')
-
-    result = try_preprocess(compiler, language, check)
-
-    if not result:
-        raise FatalCheckError(
-            'Unknown compiler or compiler not supported.')
-
-    # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may
-    # have non-ASCII characters. Treat the output as bytearray.
-    data = {}
-    for line in result.splitlines():
-        if line.startswith(b'%'):
-            k, _, v = line.partition(' ')
-            k = k.lstrip('%')
-            data[k] = v.replace(' ', '').lstrip('"').rstrip('"')
-            log.debug('%s = %s', k, data[k])
-
-    try:
-        type = CompilerType(data['COMPILER'])
-    except Exception:
-        raise FatalCheckError(
-            'Unknown compiler or compiler not supported.')
-
-    cplusplus = int(data.get('cplusplus', '0L').rstrip('L'))
-    stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L'))
-
-    version = data.get('VERSION')
-    if version:
-        version = Version(version)
-
-    return namespace(
-        type=type,
-        version=version,
-        cpu=data.get('CPU'),
-        kernel=data.get('KERNEL'),
-        endianness=data.get('ENDIANNESS'),
-        os=data.get('OS'),
-        language='C++' if cplusplus else 'C',
-        language_version=cplusplus if cplusplus else stdc_version,
-    )
-
-
-def same_arch_different_bits():
-    return (
-        ('x86', 'x86_64'),
-        ('ppc', 'ppc64'),
-        ('sparc', 'sparc64'),
-    )
-
-
-@imports(_from='mozbuild.shellutil', _import='quote')
-@imports(_from='mozbuild.configure.constants',
-         _import='OS_preprocessor_checks')
-def check_compiler(compiler, language, target):
-    info = get_compiler_info(compiler, language)
-
-    flags = []
-
-    # Check language standards
-    # --------------------------------------------------------------------
-    if language != info.language:
-        raise FatalCheckError(
-            '`%s` is not a %s compiler.' % (quote(*compiler), language))
-
-    # Note: We do a strict version check because there sometimes are backwards
-    # incompatible changes in the standard, and not all code that compiles as
-    # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for
-    # example)
-    if info.language == 'C' and info.language_version != 199901:
-        if info.type == 'clang-cl':
-            flags.append('-Xclang')
-        flags.append('-std=gnu99')
-
-    # Note: this is a strict version check because we used to always add
-    # -std=gnu++14.
-    cxx14_version = 201402
-    if info.language == 'C++':
-        if info.language_version != cxx14_version:
-            # MSVC headers include C++14 features, but don't guard them
-            # with appropriate checks.
-            if info.type == 'clang-cl':
-                flags.append('-Xclang')
-                flags.append('-std=c++14')
-            else:
-                flags.append('-std=gnu++14')
-
-    # Check compiler target
-    # --------------------------------------------------------------------
-    has_target = False
-    if info.type == 'clang':
-        if not info.kernel or info.kernel != target.kernel or \
-                not info.endianness or info.endianness != target.endianness:
-            flags.append('--target=%s' % target.toolchain)
-            has_target = True
-
-        # Add target flag when there is an OS mismatch (e.g. building for Android on
-        # Linux). However, only do this if the target OS is in our whitelist, to
-        # keep things the same on other platforms.
-        elif target.os in OS_preprocessor_checks and (
-                not info.os or info.os != target.os):
-            flags.append('--target=%s' % target.toolchain)
-            has_target = True
-
-    if not has_target and (not info.cpu or info.cpu != target.cpu):
-        same_arch = same_arch_different_bits()
-        if (target.cpu, info.cpu) in same_arch:
-            flags.append('-m32')
-        elif (info.cpu, target.cpu) in same_arch:
-            flags.append('-m64')
-        elif info.type == 'clang-cl' and target.cpu == 'aarch64':
-            flags.append('--target=%s' % target.toolchain)
-        elif info.type == 'clang':
-            flags.append('--target=%s' % target.toolchain)
-
-    return namespace(
-        type=info.type,
-        version=info.version,
-        target_cpu=info.cpu,
-        target_kernel=info.kernel,
-        target_endianness=info.endianness,
-        target_os=info.os,
-        flags=flags,
-    )
-
-
-@imports(_from='__builtin__', _import='open')
-@imports('json')
-@imports('os')
-@imports('subprocess')
-@imports('sys')
-def get_vc_paths(topsrcdir):
-    def vswhere(args):
-        encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8'
-        program_files = (os.environ.get('PROGRAMFILES(X86)') or
-                         os.environ.get('PROGRAMFILES'))
-        if not program_files:
-            return []
-        vswhere = os.path.join(program_files, 'Microsoft Visual Studio',
-                               'Installer', 'vswhere.exe')
-        if not os.path.exists(vswhere):
-            return []
-        return json.loads(
-            subprocess.check_output([vswhere, '-format', 'json'] + args)
-            .decode(encoding, 'replace'))
-
-    for install in vswhere(['-products', '*', '-requires', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64']):
-        path = install['installationPath']
-        tools_version = open(os.path.join(
-            path, r'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt'), 'rb').read().strip()
-        tools_path = os.path.join(
-            path, r'VC\Tools\MSVC', tools_version)
-        yield (Version(install['installationVersion']), tools_path)
-
-
-@depends(host)
-def host_is_windows(host):
-    if host.kernel == 'WINNT':
-        return True
-
-
-js_option('--with-visual-studio-version', nargs=1,
-          choices=('2017',), when=host_is_windows,
-          help='Select a specific Visual Studio version to use')
-
-
-@depends('--with-visual-studio-version', when=host_is_windows)
-def vs_major_version(value):
-    if value:
-        return {'2017': 15}[value[0]]
-
-
-js_option(env='VC_PATH', nargs=1, when=host_is_windows,
-          help='Path to the Microsoft Visual C/C++ compiler')
-
-
-@depends(host, vs_major_version, check_build_environment, 'VC_PATH',
-         '--with-visual-studio-version', when=host_is_windows)
-@imports(_from='__builtin__', _import='sorted')
-@imports(_from='operator', _import='itemgetter')
-def vc_compiler_paths_for_version(host, vs_major_version, env, vc_path, vs_release_name):
-    if vc_path and vs_release_name:
-        die('VC_PATH and --with-visual-studio-version cannot be used together.')
-    if vc_path:
-        # Use an arbitrary version, it doesn't matter.
-        all_versions = [(Version('15'), vc_path[0])]
-    else:
-        all_versions = sorted(get_vc_paths(env.topsrcdir), key=itemgetter(0))
-    if not all_versions:
-        return
-    if vs_major_version:
-        versions = [d for (v, d) in all_versions if v.major ==
-                    vs_major_version]
-        if not versions:
-            die('Visual Studio %s could not be found!' % vs_release_name)
-        path = versions[0]
-    else:
-        # Choose the newest version.
-        path = all_versions[-1][1]
-    host_dir = {
-        'x86_64': 'HostX64',
-        'x86': 'HostX86',
-    }.get(host.cpu)
-    if host_dir:
-        path = os.path.join(path, 'bin', host_dir)
-        return {
-            'x64': [os.path.join(path, 'x64')],
-            # The cross toolchains require DLLs from the native x64 toolchain.
-            'x86': [os.path.join(path, 'x86'), os.path.join(path, 'x64')],
-            'arm64': [os.path.join(path, 'arm64'), os.path.join(path, 'x64')],
-        }
-
-
-@template
-def vc_compiler_path_for(host_or_target):
-    @depends(host_or_target, vc_compiler_paths_for_version,
-             when=host_is_windows)
-    def vc_compiler_path(target, paths):
-        vc_target = {
-            'x86': 'x86',
-            'x86_64': 'x64',
-            'arm': 'arm',
-            'aarch64': 'arm64'
-        }.get(target.cpu)
-        if not paths:
-            return
-        return paths.get(vc_target)
-    return vc_compiler_path
-
-
-vc_compiler_path = vc_compiler_path_for(target)
-host_vc_compiler_path = vc_compiler_path_for(host)
-
-
-@dependable
-@imports('os')
-@imports(_from='os', _import='environ')
-def original_path():
-    return environ['PATH'].split(os.pathsep)
-
-
-@template
-def toolchain_search_path_for(host_or_target):
-    vc_path = {
-        host: host_vc_compiler_path,
-        target: vc_compiler_path,
-    }[host_or_target]
-
-    @depends(vc_path, original_path, developer_options, mozbuild_state_path)
-    @imports('os')
-    @imports(_from='os', _import='environ')
-    def toolchain_search_path(vc_compiler_path, original_path, developer_options,
-                              mozbuild_state_path):
-        result = list(original_path)
-
-        if vc_compiler_path:
-            # The second item, if there is one, is necessary to have in $PATH for
-            # Windows to load the required DLLs from there.
-            if len(vc_compiler_path) > 1:
-                environ['PATH'] = os.pathsep.join(result + vc_compiler_path[1:])
-
-            # The first item is where the programs are going to be
-            result.append(vc_compiler_path[0])
-
-        # Also add in the location to which `mach bootstrap` or
-        # `mach artifact toolchain` installs clang.
-        bootstrapped = []
-
-        bootstrap_clang_path = os.path.join(mozbuild_state_path, 'clang', 'bin')
-        bootstrapped.append(bootstrap_clang_path)
-
-        bootstrap_cbindgen_path = os.path.join(mozbuild_state_path, 'cbindgen')
-        bootstrapped.append(bootstrap_cbindgen_path)
-
-        bootstrap_nasm_path = os.path.join(mozbuild_state_path, 'nasm')
-        bootstrapped.append(bootstrap_nasm_path)
-
-        # Also add the rustup install directory for cargo/rustc.
-        rustup_path = os.path.expanduser(os.path.join('~', '.cargo', 'bin'))
-        result.append(rustup_path)
-
-        if developer_options:
-            return bootstrapped + result
-        return result + bootstrapped
-    return toolchain_search_path
-
-
-toolchain_search_path = toolchain_search_path_for(target)
-host_toolchain_search_path = toolchain_search_path_for(host)
-
-
-# As a workaround until bug 1516228 and bug 1516253 are fixed, set the PATH
-# variable for the build to contain the toolchain search path.
-@depends(toolchain_search_path, host_toolchain_search_path)
-@imports('os')
-@imports(_from='os', _import='environ')
-def altered_path(toolchain_search_path, host_toolchain_search_path):
-    path = environ['PATH'].split(os.pathsep)
-    altered_path = list(toolchain_search_path)
-    for p in host_toolchain_search_path:
-        if p not in altered_path:
-            altered_path.append(p)
-    for p in path:
-        if p not in altered_path:
-            altered_path.append(p)
-    return os.pathsep.join(altered_path)
-
-
-set_config('PATH', altered_path)
-
-
-@template
-def default_c_compilers(host_or_target, other_c_compiler=None):
-    '''Template defining the set of default C compilers for the host and
-    target platforms.
-    `host_or_target` is either `host` or `target` (the @depends functions
-    from init.configure.
-    `other_c_compiler` is the `target` C compiler when `host_or_target` is `host`.
-    '''
-    assert host_or_target in {host, target}
-
-    other_c_compiler = () if other_c_compiler is None else (other_c_compiler,)
-
-    @depends(host_or_target, target, toolchain_prefix, *other_c_compiler)
-    def default_c_compilers(host_or_target, target, toolchain_prefix,
-                            *other_c_compiler):
-        if host_or_target.kernel == 'WINNT':
-            supported = types = ('clang-cl', 'gcc', 'clang')
-        elif host_or_target.kernel == 'Darwin':
-            types = ('clang',)
-            supported = ('clang', 'gcc')
-        else:
-            supported = types = ('clang', 'gcc')
-
-        info = other_c_compiler[0] if other_c_compiler else None
-        if info and info.type in supported:
-            # When getting default C compilers for the host, we prioritize the
-            # same compiler as the target C compiler.
-            prioritized = info.compiler
-            if info.type == 'gcc':
-                same_arch = same_arch_different_bits()
-                if (target.cpu != host_or_target.cpu and
-                        (target.cpu, host_or_target.cpu) not in same_arch and
-                        (host_or_target.cpu, target.cpu) not in same_arch):
-                    # If the target C compiler is GCC, and it can't be used with
-                    # -m32/-m64 for the host, it's probably toolchain-prefixed,
-                    # so we prioritize a raw 'gcc' instead.
-                    prioritized = info.type
-
-            types = [prioritized] + [t for t in types if t != info.type]
-
-        gcc = ('gcc',)
-        if toolchain_prefix and host_or_target is target:
-            gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc
-
-        result = []
-        for type in types:
-            if type == 'gcc':
-                result.extend(gcc)
-            else:
-                result.append(type)
-
-        return tuple(result)
-
-    return default_c_compilers
-
-
-@template
-def default_cxx_compilers(c_compiler, other_c_compiler=None, other_cxx_compiler=None):
-    '''Template defining the set of default C++ compilers for the host and
-    target platforms.
-    `c_compiler` is the @depends function returning a Compiler instance for
-    the desired platform.
-
-    Because the build system expects the C and C++ compilers to be from the
-    same compiler suite, we derive the default C++ compilers from the C
-    compiler that was found if none was provided.
-
-    We also factor in the target C++ compiler when getting the default host
-    C++ compiler, using the target C++ compiler if the host and target C
-    compilers are the same.
-    '''
-
-    assert (other_c_compiler is None) == (other_cxx_compiler is None)
-    if other_c_compiler is not None:
-        other_compilers = (other_c_compiler, other_cxx_compiler)
-    else:
-        other_compilers = ()
-
-    @depends(c_compiler, *other_compilers)
-    def default_cxx_compilers(c_compiler, *other_compilers):
-        if other_compilers:
-            other_c_compiler, other_cxx_compiler = other_compilers
-            if other_c_compiler.compiler == c_compiler.compiler:
-                return (other_cxx_compiler.compiler,)
-
-        dir = os.path.dirname(c_compiler.compiler)
-        file = os.path.basename(c_compiler.compiler)
-
-        if c_compiler.type == 'gcc':
-            return (os.path.join(dir, file.replace('gcc', 'g++')),)
-
-        if c_compiler.type == 'clang':
-            return (os.path.join(dir, file.replace('clang', 'clang++')),)
-
-        return (c_compiler.compiler,)
-
-    return default_cxx_compilers
-
-
-@template
-def provided_program(env_var):
-    '''Template handling cases where a program can be specified either as a
-    path or as a path with applicable arguments.
-    '''
-
-    @depends_if(env_var)
-    @imports(_from='itertools', _import='takewhile')
-    @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
-    def provided(cmd):
-        # Assume the first dash-prefixed item (and any subsequent items) are
-        # command-line options, the item before the dash-prefixed item is
-        # the program we're looking for, and anything before that is a wrapper
-        # of some kind (e.g. sccache).
-        cmd = shell_split(cmd[0])
-
-        without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
-
-        return namespace(
-            wrapper=without_flags[:-1],
-            program=without_flags[-1],
-            flags=cmd[len(without_flags):],
-        )
-
-    return provided
-
-
-def prepare_flags(host_or_target, macos_sdk):
-    if macos_sdk and host_or_target.os == 'OSX':
-        return ['-isysroot', macos_sdk]
-    return []
-
-
-@template
-def compiler(language, host_or_target, c_compiler=None, other_compiler=None,
-             other_c_compiler=None):
-    '''Template handling the generic base checks for the compiler for the
-    given `language` on the given platform (`host_or_target`).
-    `host_or_target` is either `host` or `target` (the @depends functions
-    from init.configure.
-    When the language is 'C++', `c_compiler` is the result of the `compiler`
-    template for the language 'C' for the same `host_or_target`.
-    When `host_or_target` is `host`, `other_compiler` is the result of the
-    `compiler` template for the same `language` for `target`.
-    When `host_or_target` is `host` and the language is 'C++',
-    `other_c_compiler` is the result of the `compiler` template for the
-    language 'C' for `target`.
-    '''
-    assert host_or_target in {host, target}
-    assert language in ('C', 'C++')
-    assert language == 'C' or c_compiler is not None
-    assert host_or_target is target or other_compiler is not None
-    assert language == 'C' or host_or_target is target or \
-        other_c_compiler is not None
-
-    host_or_target_str = {
-        host: 'host',
-        target: 'target',
-    }[host_or_target]
-
-    var = {
-        ('C', target): 'CC',
-        ('C++', target): 'CXX',
-        ('C', host): 'HOST_CC',
-        ('C++', host): 'HOST_CXX',
-    }[language, host_or_target]
-
-    default_compilers = {
-        'C': lambda: default_c_compilers(host_or_target, other_compiler),
-        'C++': lambda: default_cxx_compilers(c_compiler, other_c_compiler, other_compiler),
-    }[language]()
-
-    what = 'the %s %s compiler' % (host_or_target_str, language)
-
-    option(env=var, nargs=1, help='Path to %s' % what)
-
-    # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
-    # HOST_CXX variables.
-    provided_compiler = provided_program(var)
-
-    search_path = {
-        host: host_toolchain_search_path,
-        target: toolchain_search_path,
-    }[host_or_target]
-
-    # Normally, we'd use `var` instead of `_var`, but the interaction with
-    # old-configure complicates things, and for now, we a) can't take the plain
-    # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
-    # old-configure AC_SUBST it (because it's autoconf doing it, not us)
-    compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
-                          input=provided_compiler.program,
-                          paths=search_path)
-
-    @depends(compiler, provided_compiler, compiler_wrapper, host_or_target, macos_sdk)
-    @checking('whether %s can be used' % what, lambda x: bool(x))
-    @imports(_from='mozbuild.shellutil', _import='quote')
-    def valid_compiler(compiler, provided_compiler, compiler_wrapper,
-                       host_or_target, macos_sdk):
-        wrapper = list(compiler_wrapper or ())
-        if provided_compiler:
-            provided_wrapper = list(provided_compiler.wrapper)
-            # When doing a subconfigure, the compiler is set by old-configure
-            # and it contains the wrappers from --with-compiler-wrapper and
-            # --with-ccache.
-            if provided_wrapper[:len(wrapper)] == wrapper:
-                provided_wrapper = provided_wrapper[len(wrapper):]
-            wrapper.extend(provided_wrapper)
-            flags = provided_compiler.flags
-        else:
-            flags = []
-
-        if not flags:
-            flags = prepare_flags(host_or_target, macos_sdk)
-
-        info = check_compiler(wrapper + [compiler] + flags, language,
-                              host_or_target)
-
-        # Check that the additional flags we got are enough to not require any
-        # more flags. If we get an exception, just ignore it; it's liable to be
-        # invalid command-line flags, which means the compiler we're checking
-        # doesn't support those command-line flags and will fail one or more of
-        # the checks below.
-        try:
-            if info.flags:
-                flags += info.flags
-                info = check_compiler(wrapper + [compiler] + flags, language,
-                                      host_or_target)
-        except FatalCheckError:
-            pass
-
-        if not info.target_cpu or info.target_cpu != host_or_target.cpu:
-            raise FatalCheckError(
-                '%s %s compiler target CPU (%s) does not match --%s CPU (%s)'
-                % (host_or_target_str.capitalize(), language,
-                   info.target_cpu or 'unknown', host_or_target_str,
-                   host_or_target.raw_cpu))
-
-        if not info.target_kernel or (info.target_kernel !=
-                                      host_or_target.kernel):
-            raise FatalCheckError(
-                '%s %s compiler target kernel (%s) does not match --%s kernel (%s)'
-                % (host_or_target_str.capitalize(), language,
-                   info.target_kernel or 'unknown', host_or_target_str,
-                   host_or_target.kernel))
-
-        if not info.target_endianness or (info.target_endianness !=
-                                          host_or_target.endianness):
-            raise FatalCheckError(
-                '%s %s compiler target endianness (%s) does not match --%s '
-                'endianness (%s)'
-                % (host_or_target_str.capitalize(), language,
-                   info.target_endianness or 'unknown', host_or_target_str,
-                   host_or_target.endianness))
-
-        # Compiler version checks
-        # ===================================================
-        # Check the compiler version here instead of in `compiler_version` so
-        # that the `checking` message doesn't pretend the compiler can be used
-        # to then bail out one line later.
-        if info.type == 'gcc':
-            if host_or_target.os == 'Android':
-                raise FatalCheckError('GCC is not supported on Android.\n'
-                                      'Please use clang from the Android NDK instead.')
-            if info.version < '6.1.0':
-                raise FatalCheckError(
-                    'Only GCC 6.1 or newer is supported (found version %s).'
-                    % info.version)
-
-        if info.type == 'clang-cl':
-            if info.version < '8.0.0':
-                raise FatalCheckError(
-                    'Only clang-cl 8.0 or newer is supported (found version %s)'
-                    % info.version)
-
-        # If you want to bump the version check here search for
-        # diagnose_if above, and see the associated comment.
-        if info.type == 'clang' and not info.version:
-            if host_or_target.os == 'OSX':
-                raise FatalCheckError(
-                    'Only clang/llvm 5.0 or newer is supported.')
-            raise FatalCheckError(
-                'Only clang/llvm 4.0 or newer is supported.')
-
-        if info.flags:
-            raise FatalCheckError(
-                'Unknown compiler or compiler not supported.')
-
-        return namespace(
-            wrapper=wrapper,
-            compiler=compiler,
-            flags=flags,
-            type=info.type,
-            version=info.version,
-            language=language,
-        )
-
-    @depends(valid_compiler)
-    @checking('%s version' % what)
-    def compiler_version(compiler):
-        return compiler.version
-
-    if language == 'C++':
-        @depends(valid_compiler, c_compiler)
-        def valid_compiler(compiler, c_compiler):
-            if compiler.type != c_compiler.type:
-                die('The %s C compiler is %s, while the %s C++ compiler is '
-                    '%s. Need to use the same compiler suite.',
-                    host_or_target_str, c_compiler.type,
-                    host_or_target_str, compiler.type)
-
-            if compiler.version != c_compiler.version:
-                die('The %s C compiler is version %s, while the %s C++ '
-                    'compiler is version %s. Need to use the same compiler '
-                    'version.',
-                    host_or_target_str, c_compiler.version,
-                    host_or_target_str, compiler.version)
-            return compiler
-
-    # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
-    # and the flags that were part of the user input for those variables to
-    # be provided.
-    add_old_configure_assignment(var, depends_if(valid_compiler)(
-        lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
-
-    if host_or_target is target:
-        add_old_configure_assignment('ac_cv_prog_%s' % var, depends_if(valid_compiler)(
-            lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
-        # We check that it works in python configure already.
-        add_old_configure_assignment('ac_cv_prog_%s_works' % var.lower(), 'yes')
-        add_old_configure_assignment(
-            'ac_cv_prog_%s_cross' % var.lower(),
-            depends(cross_compiling)(lambda x: 'yes' if x else 'no'))
-        gcc_like = depends(valid_compiler.type)(lambda x: 'yes' if x in ('gcc', 'clang') else 'no')
-        add_old_configure_assignment('ac_cv_prog_%s_g' % var.lower(), gcc_like)
-        if language == 'C':
-            add_old_configure_assignment('ac_cv_prog_gcc', gcc_like)
-        if language == 'C++':
-            add_old_configure_assignment('ac_cv_prog_gxx', gcc_like)
-
-
-    # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
-    # old-configure to do some of its still existing checks.
-    if language == 'C':
-        set_config(
-            '%s_TYPE' % var, valid_compiler.type)
-        add_old_configure_assignment(
-            '%s_TYPE' % var, valid_compiler.type)
-        set_config(
-            '%s_VERSION' % var, depends(valid_compiler.version)(lambda v: str(v)))
-
-    valid_compiler = compiler_class(valid_compiler, host_or_target)
-
-    def compiler_error():
-        raise FatalCheckError('Failed compiling a simple %s source with %s'
-                              % (language, what))
-
-    valid_compiler.try_compile(check_msg='%s works' % what,
-                               onerror=compiler_error)
-
-    set_config('%s_BASE_FLAGS' % var, valid_compiler.flags)
-
-    # Set CPP/CXXCPP for both the build system and old-configure. We don't
-    # need to check this works for preprocessing, because we already relied
-    # on $CC -E/$CXX -E doing preprocessing work to validate the compiler
-    # in the first place.
-    if host_or_target is target:
-        pp_var = {
-            'C': 'CPP',
-            'C++': 'CXXCPP',
-        }[language]
-
-        preprocessor = depends_if(valid_compiler)(
-            lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags))
-
-        set_config(pp_var, preprocessor)
-        add_old_configure_assignment(pp_var, preprocessor)
-
-    if language == 'C':
-        linker_var = {
-            target: 'LD',
-            host: 'HOST_LD',
-        }[host_or_target]
-
-        @deprecated_option(env=linker_var, nargs=1)
-        def linker(value):
-            if value:
-                return value[0]
-
-        @depends(linker)
-        def unused_linker(linker):
-            if linker:
-                log.warning('The value of %s is not used by this build system.'
-                            % linker_var)
-
-    return valid_compiler
-
-
-c_compiler = compiler('C', target)
-cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
-host_c_compiler = compiler('C', host, other_compiler=c_compiler)
-host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
-                             other_compiler=cxx_compiler,
-                             other_c_compiler=c_compiler)
-
-# Generic compiler-based conditions.
-building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc')
-
-
-@depends(cxx_compiler, ccache_prefix)
-@imports('os')
-def cxx_is_icecream(info, ccache_prefix):
-    if (os.path.islink(info.compiler) and os.path.basename(
-            os.readlink(info.compiler)) == 'icecc'):
-        return True
-    if ccache_prefix and os.path.basename(ccache_prefix) == 'icecc':
-        return True
-
-set_config('CXX_IS_ICECREAM', cxx_is_icecream)
-
-
-@depends(c_compiler)
-def msvs_version(info):
-    # clang-cl emulates the same version scheme as cl. And MSVS_VERSION needs to
-    # be set for GYP on Windows.
-    if info.type == 'clang-cl':
-        return '2017'
-
-    return ''
-
-
-set_config('MSVS_VERSION', msvs_version)
-
-include('compile-checks.configure')
-include('arm.configure', when=depends(target.cpu)(lambda cpu: cpu == 'arm'))
-
-
-@depends(host,
-         host_os_kernel_major_version,
-         target,
-         cxx_compiler.try_run(header='#include_next <inttypes.h>'))
-def check_have_mac_10_14_sdk(host, version, target, value):
-    # Only an issue on Mac OS X 10.14 (and probably above).
-    if host.kernel != 'Darwin' or target.kernel !='Darwin' or version < '18' or value:
-        return
-
-    die('System inttypes.h not found. Please try running '
-         '`open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg` '
-         'and following the instructions to install the necessary headers')
-
-
-@depends(have_64_bit,
-         try_compile(body='static_assert(sizeof(void *) == 8, "")',
-                     check_msg='for 64-bit OS'))
-def check_have_64_bit(have_64_bit, compiler_have_64_bit):
-    if have_64_bit != compiler_have_64_bit:
-        configure_error('The target compiler does not agree with configure '
-                        'about the target bitness.')
-
-
-@depends(c_compiler, target)
-def default_debug_flags(compiler_info, target):
-    # Debug info is ON by default.
-    if compiler_info.type == 'clang-cl':
-        return '-Z7'
-    elif target.kernel == 'WINNT' and compiler_info.type == 'clang':
-        return '-g -gcodeview'
-    return '-g'
-
-
-option(env='MOZ_DEBUG_FLAGS',
-       nargs=1,
-       help='Debug compiler flags')
-
-imply_option('--enable-debug-symbols',
-             depends_if('--enable-debug')(lambda v: v))
-
-js_option('--disable-debug-symbols',
-          nargs='?',
-          help='Disable debug symbols using the given compiler flags')
-
-set_config('MOZ_DEBUG_SYMBOLS',
-           depends_if('--enable-debug-symbols')(lambda _: True))
-
-
-@depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags)
-def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags):
-    # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value,
-    # --enable-debug-symbols takes precedence. Note, the value of
-    # --enable-debug-symbols may be implied by --enable-debug.
-    if len(enable_debug_flags):
-        return enable_debug_flags[0]
-    if env_debug_flags:
-        return env_debug_flags[0]
-    return default_debug_flags
-
-
-set_config('MOZ_DEBUG_FLAGS', debug_flags)
-add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags)
-
-
-@depends(c_compiler)
-def color_cflags(info):
-    # We could test compiling with flags. By why incur the overhead when
-    # color support should always be present in a specific toolchain
-    # version?
-
-    # Code for auto-adding this flag to compiler invocations needs to
-    # determine if an existing flag isn't already present. That is likely
-    # using exact string matching on the returned value. So if the return
-    # value changes to e.g. "<x>=always", exact string match may fail and
-    # multiple color flags could be added. So examine downstream consumers
-    # before adding flags to return values.
-    if info.type == 'gcc':
-        return '-fdiagnostics-color'
-    elif info.type == 'clang':
-        return '-fcolor-diagnostics'
-    else:
-        return ''
-
-
-set_config('COLOR_CFLAGS', color_cflags)
-
-# Some standard library headers (notably bionic on Android) declare standard
-# functions (e.g. getchar()) and also #define macros for those standard
-# functions.  libc++ deals with this by doing something like the following
-# (explanatory comments added):
-#
-#   #ifdef FUNC
-#   // Capture the definition of FUNC.
-#   inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); }
-#   #undef FUNC
-#   // Use a real inline definition.
-#   inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); }
-#   #endif
-#
-# _LIBCPP_INLINE_VISIBILITY is typically defined as:
-#
-#   __attribute__((__visibility__("hidden"), __always_inline__))
-#
-# Unfortunately, this interacts badly with our system header wrappers, as the:
-#
-#   #pragma GCC visibility push(default)
-#
-# that they do prior to including the actual system header is treated by the
-# compiler as an explicit declaration of visibility on every function declared
-# in the header.  Therefore, when the libc++ code above is encountered, it is
-# as though the compiler has effectively seen:
-#
-#   int FUNC(...) __attribute__((__visibility__("default")));
-#   int FUNC(...) __attribute__((__visibility__("hidden")));
-#
-# and the compiler complains about the mismatched visibility declarations.
-#
-# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no
-# existing definition.  We can therefore define it to the empty string (since
-# we are properly managing visibility ourselves) and avoid this whole mess.
-# Note that we don't need to do this with gcc, as libc++ detects gcc and
-# effectively does the same thing we are doing here.
-#
-# _LIBCPP_ALWAYS_INLINE needs similar workarounds, since it too declares
-# hidden visibility.
-
-
-@depends(c_compiler, target)
-def libcxx_override_visibility(c_compiler, target):
-    if c_compiler.type == 'clang' and target.os == 'Android':
-        return ''
-
-
-set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_override_visibility)
-set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49',
-           libcxx_override_visibility)
-set_define('_LIBCPP_ALWAYS_INLINE', libcxx_override_visibility)
-set_define('_LIBCPP_ALWAYS_INLINE_EXCEPT_GCC49', libcxx_override_visibility)
-
-
-@depends(target, check_build_environment)
-def visibility_flags(target, env):
-    if target.os != 'WINNT':
-        if target.kernel == 'Darwin':
-            return ('-fvisibility=hidden', '-fvisibility-inlines-hidden')
-        return ('-I%s/system_wrappers' % os.path.join(env.dist),
-                '-include',
-                '%s/config/gcc_hidden.h' % env.topsrcdir)
-
-
-@depends(target, visibility_flags)
-def wrap_system_includes(target, visibility_flags):
-    if visibility_flags and target.kernel != 'Darwin':
-        return True
-
-
-set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE',
-           depends(visibility_flags)(lambda v: bool(v) or None))
-set_define('HAVE_VISIBILITY_ATTRIBUTE',
-           depends(visibility_flags)(lambda v: bool(v) or None))
-set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes)
-set_config('VISIBILITY_FLAGS', visibility_flags)
-
-
-@template
-def depend_cflags(host_or_target_c_compiler):
-    @depends(host_or_target_c_compiler)
-    def depend_cflags(host_or_target_c_compiler):
-        if host_or_target_c_compiler.type != 'clang-cl':
-            return ['-MD', '-MP', '-MF $(MDDEPDIR)/$(@F).pp']
-        else:
-            # clang-cl doesn't accept the normal -MD -MP -MF options that clang
-            # does, but the underlying cc1 binary understands how to generate
-            # dependency files.  These options are based on analyzing what the
-            # normal clang driver sends to cc1 when given the "correct"
-            # dependency options.
-            return [
-                '-Xclang', '-MP',
-                '-Xclang', '-dependency-file',
-                '-Xclang', '$(MDDEPDIR)/$(@F).pp',
-                '-Xclang', '-MT',
-                '-Xclang', '$@'
-            ]
-
-    return depend_cflags
-
-
-set_config('_DEPEND_CFLAGS', depend_cflags(c_compiler))
-set_config('_HOST_DEPEND_CFLAGS', depend_cflags(host_c_compiler))
-
-
 @depends(c_compiler, check_build_environment, target)
 @imports('multiprocessing')
 @imports(_from='__builtin__', _import='min')
 def pgo_flags(compiler, build_env, target):
     topobjdir = build_env.topobjdir
     if topobjdir.endswith('/js/src'):
         topobjdir = topobjdir[:-7]
 
@@ -1453,65 +48,46 @@ def pgo_flags(compiler, build_env, targe
         )
 
 
 set_config('PROFILE_GEN_CFLAGS', pgo_flags.gen_cflags)
 set_config('PROFILE_GEN_LDFLAGS', pgo_flags.gen_ldflags)
 set_config('PROFILE_USE_CFLAGS', pgo_flags.use_cflags)
 set_config('PROFILE_USE_LDFLAGS', pgo_flags.use_ldflags)
 
-
-@depends(c_compiler)
-def preprocess_option(compiler):
-    # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
-    if compiler.type in ('gcc', 'clang'):
-        return '-E -o '
-    else:
-        return '-P -Fi'
-
-
-set_config('PREPROCESS_OPTION', preprocess_option)
-
-
-# We only want to include windows.configure when we are compiling on
-# Windows, for Windows.
-
-
-@depends(target, host)
-def is_windows(target, host):
-    return host.kernel == 'WINNT' and target.kernel == 'WINNT'
-
-
-include('windows.configure', when=is_windows)
-
-# PGO
-# ==============================================================
 llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
                            allow_missing=True,
                            paths=toolchain_search_path)
 
 js_option('--enable-profile-generate',
+          nargs='?',
+          choices=('cross',),
           help='Build a PGO instrumented binary')
 
 imply_option('MOZ_PGO',
              depends_if('--enable-profile-generate')(lambda _: True))
 
 set_config('MOZ_PROFILE_GENERATE',
            depends_if('--enable-profile-generate')(lambda _: True))
 
 set_define('MOZ_PROFILE_GENERATE',
            depends_if('--enable-profile-generate')(lambda _: True))
 
 js_option('--enable-profile-use',
+          nargs='?',
+          choices=('cross',),
           help='Use a generated profile during the build')
 
 js_option('--with-pgo-profile-path',
           help='Path to the directory with unmerged profile data to use during the build',
           nargs=1)
 
+js_option('--enable-cross-pgo',
+          help='Enable PGO on Rust code')
+
 imply_option('MOZ_PGO',
              depends_if('--enable-profile-use')(lambda _: True))
 
 set_config('MOZ_PROFILE_USE',
            depends_if('--enable-profile-use')(lambda _: True))
 
 
 @depends('--with-pgo-profile-path', '--enable-profile-use', llvm_profdata)
@@ -1534,16 +110,47 @@ set_config('PGO_PROFILE_PATH', pgo_profi
 
 option('--with-pgo-jarlog',
        help='Use the provided jarlog file when packaging during a profile-use '
             'build',
        nargs=1)
 
 set_config('PGO_JARLOG_PATH', depends_if('--with-pgo-jarlog')(lambda p: p))
 
+
+@depends('MOZ_PGO', '--enable-profile-use', '--enable-profile-generate',
+         c_compiler, rustc_info)
+def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler, rustc):
+    if not pgo:
+        return
+
+    # Enabling PGO through MOZ_PGO only and not --enable* flags.
+    if not profile_use and not profile_generate:
+        return
+
+    if profile_use and profile_generate:
+        die('Cannot build with --enable-profile-use and --enable-profile-generate.')
+
+    want_cross = (len(profile_use) and profile_use[0] == 'cross') \
+                 or (len(profile_generate) and profile_generate[0] == 'cross')
+
+    if not want_cross:
+        return
+
+    if c_compiler.type == 'gcc':
+        die('Cannot use cross-language PGO with GCC.')
+
+    # PGO is not stable prior to 1.37
+    if rustc.version < Version('1.37'):
+        die('Cannot use cross-language PGO with Rust version %s.' % rustc.version)
+
+    return True
+
+set_config('MOZ_PGO_RUST', moz_pgo_rust)
+
 # LTO
 # ==============================================================
 
 js_option('--enable-lto',
           env='MOZ_LTO',
           nargs='?',
           choices=('full', 'thin', 'cross'),
           help='Enable LTO')
@@ -1619,947 +226,8 @@ def lto(value, pgo, profile_generate, c_
 add_old_configure_assignment('MOZ_LTO', lto.enabled)
 set_config('MOZ_LTO', lto.enabled)
 set_define('MOZ_LTO', lto.enabled)
 set_config('MOZ_LTO_CFLAGS', lto.cflags)
 set_config('MOZ_LTO_LDFLAGS', lto.ldflags)
 set_config('MOZ_LTO_RUST', lto.rust_lto)
 add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.cflags)
 add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags)
-
-# ASAN
-# ==============================================================
-
-js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
-
-
-@depends(when='--enable-address-sanitizer')
-def asan():
-    return True
-
-
-add_old_configure_assignment('MOZ_ASAN', asan)
-
-# MSAN
-# ==============================================================
-
-js_option('--enable-memory-sanitizer', help='Enable Memory Sanitizer')
-
-
-@depends(when='--enable-memory-sanitizer')
-def msan():
-    return True
-
-
-add_old_configure_assignment('MOZ_MSAN', msan)
-
-# TSAN
-# ==============================================================
-
-js_option('--enable-thread-sanitizer', help='Enable Thread Sanitizer')
-
-
-@depends(when='--enable-thread-sanitizer')
-def tsan():
-    return True
-
-
-add_old_configure_assignment('MOZ_TSAN', tsan)
-
-# UBSAN
-# ==============================================================
-
-js_option('--enable-undefined-sanitizer',
-          nargs='*',
-          help='Enable UndefinedBehavior Sanitizer')
-
-@depends_if('--enable-undefined-sanitizer')
-def ubsan(options):
-    default_checks = [
-        'bool',
-        'bounds',
-        'vla-bound',
-    ]
-
-    checks = options if len(options) else default_checks
-
-    return ','.join(checks)
-
-add_old_configure_assignment('MOZ_UBSAN_CHECKS', ubsan)
-
-
-js_option('--enable-signed-overflow-sanitizer',
-          help='Enable UndefinedBehavior Sanitizer (Signed Integer Overflow Parts)')
-
-
-@depends(when='--enable-signed-overflow-sanitizer')
-def ub_signed_overflow_san():
-    return True
-
-
-add_old_configure_assignment('MOZ_SIGNED_OVERFLOW_SANITIZE', ub_signed_overflow_san)
-
-
-js_option('--enable-unsigned-overflow-sanitizer',
-          help='Enable UndefinedBehavior Sanitizer (Unsigned Integer Overflow Parts)')
-
-
-@depends(when='--enable-unsigned-overflow-sanitizer')
-def ub_unsigned_overflow_san():
-    return True
-
-
-add_old_configure_assignment('MOZ_UNSIGNED_OVERFLOW_SANITIZE', ub_unsigned_overflow_san)
-
-# Security Hardening
-# ==============================================================
-
-option('--enable-hardening', env='MOZ_SECURITY_HARDENING',
-       help='Enables security hardening compiler options')
-
-
-# This function is a bit confusing. It adds or removes hardening flags in
-# three stuations: if --enable-hardening is passed; if --disable-hardening
-# is passed, and if no flag is passed.
-#
-# At time of this comment writing, all flags are actually added in the
-# default no-flag case; making --enable-hardening the same as omitting the
-# flag. --disable-hardening will omit the security flags. (However, not all
-# possible security flags will be omitted by --disable-hardening, as many are
-# compiler-default options we do not explicitly enable.)
-@depends('--enable-hardening', '--enable-address-sanitizer',
-         '--enable-optimize', c_compiler, target)
-def security_hardening_cflags(hardening_flag, asan, optimize, c_compiler, target):
-    compiler_is_gccish = c_compiler.type in ('gcc', 'clang')
-
-    flags = []
-    ldflags = []
-    js_flags = []
-    js_ldflags = []
-
-    # ----------------------------------------------------------
-    # If hardening is explicitly enabled, or not explicitly disabled
-    if hardening_flag.origin == "default" or hardening_flag:
-        # FORTIFY_SOURCE ------------------------------------
-        # Require optimization for FORTIFY_SOURCE. See Bug 1417452
-        # Also, undefine it before defining it just in case a distro adds it, see Bug 1418398
-        if compiler_is_gccish and optimize and not asan:
-            # Don't enable FORTIFY_SOURCE on Android on the top-level, but do enable in js/
-            if target.os != 'Android':
-                flags.append("-U_FORTIFY_SOURCE")
-                flags.append("-D_FORTIFY_SOURCE=2")
-            js_flags.append("-U_FORTIFY_SOURCE")
-            js_flags.append("-D_FORTIFY_SOURCE=2")
-
-        # fstack-protector ------------------------------------
-        # Enable only if hardening is not disabled and ASAN is
-        # not on as ASAN will catch the crashes for us
-        if compiler_is_gccish and not asan:
-            # mingw-clang cross-compile toolchain has bugs with stack protector
-            if target.os != 'WINNT' or c_compiler == 'gcc':
-                flags.append("-fstack-protector-strong")
-                ldflags.append("-fstack-protector-strong")
-                js_flags.append("-fstack-protector-strong")
-                js_ldflags.append("-fstack-protector-strong")
-
-        # ASLR ------------------------------------------------
-        # ASLR (dynamicbase) is enabled by default in clang-cl; but the
-        # mingw-clang build requires it to be explicitly enabled
-        if target.os == 'WINNT' and c_compiler.type == 'clang':
-            ldflags.append("-Wl,--dynamicbase")
-            js_ldflags.append("-Wl,--dynamicbase")
-
-        # Control Flow Guard (CFG) ----------------------------
-        # On aarch64, this is enabled only with explicit --enable-hardening
-        # (roughly: automation) due to a dependency on a patched clang-cl.
-        if c_compiler.type == 'clang-cl' and c_compiler.version >= '8' and \
-           (target.cpu != 'aarch64' or hardening_flag):
-            flags.append("-guard:cf")
-            js_flags.append("-guard:cf")
-            # nolongjmp is needed because clang doesn't emit the CFG tables of
-            # setjmp return addresses https://bugs.llvm.org/show_bug.cgi?id=40057
-            ldflags.append("-guard:cf,nolongjmp")
-            js_ldflags.append("-guard:cf,nolongjmp")
-
-    # ----------------------------------------------------------
-    # If ASAN _is_ on, undefine FORTIFY_SOURCE just to be safe
-    if asan:
-        flags.append("-U_FORTIFY_SOURCE")
-        js_flags.append("-U_FORTIFY_SOURCE")
-
-    # fno-common -----------------------------------------
-    # Do not merge variables for ASAN; can detect some subtle bugs
-    if asan:
-        # clang-cl does not recognize the flag, it must be passed down to clang
-        if c_compiler.type == 'clang-cl':
-            flags.append("-Xclang")
-        flags.append("-fno-common")
-
-    return namespace(
-        flags=flags,
-        ldflags=ldflags,
-        js_flags=js_flags,
-        js_ldflags=js_ldflags,
-    )
-
-
-add_old_configure_assignment('MOZ_HARDENING_CFLAGS', security_hardening_cflags.flags)
-add_old_configure_assignment('MOZ_HARDENING_LDFLAGS', security_hardening_cflags.ldflags)
-add_old_configure_assignment('MOZ_HARDENING_CFLAGS_JS', security_hardening_cflags.js_flags)
-add_old_configure_assignment('MOZ_HARDENING_LDFLAGS_JS', security_hardening_cflags.js_ldflags)
-
-
-# Frame pointers
-# ==============================================================
-@depends(c_compiler)
-def frame_pointer_flags(compiler):
-    if compiler.type == 'clang-cl':
-        return namespace(
-            enable=['-Oy-'],
-            disable=['-Oy'],
-        )
-    return namespace(
-        enable=['-fno-omit-frame-pointer', '-funwind-tables'],
-        disable=['-fomit-frame-pointer', '-funwind-tables'],
-    )
-
-
-@depends(moz_optimize.optimize, moz_debug, target,
-         '--enable-memory-sanitizer', '--enable-address-sanitizer',
-         '--enable-undefined-sanitizer')
-def frame_pointer_default(optimize, debug, target, msan, asan, ubsan):
-    return bool(not optimize or debug or msan or asan or ubsan or \
-        (target.os == 'WINNT' and target.cpu in ('x86', 'aarch64')))
-
-
-js_option('--enable-frame-pointers', default=frame_pointer_default,
-          help='{Enable|Disable} frame pointers')
-
-
-@depends('--enable-frame-pointers', frame_pointer_flags)
-def frame_pointer_flags(enable, flags):
-    if enable:
-        return flags.enable
-    return flags.disable
-
-
-set_config('MOZ_FRAMEPTR_FLAGS', frame_pointer_flags)
-
-
-# nasm detection
-# ==============================================================
-nasm = check_prog('NASM', ['nasm'], allow_missing=True, paths=toolchain_search_path)
-
-
-@depends_if(nasm)
-@checking('nasm version')
-def nasm_version(nasm):
-    (retcode, stdout, _) = get_cmd_output(nasm, '-v')
-    if retcode:
-        # mac stub binary
-        return None
-
-    version = stdout.splitlines()[0].split()[2]
-    return Version(version)
-
-
-@depends_if(nasm_version)
-def nasm_major_version(nasm_version):
-    return str(nasm_version.major)
-
-
-@depends_if(nasm_version)
-def nasm_minor_version(nasm_version):
-    return str(nasm_version.minor)
-
-
-set_config('NASM_MAJOR_VERSION', nasm_major_version)
-set_config('NASM_MINOR_VERSION', nasm_minor_version)
-
-
-@depends(nasm, target)
-def nasm_asflags(nasm, target):
-    if nasm:
-        asflags = {
-            ('OSX', 'x86'): ['-f', 'macho32'],
-            ('OSX', 'x86_64'): ['-f', 'macho64'],
-            ('WINNT', 'x86'): ['-f', 'win32'],
-            ('WINNT', 'x86_64'): ['-f', 'win64'],
-        }.get((target.os, target.cpu), None)
-        if asflags is None:
-            # We're assuming every x86 platform we support that's
-            # not Windows or Mac is ELF.
-            if target.cpu == 'x86':
-                asflags = ['-f', 'elf32']
-            elif target.cpu == 'x86_64':
-                asflags = ['-f', 'elf64']
-        return asflags
-
-
-set_config('NASM_ASFLAGS', nasm_asflags)
-
-@depends(nasm_asflags)
-def have_nasm(value):
-    if value:
-        return True
-
-
-@depends(yasm_asflags)
-def have_yasm(yasm_asflags):
-    if yasm_asflags:
-        return True
-
-set_config('HAVE_NASM', have_nasm)
-
-set_config('HAVE_YASM', have_yasm)
-# Until the YASM variable is not necessary in old-configure.
-add_old_configure_assignment('YASM', have_yasm)
-
-
-# clang-cl integrated assembler support
-# ==============================================================
-@depends(target)
-def clangcl_asflags(target):
-    asflags = None
-    if target.os == 'WINNT' and target.cpu == 'aarch64':
-        asflags = ['--target=aarch64-windows-msvc']
-    return asflags
-
-
-set_config('CLANGCL_ASFLAGS', clangcl_asflags)
-
-
-# Code Coverage
-# ==============================================================
-
-js_option('--enable-coverage', env='MOZ_CODE_COVERAGE',
-          help='Enable code coverage')
-
-
-@depends('--enable-coverage')
-def code_coverage(value):
-    if value:
-        return True
-
-
-set_config('MOZ_CODE_COVERAGE', code_coverage)
-set_define('MOZ_CODE_COVERAGE', code_coverage)
-
-@depends(target, c_compiler, vc_path, check_build_environment, when=code_coverage)
-@imports('re')
-@imports('mozpack.path')
-@imports(_from='__builtin__', _import='open')
-def coverage_cflags(target, c_compiler, vc_path, build_env):
-    cflags = ['--coverage']
-
-    if c_compiler.type in ('clang', 'clang-cl'):
-        cflags += [
-            '-Xclang', '-coverage-no-function-names-in-data',
-        ]
-
-    if target.os == 'WINNT' and c_compiler.type == 'clang-cl':
-        # The Visual Studio directory is the parent of the Visual C++ directory.
-        vs_path = os.path.dirname(vc_path)
-
-        # We need to get the real path of Visual Studio, which can be in a
-        # symlinked directory (for example, on automation).
-        vs_path = mozpack.path.readlink(vs_path)
-        # Since the -fprofile-exclude-files option in LLVM is a regex, we need to
-        # have the same path separators.
-        vs_path = vs_path.replace('/', '\\')
-
-        cflags += [
-            '-fprofile-exclude-files=^{}.*$'.format(re.escape(vs_path)),
-        ]
-
-    response_file_path = os.path.join(build_env.topobjdir, 'code_coverage_cflags')
-
-    with open(response_file_path, 'w') as f:
-        f.write(' '.join(cflags))
-
-    return ['@{}'.format(response_file_path)]
-
-set_config('COVERAGE_CFLAGS', coverage_cflags)
-
-# ==============================================================
-
-option(env='RUSTFLAGS',
-       nargs=1,
-       help='Rust compiler flags')
-set_config('RUSTFLAGS', depends('RUSTFLAGS')(lambda flags: flags))
-
-
-# Rust compiler flags
-# ==============================================================
-
-js_option(env='RUSTC_OPT_LEVEL',
-          nargs=1,
-          help='Rust compiler optimization level (-C opt-level=%s)')
-
-# --enable-release kicks in full optimizations.
-imply_option('RUSTC_OPT_LEVEL', '2', when='--enable-release')
-
-
-@depends('RUSTC_OPT_LEVEL', moz_optimize)
-def rustc_opt_level(opt_level_option, moz_optimize):
-    if opt_level_option:
-        return opt_level_option[0]
-    else:
-        return '1' if moz_optimize.optimize else '0'
-
-
-@depends(rustc_opt_level, debug_rust, '--enable-debug-symbols', '--enable-frame-pointers')
-def rust_compile_flags(opt_level, debug_rust, debug_symbols, frame_pointers):
-    # Cargo currently supports only two interesting profiles for building:
-    # development and release. Those map (roughly) to --enable-debug and
-    # --disable-debug in Gecko, respectively.
-    #
-    # But we'd also like to support an additional axis of control for
-    # optimization level. Since Cargo only supports 2 profiles, we're in
-    # a bit of a bind.
-    #
-    # Code here derives various compiler options given other configure options.
-    # The options defined here effectively override defaults specified in
-    # Cargo.toml files.
-
-    debug_assertions = None
-    debug_info = None
-
-    # opt-level=0 implies -C debug-assertions, which may not be desired
-    # unless Rust debugging is enabled.
-    if opt_level == '0' and not debug_rust:
-        debug_assertions = False
-
-    if debug_symbols:
-        debug_info = '2'
-
-    opts = []
-
-    if opt_level is not None:
-        opts.append('opt-level=%s' % opt_level)
-    if debug_assertions is not None:
-        opts.append('debug-assertions=%s' %
-                    ('yes' if debug_assertions else 'no'))
-    if debug_info is not None:
-        opts.append('debuginfo=%s' % debug_info)
-    if frame_pointers:
-        opts.append('force-frame-pointers=yes')
-
-    flags = []
-    for opt in opts:
-        flags.extend(['-C', opt])
-
-    return flags
-
-
-# Rust incremental compilation
-# ==============================================================
-
-js_option('--disable-cargo-incremental',
-          help='Disable incremental rust compilation.')
-
-@depends(rustc_opt_level, debug_rust, 'MOZ_AUTOMATION', code_coverage,
-         '--disable-cargo-incremental')
-def cargo_incremental(opt_level, debug_rust, automation, code_coverage,
-                      enabled):
-    """Return a value for the CARGO_INCREMENTAL environment variable."""
-
-    if not enabled:
-        return '0'
-
-    # We never want to use incremental compilation in automation.  sccache
-    # handles our automation use case much better than incremental compilation
-    # would.
-    if automation:
-        return '0'
-
-    # Coverage instrumentation doesn't play well with incremental compilation
-    # https://github.com/rust-lang/rust/issues/50203.
-    if code_coverage:
-        return '0'
-
-    # Incremental compilation is automatically turned on for debug builds, so
-    # we don't need to do anything special here.
-    if debug_rust:
-        return
-
-    # --enable-release automatically sets -O2 for Rust code, and people can
-    # set RUSTC_OPT_LEVEL to 2 or even 3 if they want to profile Rust code.
-    # Let's assume that if Rust code is using -O2 or higher, we shouldn't
-    # be using incremental compilation, because we'd be imposing a
-    # significant runtime cost.
-    if opt_level not in ('0', '1'):
-        return
-
-    # We're clear to use incremental compilation!
-    return '1'
-
-
-set_config('CARGO_INCREMENTAL', cargo_incremental)
-
-# Linker detection
-# ==============================================================
-
-
-@depends(target)
-def is_linker_option_enabled(target):
-    if target.kernel not in ('WINNT', 'SunOS'):
-        return True
-
-
-option('--enable-gold',
-       env='MOZ_FORCE_GOLD',
-       help='Enable GNU Gold Linker when it is not already the default',
-       when=is_linker_option_enabled)
-
-imply_option('--enable-linker', 'gold', when='--enable-gold')
-
-js_option('--enable-linker', nargs=1,
-          help='Select the linker {bfd, gold, ld64, lld, lld-*}',
-          when=is_linker_option_enabled)
-
-
-@depends('--enable-linker', c_compiler, developer_options, '--enable-gold',
-         extra_toolchain_flags, target, when=is_linker_option_enabled)
-@checking('for linker', lambda x: x.KIND)
-@imports('os')
-@imports('shutil')
-def select_linker(linker, c_compiler, developer_options, enable_gold,
-                  toolchain_flags, target):
-
-    if linker:
-        linker = linker[0]
-    else:
-        linker = None
-
-    def is_valid_linker(linker):
-        if target.kernel == 'Darwin':
-            valid_linkers = ('ld64', 'lld')
-        else:
-            valid_linkers = ('bfd', 'gold', 'lld')
-        if linker in valid_linkers:
-            return True
-        if 'lld' in valid_linkers and linker.startswith('lld-'):
-            return True
-        return False
-
-    if linker and not is_valid_linker(linker):
-        # Check that we are trying to use a supported linker
-        die('Unsupported linker ' + linker)
-
-    # Check the kind of linker
-    version_check = ['-Wl,--version']
-    cmd_base = c_compiler.wrapper + [c_compiler.compiler] + c_compiler.flags
-
-    def try_linker(linker):
-        # Generate the compiler flag
-        if linker == 'ld64':
-            linker_flag = ['-fuse-ld=ld']
-        elif linker:
-            linker_flag = ["-fuse-ld=" + linker]
-        else:
-            linker_flag = []
-        cmd = cmd_base + linker_flag + version_check
-        if toolchain_flags:
-            cmd += toolchain_flags
-
-        # ld64 doesn't have anything to print out a version. It does print out
-        # "ld64: For information on command line options please use 'man ld'."
-        # but that would require doing two attempts, one with --version, that
-        # would fail, and another with --help.
-        # Instead, abuse its LD_PRINT_OPTIONS feature to detect a message
-        # specific to it on stderr when it fails to process --version.
-        env = dict(os.environ)
-        env['LD_PRINT_OPTIONS'] = '1'
-        retcode, stdout, stderr = get_cmd_output(*cmd, env=env)
-        cmd_output = stdout.decode('utf-8')
-        stderr = stderr.decode('utf-8')
-        if retcode == 1 and 'Logging ld64 options' in stderr:
-            kind = 'ld64'
-
-        elif retcode != 0:
-            return None
-
-        elif 'GNU ld' in cmd_output:
-            # We are using the normal linker
-            kind = 'bfd'
-
-        elif 'GNU gold' in cmd_output:
-            kind = 'gold'
-
-        elif 'LLD' in cmd_output:
-            kind = 'lld'
-
-        else:
-            kind = 'unknown'
-
-        return namespace(
-            KIND=kind,
-            LINKER_FLAG=linker_flag,
-        )
-
-    result = try_linker(linker)
-    if result is None:
-        if linker:
-            die("Could not use {} as linker".format(linker))
-        die("Failed to find a linker")
-
-    if (linker is None and enable_gold.origin == 'default' and
-            developer_options and result.KIND in ('bfd', 'gold')):
-        # try and use lld if available.
-        tried = try_linker('lld')
-        if result.KIND != 'gold' and (tried is None or tried.KIND != 'lld'):
-            tried = try_linker('gold')
-            if tried is None or tried.KIND != 'gold':
-                tried = None
-        if tried:
-            result = tried
-
-    # If an explicit linker was given, error out if what we found is different.
-    if linker and not linker.startswith(result.KIND):
-        die("Could not use {} as linker".format(linker))
-
-    return result
-
-
-set_config('LINKER_KIND', select_linker.KIND)
-
-
-@depends_if(select_linker, macos_sdk)
-def linker_ldflags(linker, macos_sdk):
-    flags = list(linker.LINKER_FLAG or [])
-    if macos_sdk:
-        if linker.KIND == 'ld64':
-            flags.append('-Wl,-syslibroot,%s' % macos_sdk)
-        else:
-            flags.append('-Wl,--sysroot=%s' % macos_sdk)
-
-    return flags
-
-
-add_old_configure_assignment('LINKER_LDFLAGS', linker_ldflags)
-
-
-# There's a wrinkle with MinGW: linker configuration is not enabled, so
-# `select_linker` is never invoked.  Hard-code around it.
-@depends(select_linker, target, c_compiler)
-def gcc_use_gnu_ld(select_linker, target, c_compiler):
-    if select_linker is not None:
-        return select_linker.KIND in ('bfd', 'gold', 'lld')
-    if target.kernel == 'WINNT' and c_compiler.type == 'clang':
-        return True
-    return None
-
-
-# GCC_USE_GNU_LD=1 means the linker is command line compatible with GNU ld.
-set_config('GCC_USE_GNU_LD', gcc_use_gnu_ld)
-add_old_configure_assignment('GCC_USE_GNU_LD', gcc_use_gnu_ld)
-
-# Assembler detection
-# ==============================================================
-
-js_option(env='AS', nargs=1, help='Path to the assembler')
-
-@depends(target, c_compiler)
-def as_info(target, c_compiler):
-    if c_compiler.type == 'clang-cl':
-        ml = {
-            'x86': 'ml',
-            'x86_64': 'ml64',
-            'aarch64': 'armasm64.exe',
-        }.get(target.cpu)
-        return namespace(
-            type='masm',
-            names=(ml, )
-        )
-    # When building with anything but clang-cl, we just use the C compiler as the assembler.
-    return namespace(
-        type='gcc',
-        names=(c_compiler.compiler, )
-    )
-
-# One would expect the assembler to be specified merely as a program.  But in
-# cases where the assembler is passed down into js/, it can be specified in
-# the same way as CC: a program + a list of argument flags.  We might as well
-# permit the same behavior in general, even though it seems somewhat unusual.
-# So we have to do the same sort of dance as we did above with
-# `provided_compiler`.
-provided_assembler = provided_program('AS')
-assembler = check_prog('_AS', input=provided_assembler.program,
-                       what='the assembler', progs=as_info.names,
-                       paths=toolchain_search_path)
-
-@depends(as_info, assembler, provided_assembler, c_compiler)
-def as_with_flags(as_info, assembler, provided_assembler, c_compiler):
-    if provided_assembler:
-        return provided_assembler.wrapper + \
-            [provided_assembler.program] + \
-            provided_assembler.flags
-
-    if as_info.type == 'masm':
-        return assembler
-
-    assert as_info.type == 'gcc'
-
-    # Need to add compiler wrappers and flags as appropriate.
-    return c_compiler.wrapper + [assembler] + c_compiler.flags
-
-
-add_old_configure_assignment('AS', as_with_flags)
-add_old_configure_assignment('ac_cv_prog_AS', as_with_flags)
-
-
-@depends(assembler, c_compiler, extra_toolchain_flags)
-@imports('subprocess')
-@imports(_from='os', _import='devnull')
-def gnu_as(assembler, c_compiler, toolchain_flags):
-    # clang uses a compatible GNU assembler.
-    if c_compiler.type == 'clang':
-        return True
-
-    if c_compiler.type == 'gcc':
-        cmd = [assembler] + c_compiler.flags
-        if toolchain_flags:
-            cmd += toolchain_flags
-        cmd += ['-Wa,--version', '-c', '-o', devnull, '-x', 'assembler', '-']
-        # We don't actually have to provide any input on stdin, `Popen.communicate` will
-        # close the stdin pipe.
-        # clang will error if it uses its integrated assembler for this target,
-        # so handle failures gracefully.
-        if 'GNU' in check_cmd_output(*cmd, stdin=subprocess.PIPE, onerror=lambda: '').decode('utf-8'):
-            return True
-
-
-set_config('GNU_AS', gnu_as)
-add_old_configure_assignment('GNU_AS', gnu_as)
-
-
-@depends(as_info, target)
-def as_dash_c_flag(as_info, target):
-    # armasm64 doesn't understand -c.
-    if as_info.type == 'masm' and target.cpu == 'aarch64':
-        return ''
-    else:
-        return '-c'
-
-
-set_config('AS_DASH_C_FLAG', as_dash_c_flag)
-
-
-@depends(as_info, target)
-def as_outoption(as_info, target):
-    # The uses of ASOUTOPTION depend on the spacing for -o/-Fo.
-    if as_info.type == 'masm' and target.cpu != 'aarch64':
-        return '-Fo'
-
-    return '-o '
-
-
-set_config('ASOUTOPTION', as_outoption)
-
-# clang plugin handling
-# ==============================================================
-
-js_option('--enable-clang-plugin', env='ENABLE_CLANG_PLUGIN',
-          help="Enable building with the mozilla clang plugin")
-
-add_old_configure_assignment('ENABLE_CLANG_PLUGIN',
-                             depends_if('--enable-clang-plugin')(lambda _: True))
-
-js_option('--enable-mozsearch-plugin', env='ENABLE_MOZSEARCH_PLUGIN',
-          help="Enable building with the mozsearch indexer plugin")
-
-add_old_configure_assignment('ENABLE_MOZSEARCH_PLUGIN',
-                             depends_if('--enable-mozsearch-plugin')(lambda _: True))
-
-# Libstdc++ compatibility hacks
-# ==============================================================
-#
-js_option('--enable-stdcxx-compat', env='MOZ_STDCXX_COMPAT',
-          help='Enable compatibility with older libstdc++')
-
-
-@template
-def libstdcxx_version(var, compiler):
-    @depends(compiler, when='--enable-stdcxx-compat')
-    @checking(var, lambda v: v and "GLIBCXX_%s" % v.dotted)
-    @imports(_from='mozbuild.configure.libstdcxx', _import='find_version')
-    @imports(_from='__builtin__', _import='Exception')
-    def version(compiler):
-        try:
-            result = find_version(
-                compiler.wrapper + [compiler.compiler] + compiler.flags)
-        except Exception:
-            die("Couldn't determine libstdc++ version")
-        if result:
-            return namespace(
-                dotted=result[0],
-                encoded=str(result[1]),
-            )
-
-    set_config(var, version.encoded)
-    return version
-
-
-add_gcc_flag(
-    '-D_GLIBCXX_USE_CXX11_ABI=0', cxx_compiler,
-    when=libstdcxx_version(
-        'MOZ_LIBSTDCXX_TARGET_VERSION', cxx_compiler))
-add_gcc_flag(
-    '-D_GLIBCXX_USE_CXX11_ABI=0', host_cxx_compiler,
-    when=libstdcxx_version(
-        'MOZ_LIBSTDCXX_HOST_VERSION', host_cxx_compiler))
-
-
-# Support various fuzzing options
-# ==============================================================
-js_option('--enable-fuzzing', help='Enable fuzzing support')
-
-@depends('--enable-fuzzing')
-def enable_fuzzing(value):
-    if value:
-        return True
-
-@depends(try_compile(body='__AFL_COMPILER;',
-                     check_msg='for AFL compiler',
-                     when='--enable-fuzzing'))
-def enable_aflfuzzer(afl):
-    if afl:
-        return True
-
-@depends(enable_fuzzing,
-         enable_aflfuzzer,
-         c_compiler,
-         target)
-def enable_libfuzzer(fuzzing, afl, c_compiler, target):
-    if fuzzing and not afl and c_compiler.type == 'clang' and target.os != 'Android':
-        return True
-
-@depends(enable_fuzzing,
-         enable_aflfuzzer,
-         enable_libfuzzer)
-def enable_fuzzing_interfaces(fuzzing, afl, libfuzzer):
-    if fuzzing and (afl or libfuzzer):
-        return True
-
-set_config('FUZZING', enable_fuzzing)
-set_define('FUZZING', enable_fuzzing)
-
-set_config('LIBFUZZER', enable_libfuzzer)
-set_define('LIBFUZZER', enable_libfuzzer)
-add_old_configure_assignment('LIBFUZZER', enable_libfuzzer)
-
-set_config('FUZZING_INTERFACES', enable_fuzzing_interfaces)
-set_define('FUZZING_INTERFACES', enable_fuzzing_interfaces)
-add_old_configure_assignment('FUZZING_INTERFACES', enable_fuzzing_interfaces)
-
-
-@depends(c_compiler.try_compile(flags=['-fsanitize=fuzzer-no-link'],
-         when=enable_fuzzing,
-         check_msg='whether the C compiler supports -fsanitize=fuzzer-no-link'))
-def libfuzzer_flags(value):
-    if value:
-        no_link_flag_supported = True
-        # recommended for (and only supported by) clang >= 6
-        use_flags = ['-fsanitize=fuzzer-no-link']
-    else:
-        no_link_flag_supported = False
-        use_flags = ['-fsanitize-coverage=trace-pc-guard,trace-cmp']
-
-    return namespace(
-        no_link_flag_supported=no_link_flag_supported,
-        use_flags=use_flags,
-    )
-
-set_config('HAVE_LIBFUZZER_FLAG_FUZZER_NO_LINK', libfuzzer_flags.no_link_flag_supported)
-set_config('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
-add_old_configure_assignment('LIBFUZZER_FLAGS', libfuzzer_flags.use_flags)
-
-# Shared library building
-# ==============================================================
-
-# XXX: The use of makefile constructs in these variables is awful.
-@depends(target, c_compiler)
-def make_shared_library(target, compiler):
-    if target.os == 'WINNT':
-        if compiler.type == 'gcc':
-            return namespace(
-                mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-o', '$@'],
-                mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-o', '$@'],
-            )
-        elif compiler.type == 'clang':
-            return namespace(
-                mkshlib=['$(CXX)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
-                mkcshlib=['$(CC)', '$(DSO_LDOPTS)', '-Wl,-pdb,$(LINK_PDBFILE)', '-o', '$@'],
-            )
-        else:
-            linker = [
-                '$(LINKER)',
-                '-NOLOGO', '-DLL',
-                '-OUT:$@',
-                '-PDB:$(LINK_PDBFILE)',
-                '$(DSO_LDOPTS)'
-            ]
-            return namespace(
-                mkshlib=linker,
-                mkcshlib=linker,
-            )
-
-    cc = ['$(CC)', '$(COMPUTED_C_LDFLAGS)']
-    cxx = ['$(CXX)', '$(COMPUTED_CXX_LDFLAGS)']
-    flags = ['$(PGO_CFLAGS)', '$(DSO_PIC_CFLAGS)', '$(DSO_LDOPTS)']
-    output = ['-o', '$@']
-
-    if target.kernel == 'Darwin':
-        soname = []
-    elif target.os == 'NetBSD':
-        soname = ['-Wl,-soname,$(DSO_SONAME)']
-    else:
-        assert compiler.type in ('gcc', 'clang')
-
-        soname = ['-Wl,-h,$(DSO_SONAME)']
-
-    return namespace(
-        mkshlib=cxx + flags + soname + output,
-        mkcshlib=cc + flags + soname + output,
-    )
-
-
-set_config('MKSHLIB', make_shared_library.mkshlib)
-set_config('MKCSHLIB', make_shared_library.mkcshlib)
-
-
-@depends(c_compiler, toolchain_prefix, when=target_is_windows)
-def rc_names(c_compiler, toolchain_prefix):
-    if c_compiler.type in ('gcc', 'clang'):
-        return tuple('%s%s' % (p, 'windres')
-                     for p in ('',) + (toolchain_prefix or ()))
-    return ('rc',)
-
-
-check_prog('RC', rc_names, paths=sdk_bin_path)
-
-
-@depends(link, toolchain_prefix)
-def ar_config(link, toolchain_prefix):
-    if link:  # if LINKER is set, it's either for lld-link or link
-        if 'lld-link' in link:
-            return namespace(
-                names=('llvm-lib',),
-                flags=('-llvmlibthin', '-out:$@'),
-            )
-        else:
-            return namespace(
-                names=('lib',),
-                flags=('-NOLOGO', '-OUT:$@'),
-            )
-    return namespace(
-        names=tuple('%s%s' % (p, 'ar')
-                   for p in (toolchain_prefix or ()) + ('',)),
-        flags=('crs', '$@'),
-    )
-
-
-ar = check_prog('AR', ar_config.names, paths=toolchain_search_path)
-
-add_old_configure_assignment('AR', ar)
-
-set_config('AR_FLAGS', ar_config.flags)
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -91,16 +91,17 @@ set_config('RUSTC', rustc)
 @checking('rustc version', lambda info: info.version)
 def rustc_info(rustc):
     out = check_cmd_output(rustc, '--version', '--verbose').splitlines()
     info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:])
     return namespace(
         version=Version(info.get('release', '0')),
         commit=info.get('commit-hash', 'unknown'),
         host=info['host'],
+        llvm_version=Version(info.get('LLVM version', '0')),
     )
 
 set_config('RUSTC_VERSION', depends(rustc_info)(lambda info: str(info.version)))
 
 @depends_if(cargo)
 @checking('cargo version', lambda info: info.version)
 @imports('re')
 def cargo_info(cargo):
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -1406,64 +1406,16 @@ def depend_cflags(host_or_target_c_compi
 
     return depend_cflags
 
 
 set_config('_DEPEND_CFLAGS', depend_cflags(c_compiler))
 set_config('_HOST_DEPEND_CFLAGS', depend_cflags(host_c_compiler))
 
 
-@depends(c_compiler, check_build_environment, target)
-@imports('multiprocessing')
-@imports(_from='__builtin__', _import='min')
-def pgo_flags(compiler, build_env, target):
-    topobjdir = build_env.topobjdir
-    if topobjdir.endswith('/js/src'):
-        topobjdir = topobjdir[:-7]
-
-    if compiler.type == 'gcc':
-        return namespace(
-            gen_cflags=['-fprofile-generate'],
-            gen_ldflags=['-fprofile-generate'],
-            use_cflags=['-fprofile-use', '-fprofile-correction',
-                        '-Wcoverage-mismatch'],
-            use_ldflags=['-fprofile-use'],
-        )
-
-    if compiler.type in ('clang-cl', 'clang'):
-        profdata = os.path.join(topobjdir, 'merged.profdata')
-        prefix = ''
-        if compiler.type == 'clang-cl':
-            prefix = '/clang:'
-            if target.cpu == 'x86_64':
-                gen_ldflags = ['clang_rt.profile-x86_64.lib']
-            elif target.cpu == 'x86':
-                gen_ldflags = ['clang_rt.profile-i386.lib']
-            else:
-                gen_ldflags = None
-        else:
-            gen_ldflags = ['-fprofile-generate']
-
-        return namespace(
-            gen_cflags=[prefix + '-fprofile-generate'],
-            gen_ldflags=gen_ldflags,
-            use_cflags=[prefix + '-fprofile-use=%s' % profdata,
-                        # Some error messages about mismatched profile data
-                        # come in via -Wbackend-plugin, so disable those too.
-                        '-Wno-error=backend-plugin'],
-            use_ldflags=[],
-        )
-
-
-set_config('PROFILE_GEN_CFLAGS', pgo_flags.gen_cflags)
-set_config('PROFILE_GEN_LDFLAGS', pgo_flags.gen_ldflags)
-set_config('PROFILE_USE_CFLAGS', pgo_flags.use_cflags)
-set_config('PROFILE_USE_LDFLAGS', pgo_flags.use_ldflags)
-
-
 @depends(c_compiler)
 def preprocess_option(compiler):
     # The uses of PREPROCESS_OPTION depend on the spacing for -o/-Fi.
     if compiler.type in ('gcc', 'clang'):
         return '-E -o '
     else:
         return '-P -Fi'
 
@@ -1477,159 +1429,16 @@ set_config('PREPROCESS_OPTION', preproce
 
 @depends(target, host)
 def is_windows(target, host):
     return host.kernel == 'WINNT' and target.kernel == 'WINNT'
 
 
 include('windows.configure', when=is_windows)
 
-# PGO
-# ==============================================================
-llvm_profdata = check_prog('LLVM_PROFDATA', ['llvm-profdata'],
-                           allow_missing=True,
-                           paths=toolchain_search_path)
-
-js_option('--enable-profile-generate',
-          help='Build a PGO instrumented binary')
-
-imply_option('MOZ_PGO',
-             depends_if('--enable-profile-generate')(lambda _: True))
-
-set_config('MOZ_PROFILE_GENERATE',
-           depends_if('--enable-profile-generate')(lambda _: True))
-
-set_define('MOZ_PROFILE_GENERATE',
-           depends_if('--enable-profile-generate')(lambda _: True))
-
-js_option('--enable-profile-use',
-          help='Use a generated profile during the build')
-
-js_option('--with-pgo-profile-path',
-          help='Path to the directory with unmerged profile data to use during the build',
-          nargs=1)
-
-imply_option('MOZ_PGO',
-             depends_if('--enable-profile-use')(lambda _: True))
-
-set_config('MOZ_PROFILE_USE',
-           depends_if('--enable-profile-use')(lambda _: True))
-
-
-@depends('--with-pgo-profile-path', '--enable-profile-use', llvm_profdata)
-@imports('os')
-def pgo_profile_path(path, pgo_use, profdata):
-    if not path:
-        return
-    if path and not pgo_use:
-        die('Pass --enable-profile-use to use --with-pgo-profile-path.')
-    if path and not profdata:
-        die('LLVM_PROFDATA must be set to process the pgo profile.')
-    if not os.path.isdir(path[0]):
-        die('Argument to --with-pgo-profile-path must be a directory.')
-    if not os.path.isabs(path[0]):
-        die('Argument to --with-pgo-profile-path must be an absolute path.')
-    return path[0]
-
-
-set_config('PGO_PROFILE_PATH', pgo_profile_path)
-
-option('--with-pgo-jarlog',
-       help='Use the provided jarlog file when packaging during a profile-use '
-            'build',
-       nargs=1)
-
-set_config('PGO_JARLOG_PATH', depends_if('--with-pgo-jarlog')(lambda p: p))
-
-# LTO
-# ==============================================================
-
-js_option('--enable-lto',
-          env='MOZ_LTO',
-          nargs='?',
-          choices=('full', 'thin', 'cross'),
-          help='Enable LTO')
-
-js_option(env='MOZ_LD64_KNOWN_GOOD',
-          nargs=1,
-          help='Indicate that ld64 is free of symbol aliasing bugs.')
-
-imply_option('MOZ_LD64_KNOWN_GOOD', depends_if('MOZ_AUTOMATION')(lambda _: True))
-
-@depends('--enable-lto', 'MOZ_PGO', '--enable-profile-generate', c_compiler,
-         'MOZ_LD64_KNOWN_GOOD', target)
-@imports('multiprocessing')
-def lto(value, pgo, profile_generate, c_compiler, ld64_known_good, target):
-    cflags = []
-    ldflags = []
-    enabled = None
-    rust_lto = False
-
-    # MSVC's implementation of PGO implies LTO. Make clang-cl match this.
-    if c_compiler.type == 'clang-cl' and pgo and not profile_generate and value.origin == 'default':
-        value = ['cross']
-
-    if value:
-        enabled = True
-        # `cross` implies `thin`, but with Rust code participating in LTO
-        # as well.  Make that a little more explicit.
-        if len(value) and value[0].lower() == 'cross':
-            if c_compiler.type == 'gcc':
-                die('Cross-language LTO is not supported with GCC.')
-
-            rust_lto = True
-            value = ['thin']
-
-        if target.kernel == 'Darwin' and target.os == 'OSX' \
-           and value[0].lower() == 'cross' and not ld64_known_good:
-            die('The Mac linker is known to have a bug that affects cross-language '
-                'LTO.  If you know that your linker is free from this bug, please '
-                'set the environment variable `MOZ_LD64_KNOWN_GOOD=1` and re-run '
-                'configure.')
-
-        if c_compiler.type == 'clang':
-            if len(value) and value[0].lower() == 'full':
-                cflags.append("-flto")
-                ldflags.append("-flto")
-            else:
-                cflags.append("-flto=thin")
-                ldflags.append("-flto=thin")
-        elif c_compiler.type == 'clang-cl':
-            if len(value) and value[0].lower() == 'full':
-                cflags.append("-flto")
-            else:
-                cflags.append("-flto=thin")
-            # With clang-cl, -flto can only be used with -c or -fuse-ld=lld.
-            # AC_TRY_LINKs during configure don't have -c, so pass -fuse-ld=lld.
-            cflags.append("-fuse-ld=lld");
-        else:
-            num_cores = multiprocessing.cpu_count()
-            cflags.append("-flto")
-            cflags.append("-flifetime-dse=1")
-
-            ldflags.append("-flto=%s" % num_cores)
-            ldflags.append("-flifetime-dse=1")
-
-    return namespace(
-        enabled=enabled,
-        cflags=cflags,
-        ldflags=ldflags,
-        rust_lto=rust_lto,
-    )
-
-
-add_old_configure_assignment('MOZ_LTO', lto.enabled)
-set_config('MOZ_LTO', lto.enabled)
-set_define('MOZ_LTO', lto.enabled)
-set_config('MOZ_LTO_CFLAGS', lto.cflags)
-set_config('MOZ_LTO_LDFLAGS', lto.ldflags)
-set_config('MOZ_LTO_RUST', lto.rust_lto)
-add_old_configure_assignment('MOZ_LTO_CFLAGS', lto.cflags)
-add_old_configure_assignment('MOZ_LTO_LDFLAGS', lto.ldflags)
-
 # ASAN
 # ==============================================================
 
 js_option('--enable-address-sanitizer', help='Enable Address Sanitizer')
 
 
 @depends(when='--enable-address-sanitizer')
 def asan():
--- a/config/makefiles/rust.mk
+++ b/config/makefiles/rust.mk
@@ -127,17 +127,21 @@ export CLANG_PATH=$(MOZ_CLANG_PATH)
 export PKG_CONFIG
 export PKG_CONFIG_ALLOW_CROSS=1
 export RUST_BACKTRACE=full
 export MOZ_TOPOBJDIR=$(topobjdir)
 
 target_rust_ltoable := force-cargo-library-build
 target_rust_nonltoable := force-cargo-test-run force-cargo-library-check $(foreach b,build check,force-cargo-program-$(b))
 
-$(target_rust_ltoable): RUSTFLAGS:=$(rustflags_override) $(RUSTFLAGS) $(if $(MOZ_LTO_RUST),-Clinker-plugin-lto)
+ifdef MOZ_PGO_RUST
+rust_pgo_flags := $(if $(MOZ_PROFILE_GENERATE),-C profile-generate=$(topobjdir)) $(if $(MOZ_PROFILE_USE),-C profile-use=$(topobjdir)/merged.profdata)
+endif
+
+$(target_rust_ltoable): RUSTFLAGS:=$(rustflags_override) $(RUSTFLAGS) $(if $(MOZ_LTO_RUST),-Clinker-plugin-lto) $(rust_pgo_flags)
 $(target_rust_nonltoable): RUSTFLAGS:=$(rustflags_override) $(RUSTFLAGS)
 
 TARGET_RECIPES := $(target_rust_ltoable) $(target_rust_nonltoable)
 
 HOST_RECIPES := \
   $(foreach a,library program,$(foreach b,build check,force-cargo-host-$(a)-$(b)))
 
 $(HOST_RECIPES): RUSTFLAGS:=$(rustflags_override)
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -20,17 +20,16 @@ collapseBreakpoints=Collapse Breakpoints
 
 # LOCALIZATION NOTE (copyToClipboard.label): This is the text that appears in the
 # context menu to copy the complete source of the open file.
 copyToClipboard.label=Copy to clipboard
 copyToClipboard.accesskey=C
 
 # LOCALIZATION NOTE (copySource.label): This is the text that appears in the
 # context menu to copy the selected source of file open.
-copySource=Copy
 copySource.label=Copy source text
 copySource.accesskey=y
 
 # LOCALIZATION NOTE (copySourceUri2): This is the text that appears in the
 # context menu to copy the source URI of file open.
 copySourceUri2=Copy source URI
 copySourceUri2.accesskey=u
 
@@ -45,17 +44,16 @@ expandAll.label=Expand all
 # LOCALIZATION NOTE (setDirectoryRoot.label): This is the text that appears in the
 # context menu to set a directory as root directory
 setDirectoryRoot.label=Set directory root
 setDirectoryRoot.accesskey=r
 
 # LOCALIZATION NOTE (removeDirectoryRoot.label): This is the text that appears in the
 # context menu to remove a directory as root directory
 removeDirectoryRoot.label=Remove directory root
-removeDirectoryRoot.accesskey=d
 
 # LOCALIZATION NOTE (copyFunction.label): This is the text that appears in the
 # context menu to copy the function the user selected
 copyFunction.label=Copy function
 copyFunction.accesskey=F
 
 # LOCALIZATION NOTE (copyStackTrace): This is the text that appears in the
 # context menu to copy the stack trace methods, file names and row number.
@@ -101,121 +99,43 @@ stepOutTooltip=Step out %S
 # LOCALIZATION NOTE (skipPausingTooltip.label): The tooltip text for disabling all
 # breakpoints and pausing triggers
 skipPausingTooltip.label=Deactivate breakpoints
 
 # LOCALIZATION NOTE (undoSkipPausingTooltip.label): The tooltip text for enabling all
 # breakpoints and pausing triggers
 undoSkipPausingTooltip.label=Activate breakpoints
 
-# LOCALIZATION NOTE (pauseButtonItem): The label that is displayed for the dropdown pause
-# list item when the debugger is in a running state.
-pauseButtonItem=Pause on Next Statement
-
-# LOCALIZATION NOTE (ignoreExceptionsItem): The pause on exceptions button description
-# when the debugger will not pause on exceptions.
-ignoreExceptionsItem=Ignore exceptions
-
-# LOCALIZATION NOTE (pauseOnUncaughtExceptionsItem): The pause on exceptions dropdown
-# item shown when a user is adding a new breakpoint.
-pauseOnUncaughtExceptionsItem=Pause on uncaught exceptions
-
 # LOCALIZATION NOTE (pauseOnExceptionsItem2): The pause on exceptions checkbox description
 # when the debugger will pause on all exceptions.
 pauseOnExceptionsItem2=Pause on exceptions
 
-# LOCALIZATION NOTE (ignoreCaughtExceptionsItem): The pause on exceptions checkbox description
-# when the debugger will not pause on any caught exception
-ignoreCaughtExceptionsItem=Ignore caught exceptions
-
 # LOCALIZATION NOTE (pauseOnCaughtExceptionsItem): The pause on exceptions checkbox description
 # when the debugger should pause on caught exceptions
 pauseOnCaughtExceptionsItem=Pause on caught exceptions
 
 # LOCALIZATION NOTE (workersHeader): The text to display in the events
 # header.
 workersHeader=Workers
 
 # LOCALIZATION NOTE (threadsHeader): The text to describe the threads header
 threadsHeader=Threads
 
 # LOCALIZATION NOTE (mainThread): The text to describe the thread of the
 # program as opposed to worker threads.
 mainThread=Main Thread
 
-# LOCALIZATION NOTE (noWorkersText): The text to display in the workers list
-# when there are no workers.
-noWorkersText=This page has no workers.
-
 # LOCALIZATION NOTE (noSourcesText): The text to display in the sources list
 # when there are no sources.
 noSourcesText=This page has no sources.
 
-# LOCALIZATION NOTE (noEventListenersText): The text to display in the events tab
-# when there are no events.
-noEventListenersText=No event listeners to display.
-
 # LOCALIZATION NOTE (eventListenersHeader1): The text to display in the events
 # header.
 eventListenersHeader1=Event Listener Breakpoints
 
-# LOCALIZATION NOTE (noStackFramesText): The text to display in the call stack tab
-# when there are no stack frames.
-noStackFramesText=No stack frames to display
-
-# LOCALIZATION NOTE (eventCheckboxTooltip): The tooltip text to display when
-# the user hovers over the checkbox used to toggle an event breakpoint.
-eventCheckboxTooltip=Toggle breaking on this event
-
-# LOCALIZATION NOTE (eventOnSelector): The text to display in the events tab
-# for every event item, between the event type and event selector.
-eventOnSelector=on
-
-# LOCALIZATION NOTE (eventInSource): The text to display in the events tab
-# for every event item, between the event selector and listener's owner source.
-eventInSource=in
-
-# LOCALIZATION NOTE (eventNodes): The text to display in the events tab when
-# an event is listened on more than one target node.
-eventNodes=%S nodes
-
-# LOCALIZATION NOTE (eventNative): The text to display in the events tab when
-# a listener is added from plugins, thus getting translated to native code.
-eventNative=[native code]
-
-# LOCALIZATION NOTE (*Events): The text to display in the events tab for
-# each group of sub-level event entries.
-animationEvents=Animation
-audioEvents=Audio
-batteryEvents=Battery
-clipboardEvents=Clipboard
-compositionEvents=Composition
-deviceEvents=Device
-displayEvents=Display
-dragAndDropEvents=Drag and Drop
-gamepadEvents=Gamepad
-indexedDBEvents=IndexedDB
-interactionEvents=Interaction
-keyboardEvents=Keyboard
-mediaEvents=HTML5 Media
-mouseEvents=Mouse
-mutationEvents=Mutation
-navigationEvents=Navigation
-pointerLockEvents=Pointer Lock
-sensorEvents=Sensor
-storageEvents=Storage
-timeEvents=Time
-touchEvents=Touch
-otherEvents=Other
-
-# LOCALIZATION NOTE (blackboxCheckboxTooltip2): The tooltip text to display when
-# the user hovers over the checkbox used to toggle blackboxing its associated
-# source.
-blackboxCheckboxTooltip2=Toggle blackboxing
-
 # LOCALIZATION NOTE (sources.search.key2): Key shortcut to open the search for
 # searching all the source files the debugger has seen.
 # Do not localize "CmdOrCtrl+P", or change the format of the string. These are
 # key identifiers, not messages displayed to the user.
 sources.search.key2=CmdOrCtrl+P
 
 # LOCALIZATION NOTE (sources.search.alt.key): A second key shortcut to open the
 # search for searching all the source files the debugger has seen.
@@ -278,24 +198,16 @@ shortcuts.header.search=Search
 # LOCALIZATION NOTE (projectTextSearch.placeholder): A placeholder shown
 # when searching across all of the files in a project.
 projectTextSearch.placeholder=Find in files…
 
 # LOCALIZATION NOTE (projectTextSearch.noResults): The center pane Text Search
 # message when the query did not match any text of all files in a project.
 projectTextSearch.noResults=No results found
 
-# LOCALIZATION NOTE (sources.noSourcesAvailable): Text shown when the debugger
-# does not have any sources.
-sources.noSourcesAvailable=This page has no sources
-
-# LOCALIZATION NOTE (sources.noSourcesAvailableRoot): Text shown when the debugger
-# does not have any sources under a specific directory root.
-sources.noSourcesAvailableRoot=This directory root has no sources
-
 # LOCALIZATION NOTE (sourceSearch.search.key2): Key shortcut to open the search
 # for searching within a the currently opened files in the editor
 # Do not localize "CmdOrCtrl+F", or change the format of the string. These are
 # key identifiers, not messages displayed to the user.
 sourceSearch.search.key2=CmdOrCtrl+F
 
 # LOCALIZATION NOTE (sourceSearch.search.placeholder): placeholder text in
 # the source search input bar
@@ -317,72 +229,27 @@ sourceSearch.search.again.key3=Cmd+G
 # key identifiers, not messages displayed to the user.
 sourceSearch.search.againPrev.key3=Cmd+Shift+G
 
 # LOCALIZATION NOTE (sourceSearch.resultsSummary2): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Shows a summary of the number of matches for autocomplete
 sourceSearch.resultsSummary2=#1 result;#1 results
 
-# LOCALIZATION NOTE (noMatchingStringsText): The text to display in the
-# global search results when there are no matching strings after filtering.
-noMatchingStringsText=No matches found
-
-# LOCALIZATION NOTE (emptySearchText): This is the text that appears in the
-# filter text box when it is empty and the scripts container is selected.
-emptySearchText=Search scripts (%S)
-
-# LOCALIZATION NOTE (emptyVariablesFilterText): This is the text that
-# appears in the filter text box for the variables view container.
-emptyVariablesFilterText=Filter variables
-
-# LOCALIZATION NOTE (emptyPropertiesFilterText): This is the text that
-# appears in the filter text box for the editor's variables view bubble.
-emptyPropertiesFilterText=Filter properties
-
-# LOCALIZATION NOTE (searchPanelFilter): This is the text that appears in the
-# filter panel popup for the filter scripts operation.
-searchPanelFilter=Filter scripts (%S)
-
-# LOCALIZATION NOTE (searchPanelGlobal): This is the text that appears in the
-# filter panel popup for the global search operation.
-searchPanelGlobal=Search in all files (%S)
-
-# LOCALIZATION NOTE (searchPanelFunction): This is the text that appears in the
-# filter panel popup for the function search operation.
-searchPanelFunction=Search for function definition (%S)
-
-# LOCALIZATION NOTE (searchPanelFunction2): This is the text that appears in the
-# filter panel popup for the function search operation.
-searchPanelFunction2=Find function definition (%S)
-
-# LOCALIZATION NOTE (searchPanelToken): This is the text that appears in the
-# filter panel popup for the token search operation.
-searchPanelToken=Find in this file (%S)
-
-# LOCALIZATION NOTE (searchPanelGoToLine): This is the text that appears in the
-# filter panel popup for the line search operation.
-searchPanelGoToLine=Go to line (%S)
-
-# LOCALIZATION NOTE (searchPanelVariable): This is the text that appears in the
-# filter panel popup for the variables search operation.
-searchPanelVariable=Filter variables (%S)
-
 # LOCALIZATION NOTE (breakpointHeadingMenuItem.*): The text for all the elements
 # that are displayed in the breakpoint headings menu item popup.
 breakpointHeadingsMenuItem.enableInSource.label=Enable breakpoints
 breakpointHeadingsMenuItem.enableInSource.accesskey=E
 breakpointHeadingsMenuItem.disableInSource.label=Disable breakpoints
 breakpointHeadingsMenuItem.disableInSource.accesskey=D
 breakpointHeadingsMenuItem.removeInSource.label=Remove breakpoints
 breakpointHeadingsMenuItem.removeInSource.accesskey=R
 
 # LOCALIZATION NOTE (breakpointMenuItem): The text for all the elements that
 # are displayed in the breakpoints menu item popup.
-breakpointMenuItem.setConditional=Configure conditional breakpoint
 breakpointMenuItem.enableSelf2.label=Enable
 breakpointMenuItem.enableSelf2.accesskey=E
 breakpointMenuItem.disableSelf2.label=Disable
 breakpointMenuItem.disableSelf2.accesskey=D
 breakpointMenuItem.deleteSelf2.label=Remove
 breakpointMenuItem.deleteSelf2.accesskey=R
 breakpointMenuItem.enableOthers2.label=Enable others
 breakpointMenuItem.enableOthers2.accesskey=o
@@ -398,51 +265,34 @@ breakpointMenuItem.deleteAll2.label=Remo
 breakpointMenuItem.deleteAll2.accesskey=a
 breakpointMenuItem.removeCondition2.label=Remove condition
 breakpointMenuItem.removeCondition2.accesskey=c
 breakpointMenuItem.addCondition2.label=Add condition
 breakpointMenuItem.addCondition2.accesskey=A
 breakpointMenuItem.editCondition2.label=Edit condition
 breakpointMenuItem.editCondition2.accesskey=n
 breakpointMenuItem.enableSelf=Enable breakpoint
-breakpointMenuItem.enableSelf.accesskey=E
 breakpointMenuItem.disableSelf=Disable breakpoint
-breakpointMenuItem.disableSelf.accesskey=D
 breakpointMenuItem.deleteSelf=Remove breakpoint
-breakpointMenuItem.deleteSelf.accesskey=R
 breakpointMenuItem.enableOthers=Enable others
-breakpointMenuItem.enableOthers.accesskey=o
 breakpointMenuItem.disableOthers=Disable others
-breakpointMenuItem.disableOthers.accesskey=s
 breakpointMenuItem.deleteOthers=Remove others
-breakpointMenuItem.deleteOthers.accesskey=h
 breakpointMenuItem.enableAll=Enable all breakpoints
-breakpointMenuItem.enableAll.accesskey=b
 breakpointMenuItem.disableAll=Disable all breakpoints
-breakpointMenuItem.disableAll.accesskey=k
 breakpointMenuItem.deleteAll=Remove all breakpoints
-breakpointMenuItem.deleteAll.accesskey=a
-breakpointMenuItem.removeCondition.label=Remove breakpoint condition
-breakpointMenuItem.removeCondition.accesskey=c
-breakpointMenuItem.editCondition.label=Edit breakpoint condition
-breakpointMenuItem.editCondition.accesskey=n
 breakpointMenuItem.disableAllAtLine.label=Disable breakpoints on line
 breakpointMenuItem.disableAllAtLine.accesskey=K
 breakpointMenuItem.enableAllAtLine.label=Enable breakpoints on line
 breakpointMenuItem.enableAllAtLine.accesskey=L
 breakpointMenuItem.removeAllAtLine.label=Remove breakpoints on line
 breakpointMenuItem.removeAllAtLine.accesskey=X
 
 # LOCALIZATION NOTE (breakpoints.header): Breakpoints right sidebar pane header.
 breakpoints.header=Breakpoints
 
-# LOCALIZATION NOTE (breakpoints.none): The text that appears when there are
-# no breakpoints present
-breakpoints.none=No breakpoints
-
 # LOCALIZATION NOTE (breakpoints.enable): The text that may appear as a tooltip
 # when hovering over the 'disable breakpoints' switch button in right sidebar
 breakpoints.enable=Enable breakpoints
 
 # LOCALIZATION NOTE (breakpoints.disable): The text that may appear as a tooltip
 # when hovering over the 'disable breakpoints' switch button in right sidebar
 breakpoints.disable=Disable breakpoints
 
@@ -474,44 +324,33 @@ callStack.group.expandTooltip=Show %S fr
 
 # LOCALIZATION NOTE (callStack.group.collapseTooltip): The text that will appear
 # when hovering an expanded Group of frames in the callStack panel. `frames` is
 # always plural since a group can only exist if it contain more that 1 frame.
 # %S is replaced by the name of the library of the frames in the group.
 # example: `Collapse React frames`.
 callStack.group.collapseTooltip=Collapse %S frames
 
-# LOCALIZATION NOTE (components.header): Header for the
-# Framework Components pane in the right sidebar.
-components.header=Components
-
 # LOCALIZATION NOTE (editor.searchResults1): Semi-colon list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Editor Search bar message to summarize the selected search result. e.g. 5 of 10 results.
 editor.searchResults1=%d of #1 result;%d of #1 results
 
-# LOCALIZATION NOTE (editor.singleResult): Copy shown when there is one result.
-editor.singleResult=1 result
-
 # LOCALIZATION NOTE (editor.noResultsFound): Editor Search bar message
 # for when no results found.
 editor.noResultsFound=No results found
 
 # LOCALIZATION NOTE (editor.searchResults.nextResult): Editor Search bar
 # tooltip for traversing to the Next Result
 editor.searchResults.nextResult=Next result
 
 # LOCALIZATION NOTE (editor.searchResults.prevResult): Editor Search bar
 # tooltip for traversing to the Previous Result
 editor.searchResults.prevResult=Previous result
 
-# LOCALIZATION NOTE (editor.searchTypeToggleTitle): Search bar title for
-# toggling search type buttons(function search, variable search)
-editor.searchTypeToggleTitle=Search for:
-
 # LOCALIZATION NOTE (editor.continueToHere.label): Editor gutter context
 # menu item for jumping to a new paused location
 editor.continueToHere.label=Continue to here
 editor.continueToHere.accesskey=H
 
 # LOCALIZATION NOTE (editor.addBreakpoint): Editor gutter context menu item
 # for adding a breakpoint on a line.
 editor.addBreakpoint=Add breakpoint
@@ -533,24 +372,16 @@ editor.removeBreakpoint=Remove breakpoin
 # menu item for adding a breakpoint condition on a line.
 editor.addConditionBreakpoint=Add condition
 editor.addConditionBreakpoint.accesskey=c
 
 # LOCALIZATION NOTE (editor.editConditionBreakpoint): Editor gutter context menu item
 # for setting a breakpoint condition on a line.
 editor.editConditionBreakpoint=Edit condition
 
-# LOCALIZATION NOTE (editor.addConditionalBreakpoint): Editor gutter context menu item
-# for creating a breakpoint with a condition
-editor.addConditionalBreakpoint=Add conditional breakpoint
-
-# LOCALIZATION NOTE (editor.addLogBreakpoint): Editor gutter context menu item
-# for creating a breakpoint with a log
-editor.addLogBreakpoint=Add log point
-
 # LOCALIZATION NOTE (editor.addLogPoint): Editor gutter context
 # menu item for adding a log point on a line.
 editor.addLogPoint=Add log
 editor.addLogPoint.accesskey=l
 
 # LOCALIZATION NOTE (editor.editLogPoint): Editor gutter context menu item
 # for editing a log point already set on a line.
 editor.editLogPoint=Edit log
@@ -564,20 +395,16 @@ editor.removeLogPoint.accesskey=V
 # LOCALIZATION NOTE (editor.conditionalPanel.placeholder2): Placeholder text for
 # input element inside ConditionalPanel component
 editor.conditionalPanel.placeholder2=Breakpoint condition, e.g. items.length > 0
 
 # LOCALIZATION NOTE (editor.conditionalPanel.logPoint.placeholder2): Placeholder text for
 # input element inside ConditionalPanel component when a log point is set
 editor.conditionalPanel.logPoint.placeholder2=Log message, e.g. displayName
 
-# LOCALIZATION NOTE (editor.conditionalPanel.close): Tooltip text for
-# close button inside ConditionalPanel component
-editor.conditionalPanel.close=Cancel edit breakpoint and close
-
 # LOCALIZATION NOTE (editor.jumpToMappedLocation1): Context menu item
 # for navigating to a source mapped location
 editor.jumpToMappedLocation1=Jump to %S location
 editor.jumpToMappedLocation1.accesskey=m
 
 # LOCALIZATION NOTE (downloadFile.label): Context menu item
 # for downloading a source's content
 downloadFile.label=Download file
@@ -603,17 +430,16 @@ original=original
 # input element
 expressions.placeholder=Add watch expression
 
 # LOCALIZATION NOTE (expressions.errorMsg): Error text for expression
 # input element
 expressions.errorMsg=Invalid expression…
 expressions.label=Add watch expression
 expressions.accesskey=e
-expressions.key=CmdOrCtrl+Shift+E
 expressions.remove.tooltip=Remove watch expression
 
 # LOCALIZATION NOTE (xhrBreakpoints.header): The pause on any XHR breakpoints headings
 xhrBreakpoints.header=XHR Breakpoints
 xhrBreakpoints.placeholder=Break when URL contains
 xhrBreakpoints.label=Add XHR breakpoint
 
 # LOCALIZATION NOTE (xhrBreakpoints.item.label): message displayed when reaching a breakpoint for XHR requests. %S is replaced by the path provided as condition for the breakpoint.
@@ -657,34 +483,29 @@ sourceTabs.prettyPrint.accesskey=p
 # LOCALIZATION NOTE (sourceFooter.blackbox): Tooltip text associated
 # with the blackbox button
 sourceFooter.blackbox=Blackbox source
 sourceFooter.blackbox.accesskey=B
 
 # LOCALIZATION NOTE (sourceFooter.unblackbox): Tooltip text associated
 # with the blackbox button
 sourceFooter.unblackbox=Unblackbox source
-sourceFooter.unblackbox.accesskey=b
 
 # 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.mappedSuffix): Text associated
 # with a mapped source.  Displays next to URLs in tree and tabs.
 sourceFooter.mappedSuffix=(mapped)
 
-# LOCALIZATION NOTE (sourceFooter.codeCoverage): Text associated
-# with a code coverage button
-sourceFooter.codeCoverage=Code coverage
-
 # LOCALIZATION NOTE (sourceFooter.currentCursorPosition): Text associated
 # with the current cursor line and column
 sourceFooter.currentCursorPosition=(%1$S, %2$S)
 
 # LOCALIZATION NOTE (sourceFooter.currentCursorPosition.tooltip): Text associated
 # with the current cursor line and column
 sourceFooter.currentCursorPosition.tooltip=(Line %1$S, column %2$S)
 
@@ -719,19 +540,16 @@ scopes.map.label=Map
 scopes.block=Block
 
 # LOCALIZATION NOTE (sources.header): Sources left sidebar header
 sources.header=Sources
 
 # LOCALIZATION NOTE (outline.header): Outline left sidebar header
 outline.header=Outline
 
-# LOCALIZATION NOTE (scopes.mapScopes): Label for toggling scope mappings
-scopes.mapScopes=Map Scopes
-
 # LOCALIZATION NOTE (outline.placeholder): Placeholder text for the filter input
 # element
 outline.placeholder=Filter functions
 
 # LOCALIZATION NOTE (outline.sortLabel): Label for the sort button
 outline.sortLabel=Sort by name
 
 # LOCALIZATION NOTE (outline.noFunctions): Outline text when there are no functions to display
@@ -768,44 +586,28 @@ welcome.search2=%S Go to file
 # a mac we use the unicode character.
 welcome.findInFiles=%S to find in files
 
 # LOCALIZATION NOTE (welcome.findInFiles2): The center pane welcome panel's
 # search prompt. e.g. cmd+f to search for files. On windows, it's ctrl+shift+f, on
 # a mac we use the unicode character.
 welcome.findInFiles2=%S Find in files
 
-# LOCALIZATION NOTE (welcome.searchFunction): Label displayed in the welcome
-# panel. %S is replaced by the keyboard shortcut to search for functions.
-welcome.searchFunction=%S to search for functions in file
-
 # LOCALIZATION NOTE (welcome.allShortcuts): The label to open the modal of
 # shortcuts, displayed in the welcome panel.
 welcome.allShortcuts=Show all shortcuts
 
 # LOCALIZATION NOTE (sourceSearch.search): The center pane Source Search
 # prompt for searching for files.
 sourceSearch.search=Search sources…
 
 # LOCALIZATION NOTE (sourceSearch.search2): The center pane Source Search
 # prompt for searching for files.
 sourceSearch.search2=Go to file…
 
-# LOCALIZATION NOTE (sourceSearch.noResults2): The center pane Source Search
-# message when the query did not match any of the sources.
-sourceSearch.noResults2=No results found
-
-# LOCALIZATION NOTE (ignoreExceptions): The pause on exceptions button tooltip
-# when the debugger will not pause on exceptions.
-ignoreExceptions=Ignore exceptions. Click to pause on uncaught exceptions
-
-# LOCALIZATION NOTE (pauseOnUncaughtExceptions): The pause on exceptions button
-# tooltip when the debugger will pause on uncaught exceptions.
-pauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all exceptions
-
 # LOCALIZATION NOTE (pauseOnExceptions): The pause on exceptions button tooltip
 # when the debugger will pause on all exceptions.
 pauseOnExceptions=Pause on all exceptions. Click to ignore exceptions
 
 # LOCALIZATION NOTE (loadingText): The text that is displayed in the script
 # editor when the loading process has started but there is no file to display
 # yet.
 loadingText=Loading\u2026
@@ -813,111 +615,16 @@ loadingText=Loading\u2026
 # LOCALIZATION NOTE (wasmIsNotAvailable): The text that is displayed in the
 # script editor when the WebAssembly source is not available.
 wasmIsNotAvailable=Please refresh to debug this module
 
 # LOCALIZATION NOTE (errorLoadingText3): The text that is displayed in the debugger
 # viewer when there is an error loading a file
 errorLoadingText3=Error loading this URI: %S
 
-# LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the
-# watch expressions list to add a new item.
-addWatchExpressionText=Add watch expression
-
-# LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the
-# variables view popup.
-addWatchExpressionButton=Watch
-
-# LOCALIZATION NOTE (extensionsText): The text that is displayed to represent
-# "moz-extension" directories in the source tree
-extensionsText=Extensions
-
-# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the
-# variables pane when there are no variables to display.
-emptyVariablesText=No variables to display
-
-# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables
-# pane as a header for each variable scope (e.g. "Global scope, "With scope",
-# etc.).
-scopeLabel=%S scope
-
-# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch
-# expressions scope. This text is displayed in the variables pane as a header for
-# the watch expressions scope.
-watchExpressionsScopeLabel=Watch expressions
-
-# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text
-# is added to scopeLabel and displayed in the variables pane as a header for
-# the global scope.
-globalScopeLabel=Global
-
-# LOCALIZATION NOTE (variablesViewErrorStacktrace): This is the text that is
-# shown before the stack trace in an error.
-variablesViewErrorStacktrace=Stack trace:
-
-# LOCALIZATION NOTE (variablesViewMoreObjects): the text that is displayed
-# when you have an object preview that does not show all of the elements. At the end of the list
-# you see "N more..." in the web console output.
-# This is a semi-colon list of plural forms.
-# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
-# #1 number of remaining items in the object
-# example: 3 more…
-variablesViewMoreObjects=#1 more…;#1 more…
-
-# LOCALIZATION NOTE (variablesEditableNameTooltip): The text that is displayed
-# in the variables list on an item with an editable name.
-variablesEditableNameTooltip=Double click to edit
-
-# LOCALIZATION NOTE (variablesEditableValueTooltip): The text that is displayed
-# in the variables list on an item with an editable value.
-variablesEditableValueTooltip=Click to change value
-
-# LOCALIZATION NOTE (variablesCloseButtonTooltip): The text that is displayed
-# in the variables list on an item which can be removed.
-variablesCloseButtonTooltip=Click to remove
-
-# LOCALIZATION NOTE (variablesEditButtonTooltip): The text that is displayed
-# in the variables list on a getter or setter which can be edited.
-variablesEditButtonTooltip=Click to set value
-
-# LOCALIZATION NOTE (variablesDomNodeValueTooltip): The text that is displayed
-# in a tooltip on the "open in inspector" button in the the variables list for a
-# DOMNode item.
-variablesDomNodeValueTooltip=Click to select the node in the inspector
-
-# LOCALIZATION NOTE (configurable|...|Tooltip): The text that is displayed
-# in the variables list on certain variables or properties as tooltips.
-# Explanations of what these represent can be found at the following links:
-# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
-# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
-# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen
-# https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/isSealed
-# It's probably best to keep these in English.
-configurableTooltip=configurable
-enumerableTooltip=enumerable
-writableTooltip=writable
-frozenTooltip=frozen
-sealedTooltip=sealed
-extensibleTooltip=extensible
-overriddenTooltip=overridden
-WebIDLTooltip=WebIDL
-
-# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
-# in the variables list as a separator between the name and value.
-variablesSeparatorLabel=:
-
-# LOCALIZATION NOTE (watchExpressionsSeparatorLabel2): The text that is displayed
-# in the watch expressions list as a separator between the code and evaluation.
-watchExpressionsSeparatorLabel2=\u0020→
-
-# LOCALIZATION NOTE (functionSearchSeparatorLabel): The text that is displayed
-# in the functions search panel as a separator between function's inferred name
-# and its real name (if available).
-functionSearchSeparatorLabel=←
-
 # LOCALIZATION NOTE(gotoLineModal.placeholder): The placeholder
 # text displayed when the user searches for specific lines in a file
 gotoLineModal.placeholder=Go to line…
 
 # LOCALIZATION NOTE(gotoLineModal.title): The message shown to users
 # to open the go to line modal
 gotoLineModal.title=Go to a line number in a file
 
@@ -954,29 +661,16 @@ symbolSearch.searchModifier.regex=Regex
 # LOCALIZATION NOTE(symbolSearch.searchModifier.caseSensitive): A search option
 # when searching text in a file
 symbolSearch.searchModifier.caseSensitive=Case sensitive
 
 # LOCALIZATION NOTE(symbolSearch.searchModifier.wholeWord): A search option
 # when searching text in a file
 symbolSearch.searchModifier.wholeWord=Whole word
 
-# LOCALIZATION NOTE (resumptionOrderPanelTitle): This is the text that appears
-# as a description in the notification panel popup, when multiple debuggers are
-# open in separate tabs and the user tries to resume them in the wrong order.
-# The substitution parameter is the URL of the last paused window that must be
-# resumed first.
-resumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S
-
-variablesViewOptimizedOut=(optimized away)
-variablesViewUninitialized=(uninitialized)
-variablesViewMissingArgs=(unavailable)
-
-anonymousSourcesLabel=Anonymous sources
-
 experimental=This is an experimental feature
 
 # LOCALIZATION NOTE (whyPaused.debuggerStatement): The text that is displayed
 # in a info block explaining how the debugger is currently paused due to a `debugger`
 # statement in the code
 whyPaused.debuggerStatement=Paused on debugger statement
 
 # LOCALIZATION NOTE (whyPaused.breakpoint): The text that is displayed
--- a/dom/base/nsContentSink.cpp
+++ b/dom/base/nsContentSink.cpp
@@ -49,17 +49,16 @@
 #include "nsIObserverService.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/ServiceWorkerDescriptor.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "nsParserConstants.h"
 #include "nsSandboxFlags.h"
 #include "Link.h"
 #include "HTMLLinkElement.h"
-
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::dom;
 
 LazyLogModule gContentSinkLogModuleInfo("nscontentsink");
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsContentSink)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsContentSink)
@@ -131,63 +130,16 @@ nsContentSink::nsContentSink()
 nsContentSink::~nsContentSink() {
   if (mDocument) {
     // Remove ourselves just to be safe, though we really should have
     // been removed in DidBuildModel if everything worked right.
     mDocument->RemoveObserver(this);
   }
 }
 
-bool nsContentSink::sNotifyOnTimer;
-int32_t nsContentSink::sBackoffCount;
-int32_t nsContentSink::sNotificationInterval;
-int32_t nsContentSink::sInteractiveDeflectCount;
-int32_t nsContentSink::sPerfDeflectCount;
-int32_t nsContentSink::sPendingEventMode;
-int32_t nsContentSink::sEventProbeRate;
-int32_t nsContentSink::sInteractiveParseTime;
-int32_t nsContentSink::sPerfParseTime;
-int32_t nsContentSink::sInteractiveTime;
-int32_t nsContentSink::sInitialPerfTime;
-int32_t nsContentSink::sEnablePerfMode;
-
-void nsContentSink::InitializeStatics() {
-  Preferences::AddBoolVarCache(&sNotifyOnTimer, "content.notify.ontimer", true);
-  // -1 means never.
-  Preferences::AddIntVarCache(&sBackoffCount, "content.notify.backoffcount",
-                              -1);
-  // The gNotificationInterval has a dramatic effect on how long it
-  // takes to initially display content for slow connections.
-  // The current value provides good
-  // incremental display of content without causing an increase
-  // in page load time. If this value is set below 1/10 of second
-  // it starts to impact page load performance.
-  // see bugzilla bug 72138 for more info.
-  Preferences::AddIntVarCache(&sNotificationInterval, "content.notify.interval",
-                              120000);
-  Preferences::AddIntVarCache(&sInteractiveDeflectCount,
-                              "content.sink.interactive_deflect_count", 0);
-  Preferences::AddIntVarCache(&sPerfDeflectCount,
-                              "content.sink.perf_deflect_count", 200);
-  Preferences::AddIntVarCache(&sPendingEventMode,
-                              "content.sink.pending_event_mode", 1);
-  Preferences::AddIntVarCache(&sEventProbeRate, "content.sink.event_probe_rate",
-                              1);
-  Preferences::AddIntVarCache(&sInteractiveParseTime,
-                              "content.sink.interactive_parse_time", 3000);
-  Preferences::AddIntVarCache(&sPerfParseTime, "content.sink.perf_parse_time",
-                              360000);
-  Preferences::AddIntVarCache(&sInteractiveTime,
-                              "content.sink.interactive_time", 750000);
-  Preferences::AddIntVarCache(&sInitialPerfTime,
-                              "content.sink.initial_perf_time", 2000000);
-  Preferences::AddIntVarCache(&sEnablePerfMode, "content.sink.enable_perf_mode",
-                              0);
-}
-
 nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI,
                              nsISupports* aContainer, nsIChannel* aChannel) {
   MOZ_ASSERT(aDoc, "null ptr");
   MOZ_ASSERT(aURI, "null ptr");
 
   if (!aDoc || !aURI) {
     return NS_ERROR_NULL_POINTER;
   }
@@ -208,20 +160,20 @@ nsresult nsContentSink::Init(Document* a
 
     ProcessHTTPHeaders(aChannel);
   }
 
   mCSSLoader = aDoc->CSSLoader();
 
   mNodeInfoManager = aDoc->NodeInfoManager();
 
-  mBackoffCount = sBackoffCount;
+  mBackoffCount = StaticPrefs::content_notify_backoffcount();
 
-  if (sEnablePerfMode != 0) {
-    mDynamicLowerValue = sEnablePerfMode == 1;
+  if (StaticPrefs::content_sink_enable_perf_mode() != 0) {
+    mDynamicLowerValue = StaticPrefs::content_sink_enable_perf_mode() == 1;
     FavorPerformanceHint(!mDynamicLowerValue, 0);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsContentSink::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
@@ -1266,18 +1218,18 @@ nsContentSink::Notify(nsITimer* timer) {
     ScrollToRef();
   }
 
   mNotificationTimer = nullptr;
   return NS_OK;
 }
 
 bool nsContentSink::IsTimeToNotify() {
-  if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
-      mInMonolithicContainer) {
+  if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted ||
+      !mBackoffCount || mInMonolithicContainer) {
     return false;
   }
 
   if (WaitForPendingSheets()) {
     mDeferredFlushTags = true;
     return false;
   }
 
@@ -1297,17 +1249,17 @@ bool nsContentSink::IsTimeToNotify() {
 nsresult nsContentSink::WillInterruptImpl() {
   nsresult result = NS_OK;
 
   SINK_TRACE(static_cast<LogModule*>(gContentSinkLogModuleInfo),
              SINK_TRACE_CALLS, ("nsContentSink::WillInterrupt: this=%p", this));
 #ifndef SINK_NO_INCREMENTAL
   if (WaitForPendingSheets()) {
     mDeferredFlushTags = true;
-  } else if (sNotifyOnTimer && mLayoutStarted) {
+  } else if (StaticPrefs::content_notify_ontimer() && mLayoutStarted) {
     if (mBackoffCount && !mInMonolithicContainer) {
       int64_t now = PR_Now();
       int64_t interval = GetNotificationInterval();
       int64_t diff = now - mLastNotificationTime;
 
       // If it's already time for us to have a notification
       if (diff > interval || mDroppedTimer) {
         mBackoffCount--;
@@ -1374,33 +1326,36 @@ nsresult nsContentSink::DidProcessAToken
     // we're not laying anything out here.
     return NS_OK;
   }
 
   // Increase before comparing to gEventProbeRate
   ++mDeflectedCount;
 
   // Check if there's a pending event
-  if (sPendingEventMode != 0 && !mHasPendingEvent &&
-      (mDeflectedCount % sEventProbeRate) == 0) {
+  if (StaticPrefs::content_sink_pending_event_mode() != 0 &&
+      !mHasPendingEvent &&
+      (mDeflectedCount % StaticPrefs::content_sink_event_probe_rate()) == 0) {
     nsViewManager* vm = presShell->GetViewManager();
     NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
     nsCOMPtr<nsIWidget> widget;
     vm->GetRootWidget(getter_AddRefs(widget));
     mHasPendingEvent = widget && widget->HasPendingInputEvent();
   }
 
-  if (mHasPendingEvent && sPendingEventMode == 2) {
+  if (mHasPendingEvent && StaticPrefs::content_sink_pending_event_mode() == 2) {
     return NS_ERROR_HTMLPARSER_INTERRUPTED;
   }
 
   // Have we processed enough tokens to check time?
   if (!mHasPendingEvent &&
-      mDeflectedCount < uint32_t(mDynamicLowerValue ? sInteractiveDeflectCount
-                                                    : sPerfDeflectCount)) {
+      mDeflectedCount <
+          uint32_t(mDynamicLowerValue
+                       ? StaticPrefs::content_sink_interactive_deflect_count()
+                       : StaticPrefs::content_sink_perf_deflect_count())) {
     return NS_OK;
   }
 
   mDeflectedCount = 0;
 
   // Check if it's time to return to the main event loop
   if (PR_IntervalToMicroseconds(PR_IntervalNow()) > mCurrentParseEndTime) {
     return NS_ERROR_HTMLPARSER_INTERRUPTED;
@@ -1517,39 +1472,42 @@ nsresult nsContentSink::WillParseImpl(vo
 
   PresShell* presShell = mDocument->GetPresShell();
   if (!presShell) {
     return NS_OK;
   }
 
   uint32_t currentTime = PR_IntervalToMicroseconds(PR_IntervalNow());
 
-  if (sEnablePerfMode == 0) {
+  if (StaticPrefs::content_sink_enable_perf_mode() == 0) {
     nsViewManager* vm = presShell->GetViewManager();
     NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);
     uint32_t lastEventTime;
     vm->GetLastUserEventTime(lastEventTime);
 
     bool newDynLower =
         mDocument->IsInBackgroundWindow() ||
-        ((currentTime - mBeginLoadTime) > uint32_t(sInitialPerfTime) &&
-         (currentTime - lastEventTime) < uint32_t(sInteractiveTime));
+        ((currentTime - mBeginLoadTime) >
+             StaticPrefs::content_sink_initial_perf_time() &&
+         (currentTime - lastEventTime) <
+             StaticPrefs::content_sink_interactive_time());
 
     if (mDynamicLowerValue != newDynLower) {
       FavorPerformanceHint(!newDynLower, 0);
       mDynamicLowerValue = newDynLower;
     }
   }
 
   mDeflectedCount = 0;
   mHasPendingEvent = false;
 
   mCurrentParseEndTime =
-      currentTime +
-      (mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
+      currentTime + (mDynamicLowerValue
+                         ? StaticPrefs::content_sink_interactive_parse_time()
+                         : StaticPrefs::content_sink_perf_parse_time());
 
   return NS_OK;
 }
 
 void nsContentSink::WillBuildModelImpl() {
   if (!mRunsToCompletion) {
     mDocument->BlockOnload();
     mIsBlockingOnload = true;
--- a/dom/base/nsContentSink.h
+++ b/dom/base/nsContentSink.h
@@ -22,16 +22,17 @@
 #include "nsAutoPtr.h"
 #include "nsGkAtoms.h"
 #include "nsITimer.h"
 #include "nsStubDocumentObserver.h"
 #include "nsIContentSink.h"
 #include "mozilla/Logging.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsThreadUtils.h"
+#include "mozilla/StaticPrefs_content.h"
 
 class nsIURI;
 class nsIChannel;
 class nsIDocShell;
 class nsAtom;
 class nsIChannel;
 class nsIContent;
 class nsNodeInfoManager;
@@ -112,18 +113,16 @@ class nsContentSink : public nsICSSLoade
   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
 
   virtual void UpdateChildCounts() = 0;
 
   bool IsTimeToNotify();
   bool LinkContextIsOurDocument(const nsAString& aAnchor);
   bool Decode5987Format(nsAString& aEncoded);
 
-  static void InitializeStatics();
-
  protected:
   nsContentSink();
   virtual ~nsContentSink();
 
   enum CacheSelectionAction {
     // There is no offline cache manifest specified by the document,
     // or the document was loaded from a cache other than the one it
     // specifies via its manifest attribute and IS NOT a top-level
@@ -251,28 +250,30 @@ class nsContentSink : public nsICSSLoade
  protected:
   void FavorPerformanceHint(bool perfOverStarvation, uint32_t starvationDelay);
 
   inline int32_t GetNotificationInterval() {
     if (mDynamicLowerValue) {
       return 1000;
     }
 
-    return sNotificationInterval;
+    return mozilla::StaticPrefs::content_notify_interval();
   }
 
   virtual nsresult FlushTags() = 0;
 
   // Later on we might want to make this more involved somehow
   // (e.g. stop waiting after some timeout or whatnot).
   bool WaitForPendingSheets() { return mPendingSheetCount > 0; }
 
   void DoProcessLinkHeader();
 
-  void StopDeflecting() { mDeflectedCount = sPerfDeflectCount; }
+  void StopDeflecting() {
+    mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count();
+  }
 
  protected:
   RefPtr<Document> mDocument;
   RefPtr<nsParserBase> mParser;
   nsCOMPtr<nsIURI> mDocumentURI;
   nsCOMPtr<nsIDocShell> mDocShell;
   RefPtr<mozilla::css::Loader> mCSSLoader;
   RefPtr<nsNodeInfoManager> mNodeInfoManager;
@@ -330,36 +331,11 @@ class nsContentSink : public nsICSSLoade
 
   int32_t mInNotification;
   uint32_t mUpdatesInNotification;
 
   uint32_t mPendingSheetCount;
 
   nsRevocableEventPtr<nsRunnableMethod<nsContentSink, void, false> >
       mProcessLinkHeaderEvent;
-
-  // Do we notify based on time?
-  static bool sNotifyOnTimer;
-  // Back off timer notification after count.
-  static int32_t sBackoffCount;
-  // Notification interval in microseconds
-  static int32_t sNotificationInterval;
-  // How many times to deflect in interactive/perf modes
-  static int32_t sInteractiveDeflectCount;
-  static int32_t sPerfDeflectCount;
-  // 0 = don't check for pending events
-  // 1 = don't deflect if there are pending events
-  // 2 = bail if there are pending events
-  static int32_t sPendingEventMode;
-  // How often to probe for pending events. 1=every token
-  static int32_t sEventProbeRate;
-  // How long to stay off the event loop in interactive/perf modes
-  static int32_t sInteractiveParseTime;
-  static int32_t sPerfParseTime;
-  // How long to be in interactive mode after an event
-  static int32_t sInteractiveTime;
-  // How long to stay in perf mode after initial loading
-  static int32_t sInitialPerfTime;
-  // Should we switch between perf-mode and interactive-mode
-  static int32_t sEnablePerfMode;
 };
 
 #endif  // _nsContentSink_h_
--- a/dom/base/nsDocumentEncoder.cpp
+++ b/dom/base/nsDocumentEncoder.cpp
@@ -36,16 +36,17 @@
 #include "nsUnicharUtils.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsIFrame.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/Comment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "mozilla/dom/ProcessingInstruction.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/dom/Text.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/ScopeExit.h"
 
 using namespace mozilla;
@@ -1722,19 +1723,18 @@ nsCOMPtr<nsINode> nsHTMLCopyEncoder::Get
   MOZ_ASSERT(content, "null content in nsHTMLCopyEncoder::GetChildAt");
 
   resultNode = content->GetChildAt_Deprecated(aOffset);
 
   return resultNode;
 }
 
 bool nsHTMLCopyEncoder::IsMozBR(Element* aElement) {
-  return aElement->IsHTMLElement(nsGkAtoms::br) &&
-         aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                               NS_LITERAL_STRING("_moz"), eIgnoreCase);
+  HTMLBRElement* brElement = HTMLBRElement::FromNodeOrNull(aElement);
+  return brElement && brElement->IsPaddingForEmptyLastLine();
 }
 
 nsresult nsHTMLCopyEncoder::GetNodeLocation(nsINode* inChild,
                                             nsCOMPtr<nsINode>* outParent,
                                             int32_t* outOffset) {
   NS_ASSERTION((inChild && outParent && outOffset), "bad args");
   if (inChild && outParent && outOffset) {
     nsCOMPtr<nsIContent> child = do_QueryInterface(inChild);
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -3731,17 +3731,28 @@ double nsGlobalWindowOuter::GetDevicePix
   }
 
   RefPtr<nsPresContext> presContext = mDocShell->GetPresContext();
   if (!presContext) {
     return 1.0;
   }
 
   if (nsContentUtils::ResistFingerprinting(aCallerType)) {
-    return 1.0;
+    // Spoofing the DevicePixelRatio causes blurriness in some situations
+    // on HiDPI displays. pdf.js is a non-system caller; but it can't
+    // expose the fingerprintable information, so we can safely disable
+    // spoofing in this situation. It doesn't address the issue for
+    // web-rendered content (including pdf.js instances on the web.)
+    // In the future we hope to have a better solution to fix all HiDPI
+    // blurriness...
+    nsAutoCString origin;
+    nsresult rv = this->GetPrincipal()->GetOrigin(origin);
+    if (NS_FAILED(rv) || origin != NS_LITERAL_CSTRING("resource://pdf.js")) {
+      return 1.0;
+    }
   }
 
   float overrideDPPX = presContext->GetOverrideDPPX();
 
   if (overrideDPPX > 0) {
     return overrideDPPX;
   }
 
--- a/dom/base/nsHTMLContentSerializer.cpp
+++ b/dom/base/nsHTMLContentSerializer.cpp
@@ -58,41 +58,30 @@ bool nsHTMLContentSerializer::SerializeH
     nsAString& aStr) {
   MaybeSerializeIsValue(aElement, aStr);
 
   int32_t count = aElement->GetAttrCount();
   if (!count) return true;
 
   nsresult rv;
   nsAutoString valueStr;
-  NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
 
   for (int32_t index = 0; index < count; index++) {
     const nsAttrName* name = aElement->GetAttrNameAt(index);
     int32_t namespaceID = name->NamespaceID();
     nsAtom* attrName = name->LocalName();
 
     // Filter out any attribute starting with [-|_]moz
     nsDependentAtomString attrNameStr(attrName);
     if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
         StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
       continue;
     }
     aElement->GetAttr(namespaceID, attrName, valueStr);
 
-    //
-    // Filter out special case of <br type="_moz"> or <br _moz*>,
-    // used by the editor.  Bug 16988.  Yuck.
-    //
-    if (aTagName == nsGkAtoms::br && aNamespace == kNameSpaceID_XHTML &&
-        attrName == nsGkAtoms::type && namespaceID == kNameSpaceID_None &&
-        StringBeginsWith(valueStr, _mozStr)) {
-      continue;
-    }
-
     if (mIsCopying && mIsFirstChildOfOL && aTagName == nsGkAtoms::li &&
         aNamespace == kNameSpaceID_XHTML && attrName == nsGkAtoms::value &&
         namespaceID == kNameSpaceID_None) {
       // This is handled separately in SerializeLIValueAttribute()
       continue;
     }
     bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
 
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -17,16 +17,17 @@
 #include "nsTextFragment.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsCRT.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/dom/CharacterData.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "mozilla/dom/Text.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/BinarySearch.h"
 #include "nsComputedDOMStyle.h"
 
 namespace mozilla {
 class Encoding;
 }
@@ -1034,19 +1035,22 @@ nsresult nsPlainTextSerializer::DoAddLea
 
   if (MustSuppressLeaf()) {
     return NS_OK;
   }
 
   if (aTag == nsGkAtoms::br) {
     // Another egregious editor workaround, see bug 38194:
     // ignore the bogus br tags that the editor sticks here and there.
-    nsAutoString tagAttr;
-    if (NS_FAILED(GetAttributeValue(nsGkAtoms::type, tagAttr)) ||
-        !tagAttr.EqualsLiteral("_moz")) {
+    // FYI: `brElement` may be `nullptr` if the element is <br> element
+    //      of non-HTML element.
+    // XXX Do we need to call `EnsureVerticalSpace()` when the <br> element
+    //     is not an HTML element?
+    HTMLBRElement* brElement = HTMLBRElement::FromNodeOrNull(mElement);
+    if (!brElement || !brElement->IsPaddingForEmptyLastLine()) {
       EnsureVerticalSpace(mEmptyLines + 1);
     }
   } else if (aTag == nsGkAtoms::hr &&
              (mFlags & nsIDocumentEncoder::OutputFormatted)) {
     EnsureVerticalSpace(0);
 
     // Make a line of dashes as wide as the wrap width
     // XXX honoring percentage would be nice
--- a/dom/base/nsXHTMLContentSerializer.cpp
+++ b/dom/base/nsXHTMLContentSerializer.cpp
@@ -203,18 +203,16 @@ bool nsXHTMLContentSerializer::Serialize
       // Serialize namespace decl
       NS_ENSURE_TRUE(
           SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true),
           false);
     }
     PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
   }
 
-  NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
-
   count = aElement->GetAttrCount();
 
   // Now serialize each of the attributes
   // XXX Unfortunately we need a namespace manager to get
   // attribute URIs.
   for (index = 0; index < count; index++) {
     if (aSkipAttr == index) {
       continue;
@@ -247,25 +245,16 @@ bool nsXHTMLContentSerializer::Serialize
     }
 
     info.mValue->ToString(valueStr);
 
     nsDependentAtomString nameStr(attrName);
     bool isJS = false;
 
     if (kNameSpaceID_XHTML == contentNamespaceID) {
-      //
-      // Filter out special case of <br type="_moz"> or <br _moz*>,
-      // used by the editor.  Bug 16988.  Yuck.
-      //
-      if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br &&
-          attrName == nsGkAtoms::type && StringBeginsWith(valueStr, _mozStr)) {
-        continue;
-      }
-
       if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li) &&
           (attrName == nsGkAtoms::value)) {
         // This is handled separately in SerializeLIValueAttribute()
         continue;
       }
 
       isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
 
--- a/dom/events/ContentEventHandler.cpp
+++ b/dom/events/ContentEventHandler.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/ContentIterator.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/RangeUtils.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "mozilla/dom/HTMLUnknownElement.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Text.h"
 #include "nsCaret.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsCopySupport.h"
 #include "nsElementTable.h"
@@ -478,30 +479,27 @@ nsresult ContentEventHandler::QueryConte
   // Returning empty rect may cause native IME confused, let's make sure to
   // return non-empty rect.
   EnsureNonEmptyRect(aEvent->mReply.mRect);
   aEvent->mSucceeded = true;
 
   return NS_OK;
 }
 
-// Editor places a bogus BR node under its root content if the editor doesn't
-// have any text. This happens even for single line editors.
+// Editor places a padding <br> element under its root content if the editor
+// doesn't have any text. This happens even for single line editors.
 // When we get text content and when we change the selection,
-// we don't want to include the bogus BRs at the end.
+// we don't want to include the padding <br> elements at the end.
 static bool IsContentBR(nsIContent* aContent) {
-  return aContent->IsHTMLElement(nsGkAtoms::br) &&
-         !aContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                             nsGkAtoms::moz, eIgnoreCase) &&
-         !aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
-                                             nsGkAtoms::mozeditorbogusnode,
-                                             nsGkAtoms::_true, eIgnoreCase);
+  HTMLBRElement* brElement = HTMLBRElement::FromNode(aContent);
+  return brElement && !brElement->IsPaddingForEmptyLastLine() &&
+         !brElement->IsPaddingForEmptyEditor();
 }
 
-static bool IsMozBR(nsIContent* aContent) {
+static bool IsPaddingBR(nsIContent* aContent) {
   return aContent->IsHTMLElement(nsGkAtoms::br) && !IsContentBR(aContent);
 }
 
 static void ConvertToNativeNewlines(nsString& aString) {
 #if defined(XP_WIN)
   aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
 #endif
 }
@@ -1479,17 +1477,17 @@ ContentEventHandler::GetFirstFrameInRang
         break;
       }
       continue;
     }
 
     // If the element node causes a line break before it, it's the first
     // node causing text.
     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
-        IsMozBR(node->AsContent())) {
+        IsPaddingBR(node->AsContent())) {
       nodePosition.Set(node, 0);
     }
   }
 
   if (!nodePosition.IsSet()) {
     return FrameAndNodeOffset();
   }
 
@@ -1566,17 +1564,17 @@ ContentEventHandler::GetLastFrameInRange
       // node position for returning its frame).
       if (!nodePosition.Offset()) {
         continue;
       }
       break;
     }
 
     if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
-        IsMozBR(node->AsContent())) {
+        IsPaddingBR(node->AsContent())) {
       nodePosition.Set(node, 0);
       break;
     }
   }
 
   if (!nodePosition.IsSet()) {
     return FrameAndNodeOffset();
   }
@@ -1618,17 +1616,17 @@ ContentEventHandler::GetLastFrameInRange
 }
 
 ContentEventHandler::FrameRelativeRect
 ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame) {
   // Note that this method should be called only with an element's frame whose
   // open tag causes a line break or moz-<br> for computing empty last line's
   // rect.
   MOZ_ASSERT(ShouldBreakLineBefore(aFrame->GetContent(), mRootContent) ||
-             IsMozBR(aFrame->GetContent()));
+             IsPaddingBR(aFrame->GetContent()));
 
   nsIFrame* frameForFontMetrics = aFrame;
 
   // If it's not a <br> frame, this method computes the line breaker's rect
   // outside the frame.  Therefore, we need to compute with parent frame's
   // font metrics in such case.
   if (!aFrame->IsBrFrame() && aFrame->GetParent()) {
     frameForFontMetrics = aFrame->GetParent();
@@ -1878,17 +1876,17 @@ nsresult ContentEventHandler::OnQueryTex
             rawRange.StartOffset() == rawRangeToPrevOffset.StartOffset();
       }
     }
     // Other contents should cause a line breaker rect before it.
     // Note that moz-<br> element does not cause any text, however,
     // it represents empty line at the last of current block.  Therefore,
     // we need to compute its rect too.
     else if (ShouldBreakLineBefore(firstContent, mRootContent) ||
-             IsMozBR(firstContent)) {
+             IsPaddingBR(firstContent)) {
       nsRect brRect;
       // If the frame is not a <br> frame, we need to compute the caret rect
       // with last character's rect before firstContent if there is.
       // For example, if caret is after "c" of |<p>abc</p><p>def</p>|, IME may
       // query a line breaker's rect after "c".  Then, if we compute it only
       // with the 2nd <p>'s block frame, the result will be:
       //  +-<p>--------------------------------+
       //  |abc                                 |
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -241,19 +241,22 @@ EVENT(stalled, eStalled, EventNameType_H
 EVENT(start, eMarqueeStart, EventNameType_HTMLMarqueeOnly, eBasicEventClass)
 EVENT(submit, eFormSubmit, EventNameType_HTMLXUL, eBasicEventClass)
 EVENT(suspend, eSuspend, EventNameType_HTML, eBasicEventClass)
 EVENT(timeupdate, eTimeUpdate, EventNameType_HTML, eBasicEventClass)
 EVENT(toggle, eToggle, EventNameType_HTML, eBasicEventClass)
 EVENT(volumechange, eVolumeChange, EventNameType_HTML, eBasicEventClass)
 EVENT(waiting, eWaiting, EventNameType_HTML, eBasicEventClass)
 EVENT(wheel, eWheel, EventNameType_All, eWheelEventClass)
-EVENT(copy, eCopy, EventNameType_HTMLXUL, eClipboardEventClass)
-EVENT(cut, eCut, EventNameType_HTMLXUL, eClipboardEventClass)
-EVENT(paste, ePaste, EventNameType_HTMLXUL, eClipboardEventClass)
+EVENT(copy, eCopy, EventNameType_HTMLXUL | EventNameType_SVGGraphic,
+      eClipboardEventClass)
+EVENT(cut, eCut, EventNameType_HTMLXUL | EventNameType_SVGGraphic,
+      eClipboardEventClass)
+EVENT(paste, ePaste, EventNameType_HTMLXUL | EventNameType_SVGGraphic,
+      eClipboardEventClass)
 // Gecko-specific extensions that apply to elements
 EVENT(beforescriptexecute, eBeforeScriptExecute, EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(afterscriptexecute, eAfterScriptExecute, EventNameType_HTMLXUL,
       eBasicEventClass)
 
 FORWARDED_EVENT(blur, eBlur, EventNameType_HTMLXUL, eFocusEventClass)
 ERROR_EVENT(error, eLoadError, EventNameType_All, eBasicEventClass)
--- a/dom/events/IMEContentObserver.cpp
+++ b/dom/events/IMEContentObserver.cpp
@@ -181,17 +181,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(IMEContentObserver)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
 
 IMEContentObserver::IMEContentObserver()
     : mESM(nullptr),
       mIMENotificationRequests(nullptr),
-      mPreAttrChangeLength(0),
       mSuppressNotifications(0),
       mPreCharacterDataChangeLength(-1),
       mSendingNotification(NOTIFY_IME_OF_NOTHING),
       mIsObserving(false),
       mIMEHasFocus(false),
       mNeedsToNotifyIMEOfFocusSet(false),
       mNeedsToNotifyIMEOfTextChange(false),
       mNeedsToNotifyIMEOfSelectionChange(false),
@@ -1080,61 +1079,16 @@ void IMEContentObserver::ContentRemoved(
   }
 
   TextChangeData data(offset, offset + textLength, offset,
                       IsEditorHandlingEventForComposition(),
                       IsEditorComposing());
   MaybeNotifyIMEOfTextChange(data);
 }
 
-void IMEContentObserver::AttributeWillChange(dom::Element* aElement,
-                                             int32_t aNameSpaceID,
-                                             nsAtom* aAttribute,
-                                             int32_t aModType) {
-  if (!NeedsTextChangeNotification()) {
-    return;
-  }
-
-  mPreAttrChangeLength =
-      ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
-}
-
-void IMEContentObserver::AttributeChanged(dom::Element* aElement,
-                                          int32_t aNameSpaceID,
-                                          nsAtom* aAttribute, int32_t aModType,
-                                          const nsAttrValue* aOldValue) {
-  if (!NeedsTextChangeNotification()) {
-    return;
-  }
-
-  mEndOfAddedTextCache.Clear();
-  mStartOfRemovingTextRangeCache.Clear();
-
-  uint32_t postAttrChangeLength =
-      ContentEventHandler::GetNativeTextLengthBefore(aElement, mRootContent);
-  if (postAttrChangeLength == mPreAttrChangeLength) {
-    return;
-  }
-  // First, compute text range which were added during a document change.
-  MaybeNotifyIMEOfAddedTextDuringDocumentChange();
-  // Then, compute the new text changed caused by this attribute change.
-  uint32_t start;
-  nsresult rv = ContentEventHandler::GetFlatTextLengthInRange(
-      NodePosition(mRootContent, 0), NodePositionBefore(aElement, 0),
-      mRootContent, &start, LINE_BREAK_TYPE_NATIVE);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  TextChangeData data(
-      start, start + mPreAttrChangeLength, start + postAttrChangeLength,
-      IsEditorHandlingEventForComposition(), IsEditorComposing());
-  MaybeNotifyIMEOfTextChange(data);
-}
-
 void IMEContentObserver::ClearAddedNodesDuringDocumentChange() {
   mFirstAddedContainer = mLastAddedContainer = nullptr;
   mFirstAddedContent = mLastAddedContent = nullptr;
   MOZ_LOG(sIMECOLog, LogLevel::Debug,
           ("0x%p IMEContentObserver::ClearAddedNodesDuringDocumentChange()"
            ", finished storing consecutive nodes",
            this));
 }
--- a/dom/events/IMEContentObserver.h
+++ b/dom/events/IMEContentObserver.h
@@ -52,18 +52,16 @@ class IMEContentObserver final : public 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(IMEContentObserver,
                                            nsIReflowObserver)
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATAWILLCHANGE
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
-  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
   NS_DECL_NSIREFLOWOBSERVER
 
   // nsIScrollObserver
   virtual void ScrollPositionChanged() override;
 
   /**
    * OnSelectionChange() is called when selection is changed in the editor.
    */
@@ -479,17 +477,16 @@ class IMEContentObserver final : public 
   // mSelectionData is the last selection data which was notified.  The
   // selection information is modified by UpdateSelectionCache().  The reason
   // of the selection change is modified by MaybeNotifyIMEOfSelectionChange().
   SelectionChangeData mSelectionData;
 
   EventStateManager* mESM;
 
   const IMENotificationRequests* mIMENotificationRequests;
-  uint32_t mPreAttrChangeLength;
   uint32_t mSuppressNotifications;
   int64_t mPreCharacterDataChangeLength;
 
   // mSendingNotification is a notification which is now sending from
   // IMENotificationSender.  When the value is NOTIFY_IME_OF_NOTHING, it's
   // not sending any notification.
   IMEMessage mSendingNotification;
 
--- a/dom/html/HTMLBRElement.h
+++ b/dom/html/HTMLBRElement.h
@@ -9,20 +9,39 @@
 
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 
 namespace mozilla {
 namespace dom {
 
+#define BR_ELEMENT_FLAG_BIT(n_) \
+  NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
+
+// BR element specific bits
+enum {
+  // NS_PADDING_FOR_EMPTY_EDITOR is set if the <br> element is created by
+  // editor for placing caret at proper position in empty editor.
+  NS_PADDING_FOR_EMPTY_EDITOR = BR_ELEMENT_FLAG_BIT(0),
+
+  // NS_PADDING_FOR_EMPTY_LAST_LINE is set if the <br> element is created by
+  // editor for placing caret at proper position for making empty last line
+  // in a block or <textarea> element visible.
+  NS_PADDING_FOR_EMPTY_LAST_LINE = BR_ELEMENT_FLAG_BIT(1),
+};
+
+ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
+
 class HTMLBRElement final : public nsGenericHTMLElement {
  public:
   explicit HTMLBRElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
+  NS_IMPL_FROMNODE_HTML_WITH_TAG(HTMLBRElement, br)
+
   virtual bool ParseAttribute(int32_t aNamespaceID, nsAtom* aAttribute,
                               const nsAString& aValue,
                               nsIPrincipal* aMaybeScriptedPrincipal,
                               nsAttrValue& aResult) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction()
       const override;
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
@@ -33,16 +52,23 @@ class HTMLBRElement final : public nsGen
   }
   void GetClear(DOMString& aClear) const {
     return GetHTMLAttr(nsGkAtoms::clear, aClear);
   }
 
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) override;
 
+  bool IsPaddingForEmptyEditor() const {
+    return HasFlag(NS_PADDING_FOR_EMPTY_EDITOR);
+  }
+  bool IsPaddingForEmptyLastLine() const {
+    return HasFlag(NS_PADDING_FOR_EMPTY_LAST_LINE);
+  }
+
  private:
   virtual ~HTMLBRElement();
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
                                     MappedDeclarations&);
 };
 
 }  // namespace dom
--- a/dom/webidl/HTMLBRElement.webidl
+++ b/dom/webidl/HTMLBRElement.webidl
@@ -17,8 +17,21 @@
 interface HTMLBRElement : HTMLElement {};
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLBRElement {
              [CEReactions, SetterThrows]
              attribute DOMString clear;
 };
 
+// Mozilla extensions
+
+partial interface HTMLBRElement {
+  // Set to true if the <br> element is created by editor for placing caret
+  // at proper position in empty editor.
+  [ChromeOnly]
+  readonly attribute boolean isPaddingForEmptyEditor;
+  // Set to true if the <br> element is created by editor for placing caret
+  // at proper position making last empty line in a block element in HTML
+  // editor or <textarea> element visible.
+  [ChromeOnly]
+  readonly attribute boolean isPaddingForEmptyLastLine;
+};
--- a/editor/libeditor/EditAction.h
+++ b/editor/libeditor/EditAction.h
@@ -343,19 +343,20 @@ enum class EditAction {
   eSetHTML,
 
   // eInsertHTML indicates to insert HTML source code.
   eInsertHTML,
 
   // eHidePassword indicates that editor hides password with mask characters.
   eHidePassword,
 
-  // eCreateBogusNode indicates that editor wants to create a bogus node after
-  // the editor is modified, asynchronously.
-  eCreateBogusNode,
+  // eCreatePaddingBRElementForEmptyEditor indicates that editor wants to
+  // create a padding <br> element for empty editor after it modifies its
+  // content.
+  eCreatePaddingBRElementForEmptyEditor,
 };
 
 // This is int32_t instead of int16_t because nsIInlineSpellChecker.idl's
 // spellCheckAfterEditorChange is defined to take it as a long.
 // TODO: Make each name eFoo and investigate whether the numeric values
 //       still have some meaning.
 enum class EditSubAction : int32_t {
   // eNone indicates not edit sub-action is being handled.  This is useful
@@ -464,18 +465,19 @@ enum class EditSubAction : int32_t {
   eSetPositionToAbsolute,
   eSetPositionToStatic,
 
   // eDecreaseZIndex and eIncreaseZIndex indicate to decrease and increase
   // z-index value.
   eDecreaseZIndex,
   eIncreaseZIndex,
 
-  // eCreateBogusNode indicates to create a bogus <br> node.
-  eCreateBogusNode,
+  // eCreatePaddingBRElementForEmptyEditor indicates to create a padding <br>
+  // element for empty editor.
+  eCreatePaddingBRElementForEmptyEditor,
 };
 
 inline EditorInputType ToInputType(EditAction aEditAction) {
   switch (aEditAction) {
     case EditAction::eInsertText:
       return EditorInputType::eInsertText;
     case EditAction::eReplaceText:
       return EditorInputType::eInsertReplacementText;
--- a/editor/libeditor/EditorBase.cpp
+++ b/editor/libeditor/EditorBase.cpp
@@ -1445,16 +1445,93 @@ nsresult EditorBase::InsertNodeWithTrans
     for (auto& listener : listeners) {
       listener->DidInsertNode(&aContentToInsert, rv);
     }
   }
 
   return rv;
 }
 
+EditorDOMPoint EditorBase::PrepareToInsertBRElement(
+    const EditorDOMPoint& aPointToInsert) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
+    return EditorDOMPoint();
+  }
+
+  if (!aPointToInsert.IsInTextNode()) {
+    return aPointToInsert;
+  }
+
+  if (aPointToInsert.IsStartOfContainer()) {
+    // Insert before the text node.
+    EditorDOMPoint pointInContainer(aPointToInsert.GetContainer());
+    NS_WARNING_ASSERTION(pointInContainer.IsSet(),
+                         "Failed to climb up the DOM tree from text node");
+    return pointInContainer;
+  }
+
+  if (aPointToInsert.IsEndOfContainer()) {
+    // Insert after the text node.
+    EditorDOMPoint pointInContainer(aPointToInsert.GetContainer());
+    if (NS_WARN_IF(!pointInContainer.IsSet())) {
+      return pointInContainer;
+    }
+    DebugOnly<bool> advanced = pointInContainer.AdvanceOffset();
+    NS_WARNING_ASSERTION(advanced,
+                         "Failed to advance offset to after the text node");
+    return pointInContainer;
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(aPointToInsert.IsSetAndValid());
+
+  // Unfortunately, we need to split the text node at the offset.
+  ErrorResult error;
+  nsCOMPtr<nsIContent> newLeftNode =
+      SplitNodeWithTransaction(aPointToInsert, error);
+  if (NS_WARN_IF(error.Failed())) {
+    error.SuppressException();
+    return EditorDOMPoint();
+  }
+  Unused << newLeftNode;
+  // Insert new <br> before the right node.
+  EditorDOMPoint pointInContainer(aPointToInsert.GetContainer());
+  NS_WARNING_ASSERTION(pointInContainer.IsSet(),
+                       "Failed to split the text node");
+  return pointInContainer;
+}
+
+CreateElementResult
+EditorBase::InsertPaddingBRElementForEmptyLastLineWithTransaction(
+    const EditorDOMPoint& aPointToInsert) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  EditorDOMPoint pointToInsert = PrepareToInsertBRElement(aPointToInsert);
+  if (NS_WARN_IF(!pointToInsert.IsSet())) {
+    return CreateElementResult(NS_ERROR_FAILURE);
+  }
+
+  RefPtr<Element> newBRElement = CreateHTMLContent(nsGkAtoms::br);
+  if (NS_WARN_IF(!newBRElement)) {
+    return CreateElementResult(NS_ERROR_FAILURE);
+  }
+  newBRElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
+
+  nsresult rv = InsertNodeWithTransaction(*newBRElement, pointToInsert);
+  if (NS_WARN_IF(Destroyed())) {
+    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
+  }
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return CreateElementResult(rv);
+  }
+
+  return CreateElementResult(newBRElement.forget());
+}
+
 NS_IMETHODIMP
 EditorBase::SplitNode(nsINode* aNode, int32_t aOffset, nsINode** aNewLeftNode) {
   if (NS_WARN_IF(!aNode)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   AutoEditActionDataSetter editActionData(*this, EditAction::eSplitNode);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
@@ -2515,19 +2592,20 @@ EditorRawDOMPoint EditorBase::FindBetter
     // In some cases, aNode is the anonymous DIV, and offset is 0.  To avoid
     // injecting unneeded text nodes, we first look to see if we have one
     // available.  In that case, we'll just adjust node and offset accordingly.
     if (aPoint.IsStartOfContainer() && aPoint.GetContainer()->HasChildren() &&
         aPoint.GetContainer()->GetFirstChild()->IsText()) {
       return EditorRawDOMPoint(aPoint.GetContainer()->GetFirstChild(), 0);
     }
 
-    // In some other cases, aNode is the anonymous DIV, and offset points to the
-    // terminating mozBR.  In that case, we'll adjust aInOutNode and
-    // aInOutOffset to the preceding text node, if any.
+    // In some other cases, aNode is the anonymous DIV, and offset points to
+    // the terminating padding <br> element for empty last line.  In that case,
+    // we'll adjust aInOutNode and aInOutOffset to the preceding text node,
+    // if any.
     if (!aPoint.IsStartOfContainer()) {
       if (AsHTMLEditor()) {
         // Fall back to a slow path that uses GetChildAt_Deprecated() for
         // Thunderbird's plaintext editor.
         nsIContent* child = aPoint.GetPreviousSiblingOfChild();
         if (child && child->IsText()) {
           if (NS_WARN_IF(child->Length() > INT32_MAX)) {
             return aPoint;
@@ -2546,20 +2624,20 @@ EditorRawDOMPoint EditorBase::FindBetter
             return EditorRawDOMPoint(child, child->Length());
           }
           child = child->GetPreviousSibling();
         }
       }
     }
   }
 
-  // Sometimes, aNode is the mozBR element itself.  In that case, we'll adjust
-  // the insertion point to the previous text node, if one exists, or to the
-  // parent anonymous DIV.
-  if (TextEditUtils::IsMozBR(aPoint.GetContainer()) &&
+  // Sometimes, aNode is the padding <br> element itself.  In that case, we'll
+  // adjust the insertion point to the previous text node, if one exists, or
+  // to the parent anonymous DIV.
+  if (EditorBase::IsPaddingBRElementForEmptyLastLine(*aPoint.GetContainer()) &&
       aPoint.IsStartOfContainer()) {
     nsIContent* previousSibling = aPoint.GetContainer()->GetPreviousSibling();
     if (previousSibling && previousSibling->IsText()) {
       if (NS_WARN_IF(previousSibling->Length() > INT32_MAX)) {
         return aPoint;
       }
       return EditorRawDOMPoint(previousSibling, previousSibling->Length());
     }
@@ -2598,19 +2676,19 @@ nsresult EditorBase::InsertTextWithTrans
 
   // This method doesn't support over INT32_MAX length text since aInOutOffset
   // is int32_t*.
   CheckedInt<int32_t> lengthToInsert(aStringToInsert.Length());
   if (NS_WARN_IF(!lengthToInsert.isValid())) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  // In some cases, the node may be the anonymous div elemnt or a mozBR
-  // element.  Let's try to look for better insertion point in the nearest
-  // text node if there is.
+  // In some cases, the node may be the anonymous div element or a padding
+  // <br> element for empty last line.  Let's try to look for better insertion
+  // point in the nearest text node if there is.
   EditorDOMPoint pointToInsert = FindBetterInsertionPoint(aPointToInsert);
 
   // If a neighboring text node already exists, use that
   if (!pointToInsert.IsInTextNode()) {
     nsIContent* child = nullptr;
     if (!pointToInsert.IsStartOfContainer() &&
         (child = pointToInsert.GetPreviousSiblingOfChild()) &&
         child->IsText()) {
--- a/editor/libeditor/EditorBase.h
+++ b/editor/libeditor/EditorBase.h
@@ -13,16 +13,17 @@
 #include "mozilla/OwningNonNull.h"       // for OwningNonNull
 #include "mozilla/PresShell.h"           // for PresShell
 #include "mozilla/RangeBoundary.h"       // for RawRangeBoundary, RangeBoundary
 #include "mozilla/SelectionState.h"      // for RangeUpdater, etc.
 #include "mozilla/StyleSheet.h"          // for StyleSheet
 #include "mozilla/TransactionManager.h"  // for TransactionManager
 #include "mozilla/WeakPtr.h"             // for WeakPtr
 #include "mozilla/dom/DataTransfer.h"    // for dom::DataTransfer
+#include "mozilla/dom/HTMLBRElement.h"   // for dom::HTMLBRElement
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/Text.h"
 #include "nsCOMPtr.h"  // for already_AddRefed, nsCOMPtr
 #include "nsCycleCollectionParticipant.h"
 #include "nsGkAtoms.h"
 #include "mozilla/dom/Document.h"
 #include "nsIContentInlines.h"       // for nsINode::IsEditable()
 #include "nsIEditor.h"               // for nsIEditor, etc.
@@ -85,30 +86,31 @@ class SplitNodeTransaction;
 class TextComposition;
 class TextEditor;
 class TextEditRules;
 class TextInputListener;
 class TextServicesDocument;
 class TypeInState;
 class WSRunObject;
 
+template <typename NodeType>
+class CreateNodeResultBase;
+typedef CreateNodeResultBase<dom::Element> CreateElementResult;
+
 namespace dom {
 class DataTransfer;
 class DragEvent;
 class Element;
 class EventTarget;
 }  // namespace dom
 
 namespace widget {
 struct IMEState;
 }  // namespace widget
 
-#define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode
-#define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
-
 /**
  * SplitAtEdges is for EditorBase::SplitNodeDeepWithTransaction(),
  * HTMLEditor::InsertNodeAtPoint()
  */
 enum class SplitAtEdges {
   // EditorBase::SplitNodeDeepWithTransaction() won't split container element
   // nodes at their edges.  I.e., when split point is start or end of
   // container, it won't be split.
@@ -702,17 +704,17 @@ class EditorBase : public nsIEditor,
         case EditSubAction::eJoinNodes:
         case EditSubAction::eDeleteText:
           MOZ_ASSERT(aDirection == ePrevious);
           mDirectionOfTopLevelEditSubAction = ePrevious;
           break;
         case EditSubAction::eUndo:
         case EditSubAction::eRedo:
         case EditSubAction::eComputeTextToOutput:
-        case EditSubAction::eCreateBogusNode:
+        case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
         case EditSubAction::eNone:
           MOZ_ASSERT(aDirection == eNone);
           mDirectionOfTopLevelEditSubAction = eNone;
           break;
         case EditSubAction::eReplaceHeadWithHTMLSource:
           // NOTE: Not used with AutoTopLevelEditSubActionNotifier.
           mDirectionOfTopLevelEditSubAction = eNone;
           break;
@@ -962,16 +964,28 @@ class EditorBase : public nsIEditor,
    *                            transaction will append the node to the
    *                            container.  Otherwise, will insert the node
    *                            before child node referred by this.
    */
   MOZ_CAN_RUN_SCRIPT nsresult InsertNodeWithTransaction(
       nsIContent& aContentToInsert, const EditorDOMPoint& aPointToInsert);
 
   /**
+   * InsertPaddingBRElementForEmptyLastLineWithTransaction() creates a padding
+   * <br> element with setting flags to NS_PADDING_FOR_EMPTY_LAST_LINE and
+   * inserts it around aPointToInsert.
+   *
+   * @param aPointToInsert      The DOM point where should be <br> node inserted
+   *                            before.
+   */
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE CreateElementResult
+  InsertPaddingBRElementForEmptyLastLineWithTransaction(
+      const EditorDOMPoint& aPointToInsert);
+
+  /**
    * ReplaceContainerWithTransaction() creates new element whose name is
    * aTagName, moves all children in aOldContainer to the new element, then,
    * removes aOldContainer from the DOM tree.
    *
    * @param aOldContainer       The element node which should be replaced
    *                            with new element.
    * @param aTagName            The name of new element node.
    */
@@ -1614,18 +1628,18 @@ class EditorBase : public nsIEditor,
   /**
    * returns true if aNode is an editable node.
    */
   bool IsEditable(nsINode* aNode) {
     if (NS_WARN_IF(!aNode)) {
       return false;
     }
 
-    if (!aNode->IsContent() || IsMozEditorBogusNode(aNode) ||
-        !IsModifiableNode(*aNode)) {
+    if (!aNode->IsContent() || !IsModifiableNode(*aNode) ||
+        EditorBase::IsPaddingBRElementForEmptyEditor(*aNode)) {
       return false;
     }
 
     switch (aNode->NodeType()) {
       case nsINode::ELEMENT_NODE:
         // In HTML editors, if we're dealing with an element, then ask it
         // whether it's editable.
         return mIsHTMLEditorClass ? aNode->IsEditable() : true;
@@ -1633,36 +1647,44 @@ class EditorBase : public nsIEditor,
         // Text nodes are considered to be editable by both typed of editors.
         return true;
       default:
         return false;
     }
   }
 
   /**
-   * Returns true if aNode is a usual element node (not bogus node) or
-   * a text node.  In other words, returns true if aNode is a usual element
-   * node or visible data node.
+   * Returns true if aNode is a usual element node (not padding <br> element
+   * for empty editor) or a text node.  In other words, returns true if aNode
+   * is a usual element node or visible data node.
    */
   bool IsElementOrText(const nsINode& aNode) const {
-    if (!aNode.IsContent() || IsMozEditorBogusNode(&aNode)) {
-      return false;
+    if (aNode.IsText()) {
+      return true;
     }
-    return aNode.NodeType() == nsINode::ELEMENT_NODE ||
-           aNode.NodeType() == nsINode::TEXT_NODE;
+    return aNode.IsElement() &&
+           !EditorBase::IsPaddingBRElementForEmptyEditor(aNode);
   }
 
   /**
-   * Returns true if aNode is a MozEditorBogus node.
+   * Returns true if aNode is a <br> element and it's marked as padding for
+   * empty editor.
    */
-  bool IsMozEditorBogusNode(const nsINode* aNode) const {
-    return aNode && aNode->IsElement() &&
-           aNode->AsElement()->AttrValueIs(
-               kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
-               kMOZEditorBogusNodeValue, eCaseMatters);
+  static bool IsPaddingBRElementForEmptyEditor(const nsINode& aNode) {
+    const dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(&aNode);
+    return brElement && brElement->IsPaddingForEmptyEditor();
+  }
+
+  /**
+   * Returns true if aNode is a <br> element and it's marked as padding for
+   * empty last line.
+   */
+  static bool IsPaddingBRElementForEmptyLastLine(const nsINode& aNode) {
+    const dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(&aNode);
+    return brElement && brElement->IsPaddingForEmptyLastLine();
   }
 
   /**
    * Counts number of editable child nodes.
    */
   uint32_t CountEditableChildren(nsINode* aNode);
 
   /**
@@ -2070,16 +2092,28 @@ class EditorBase : public nsIEditor,
   enum NotificationForEditorObservers {
     eNotifyEditorObserversOfEnd,
     eNotifyEditorObserversOfBefore,
     eNotifyEditorObserversOfCancel
   };
   MOZ_CAN_RUN_SCRIPT
   void NotifyEditorObservers(NotificationForEditorObservers aNotification);
 
+  /**
+   * PrepareToInsertBRElement() returns a point where new <br> element should
+   * be inserted.  If aPointToInsert points middle of a text node, this method
+   * splits the text node and returns the point before right node.
+   *
+   * @param aPointToInsert      Candidate point to insert new <br> element.
+   * @return                    Computed point to insert new <br> element.
+   *                            If something failed, this is unset.
+   */
+  MOZ_CAN_RUN_SCRIPT EditorDOMPoint
+  PrepareToInsertBRElement(const EditorDOMPoint& aPointToInsert);
+
  private:
   nsCOMPtr<nsISelectionController> mSelectionController;
   RefPtr<Document> mDocument;
 
   AutoEditActionDataSetter* mEditActionData;
 
   /**
    * SetTextDirectionTo() sets text-direction of the root element.
--- a/editor/libeditor/HTMLAbsPositionEditor.cpp
+++ b/editor/libeditor/HTMLAbsPositionEditor.cpp
@@ -523,17 +523,17 @@ nsresult HTMLEditor::SetPositionToAbsolu
   SnapToGrid(x, y);
   SetTopAndLeft(aElement, x, y);
 
   // we may need to create a br if the positioned element is alone in its
   // container
   nsINode* parentNode = aElement.GetParentNode();
   if (parentNode->GetChildCount() == 1) {
     RefPtr<Element> newBrElement =
-        InsertBrElementWithTransaction(EditorDOMPoint(parentNode, 0));
+        InsertBRElementWithTransaction(EditorDOMPoint(parentNode, 0));
     if (NS_WARN_IF(!newBrElement)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
 }
 
 nsresult HTMLEditor::SetPositionToStatic(Element& aElement) {
--- a/editor/libeditor/HTMLEditRules.cpp
+++ b/editor/libeditor/HTMLEditRules.cpp
@@ -15,27 +15,28 @@
 #include "mozilla/ContentIterator.h"
 #include "mozilla/CSSEditUtils.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/HTMLEditor.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Move.h"
+#include "mozilla/mozalloc.h"
+#include "mozilla/OwningNonNull.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/RangeUtils.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
+#include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/StaticRange.h"
-#include "mozilla/dom/Element.h"
-#include "mozilla/dom/RangeBinding.h"
-#include "mozilla/OwningNonNull.h"
-#include "mozilla/mozalloc.h"
 #include "nsAString.h"
 #include "nsAlgorithm.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsDebug.h"
 #include "nsError.h"
@@ -64,19 +65,16 @@
 #endif
 
 class nsISupports;
 
 namespace mozilla {
 
 using namespace dom;
 
-// const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
-// const static char* kMOZEditorBogusNodeValue="TRUE";
-
 enum { kLonely = 0, kPrevSib = 1, kNextSib = 2, kBothSibs = 3 };
 
 /********************************************************
  *  first some helpful functors we will use
  ********************************************************/
 
 static bool IsStyleCachePreservingSubAction(EditSubAction aEditSubAction) {
   return aEditSubAction == EditSubAction::eDeleteSelectedContent ||
@@ -450,17 +448,17 @@ nsresult HTMLEditRules::AfterEditInner(E
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = ConfirmSelectionInBody();
   if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
   if (aEditSubAction == EditSubAction::eReplaceHeadWithHTMLSource ||
-      aEditSubAction == EditSubAction::eCreateBogusNode) {
+      aEditSubAction == EditSubAction::eCreatePaddingBRElementForEmptyEditor) {
     return NS_OK;
   }
 
   nsCOMPtr<nsINode> rangeStartContainer, rangeEndContainer;
   uint32_t rangeStartOffset = 0, rangeEndOffset = 0;
   // do we have a real range to act on?
   bool bDamagedRange = false;
   if (mDocChangeRange) {
@@ -626,17 +624,20 @@ nsresult HTMLEditRules::AfterEditInner(E
   rv = HTMLEditorRef().HandleInlineSpellCheck(
       aEditSubAction, mRangeItem->mStartContainer, mRangeItem->mStartOffset,
       rangeStartContainer, rangeStartOffset, rangeEndContainer, rangeEndOffset);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // detect empty doc
-  rv = CreateBogusNodeIfNeeded();
+  // XXX Need to investigate when the padding <br> element is removed because
+  //     I don't see the <br> element with testing manually.  If it won't be
+  //     used, we can get rid of this cost.
+  rv = CreatePaddingBRElementForEmptyEditorIfNeeded();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // adjust selection HINT if needed
   if (!mDidExplicitlySetInterline) {
     CheckInterlinePosition();
   }
@@ -799,17 +800,19 @@ nsresult HTMLEditRules::DidDoAction(Edit
       }
       return DidAbsolutePosition();
     }
     default:
       return TextEditRules::DidDoAction(aInfo, aResult);
   }
 }
 
-bool HTMLEditRules::DocumentIsEmpty() { return !!mBogusNode; }
+bool HTMLEditRules::DocumentIsEmpty() const {
+  return !!mPaddingBRElementForEmptyEditor;
+}
 
 nsresult HTMLEditRules::GetListState(bool* aMixed, bool* aOL, bool* aUL,
                                      bool* aDL) {
   NS_ENSURE_TRUE(aMixed && aOL && aUL && aDL, NS_ERROR_NULL_POINTER);
   *aMixed = false;
   *aOL = false;
   *aUL = false;
   *aDL = false;
@@ -1232,49 +1235,51 @@ nsresult HTMLEditRules::GetFormatString(
 nsresult HTMLEditRules::WillInsert(bool* aCancel) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   nsresult rv = TextEditRules::WillInsert(aCancel);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Adjust selection to prevent insertion after a moz-BR.  This next only
-  // works for collapsed selections right now, because selection is a pain to
-  // work with when not collapsed.  (no good way to extend start or end of
-  // selection), so we ignore those types of selections.
+  // Adjust selection to prevent insertion after a padding <br> element for
+  // empty last line.  This next only works for collapsed selections right
+  // now, because selection is a pain to work with when not collapsed.  (no
+  // good way to extend start or end of selection), so we ignore those types
+  // of selections.
   if (!SelectionRefPtr()->IsCollapsed()) {
     return NS_OK;
   }
 
-  // If we are after a mozBR in the same block, then move selection to be
-  // before it
+  // If we are after a padding <br> element for empty last line in the same
+  // block, then move selection to be before it
   nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
   if (NS_WARN_IF(!firstRange)) {
     return NS_ERROR_FAILURE;
   }
 
   EditorRawDOMPoint atStartOfSelection(firstRange->StartRef());
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
   MOZ_ASSERT(atStartOfSelection.IsSetAndValid());
 
   // Get prior node
   nsCOMPtr<nsIContent> priorNode =
       HTMLEditorRef().GetPreviousEditableHTMLNode(atStartOfSelection);
-  if (priorNode && TextEditUtils::IsMozBR(priorNode)) {
+  if (priorNode && EditorBase::IsPaddingBRElementForEmptyLastLine(*priorNode)) {
     RefPtr<Element> block1 =
         HTMLEditorRef().GetBlock(*atStartOfSelection.GetContainer());
     RefPtr<Element> block2 = HTMLEditorRef().GetBlockNodeParent(priorNode);
 
     if (block1 && block1 == block2) {
-      // If we are here then the selection is right after a mozBR that is in
-      // the same block as the selection.  We need to move the selection start
-      // to be before the mozBR.
+      // If we are here then the selection is right after a padding <br>
+      // element for empty last line that is in the same block as the
+      // selection.  We need to move the selection start to be before the
+      // padding <br> element.
       EditorRawDOMPoint point(priorNode);
       ErrorResult error;
       SelectionRefPtr()->Collapse(point, error);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         error.SuppressException();
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(error.Failed())) {
@@ -1469,17 +1474,17 @@ nsresult HTMLEditRules::WillInsertText(E
           pos = tString.Length();
         }
 
         nsDependentSubstring subStr(tString, oldPos, subStrLen);
 
         // is it a return?
         if (subStr.Equals(newlineStr)) {
           RefPtr<Element> brElement = MOZ_KnownLive(HTMLEditorRef())
-                                          .InsertBrElementWithTransaction(
+                                          .InsertBRElementWithTransaction(
                                               currentPoint, nsIEditor::eNone);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(!brElement)) {
             return NS_ERROR_FAILURE;
           }
           pos++;
@@ -1636,29 +1641,31 @@ nsresult HTMLEditRules::WillInsertText(E
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::WillLoadHTML() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  // Delete mBogusNode if it exists. If we really need one,
-  // it will be added during post-processing in AfterEditInner().
-
-  if (mBogusNode) {
-    // A mutation event listener may recreate bogus node again during the
-    // call of DeleteNodeWithTransaction().  So, move it first.
-    nsCOMPtr<nsINode> bogusNode(std::move(mBogusNode));
-    DebugOnly<nsresult> rv =
-        MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*bogusNode);
+  // Delete mPaddingBRElementForEmptyEditor if it exists. If we really
+  // need one, it will be added during post-processing in AfterEditInner().
+  if (mPaddingBRElementForEmptyEditor) {
+    // A mutation event listener may recreate padding <br> element for empty
+    // editor again during the call of DeleteNodeWithTransaction().  So, move
+    // it first.
+    RefPtr<HTMLBRElement> paddingBRElement(
+        std::move(mPaddingBRElementForEmptyEditor));
+    DebugOnly<nsresult> rv = MOZ_KnownLive(HTMLEditorRef())
+                                 .DeleteNodeWithTransaction(*paddingBRElement);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove the bogus node");
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "Failed to remove the padding <br> element");
   }
 
   return NS_OK;
 }
 
 bool HTMLEditRules::CanContainParagraph(Element& aElement) const {
   MOZ_ASSERT(IsEditorDataAvailable());
 
@@ -1844,17 +1851,17 @@ EditActionResult HTMLEditRules::WillInse
   // "Text" is deleted leaving an empty block.  We want to put in one br to
   // make block have a line.  Then code further below will put in a second br.)
   if (IsEmptyBlockElement(*blockParent, IgnoreSingleBR::eNo)) {
     AutoEditorDOMPointChildInvalidator lockOffset(atStartOfSelection);
     EditorDOMPoint endOfBlockParent;
     endOfBlockParent.SetToEndOf(blockParent);
     RefPtr<Element> brElement =
         MOZ_KnownLive(HTMLEditorRef())
-            .InsertBrElementWithTransaction(endOfBlockParent);
+            .InsertBRElementWithTransaction(endOfBlockParent);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
     }
     if (NS_WARN_IF(!brElement)) {
       return EditActionIgnored(NS_ERROR_FAILURE);
     }
   }
 
@@ -1932,17 +1939,17 @@ nsresult HTMLEditRules::InsertBRElement(
 
   bool brElementIsAfterBlock = false;
   bool brElementIsBeforeBlock = false;
 
   // First, insert a <br> element.
   RefPtr<Element> brElement;
   if (IsPlaintextEditor()) {
     brElement = MOZ_KnownLive(HTMLEditorRef())
-                    .InsertBrElementWithTransaction(aPointToBreak);
+                    .InsertBRElementWithTransaction(aPointToBreak);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!brElement)) {
       return NS_ERROR_FAILURE;
     }
   } else {
     EditorDOMPoint pointToBreak(aPointToBreak);
@@ -2139,32 +2146,32 @@ EditActionResult HTMLEditRules::SplitMai
       previousNodeOfSplitPoint->GetPrimaryFrame()->IsBlockFrameOrSubclass()) {
     nsCOMPtr<nsINode> lastChild = previousNodeOfSplitPoint->GetLastChild();
     if (lastChild && !lastChild->IsHTMLElement(nsGkAtoms::br)) {
       // We ignore the result here.
       EditorDOMPoint endOfPreviousNodeOfSplitPoint;
       endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
       RefPtr<Element> invisibleBrElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(endOfPreviousNodeOfSplitPoint);
+              .InsertBRElementWithTransaction(endOfPreviousNodeOfSplitPoint);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
       }
       NS_WARNING_ASSERTION(invisibleBrElement,
                            "Failed to create an invisible <br> element");
     }
   }
 
   // In most cases, <br> should be inserted after current cite.  However, if
   // left cite hasn't been created because the split point was start of the
   // cite node, <br> should be inserted before the current cite.
   EditorDOMPoint pointToInsertBrNode(splitCiteNodeResult.SplitPoint());
   RefPtr<Element> brElement =
       MOZ_KnownLive(HTMLEditorRef())
-          .InsertBrElementWithTransaction(pointToInsertBrNode);
+          .InsertBRElementWithTransaction(pointToInsertBrNode);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
   }
   if (NS_WARN_IF(!brElement)) {
     return EditActionIgnored(NS_ERROR_FAILURE);
   }
   // Now, offset of pointToInsertBrNode is invalid.  Let's clear it.
   pointToInsertBrNode.Clear();
@@ -2205,17 +2212,17 @@ EditActionResult HTMLEditRules::SplitMai
                            "Failed to advance offset after the <br> node");
       WSRunObject wsObjAfterBR(&HTMLEditorRef(), pointAfterNewBrNode);
       wsObjAfterBR.NextVisibleNode(pointAfterNewBrNode, &wsType);
       if (wsType == WSType::normalWS || wsType == WSType::text ||
           wsType == WSType::special ||
           // In case we're at the very end.
           wsType == WSType::thisBlock) {
         brElement = MOZ_KnownLive(HTMLEditorRef())
-                        .InsertBrElementWithTransaction(pointToCreateNewBrNode);
+                        .InsertBRElementWithTransaction(pointToCreateNewBrNode);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
         }
         if (NS_WARN_IF(!brElement)) {
           return EditActionIgnored(NS_ERROR_FAILURE);
         }
         // Now, those points may be invalid.
         pointToCreateNewBrNode.Clear();
@@ -2278,18 +2285,19 @@ nsresult HTMLEditRules::WillDeleteSelect
   // Initialize out params
   *aCancel = false;
   *aHandled = false;
 
   // Remember that we did a selection deletion.  Used by
   // CreateStyleForInsertText()
   mDidDeleteSelection = true;
 
-  // If there is only bogus content, cancel the operation
-  if (mBogusNode) {
+  // If there is only padding <br> element for empty editor, cancel the
+  // operation.
+  if (mPaddingBRElementForEmptyEditor) {
     *aCancel = true;
     return NS_OK;
   }
 
   // First check for table selection mode.  If so, hand off to table editor.
   ErrorResult error;
   RefPtr<Element> cellElement =
       HTMLEditorRef().GetFirstSelectedTableCellElement(error);
@@ -3234,17 +3242,17 @@ nsresult HTMLEditRules::InsertBRIfNeeded
        (wsObj.mStartReason & WSType::br)) &&
       (wsObj.mEndReason & WSType::block)) {
     // if we are tucked between block boundaries then insert a br
     // first check that we are allowed to
     if (HTMLEditorRef().CanContainTag(*atStartOfSelection.GetContainer(),
                                       *nsGkAtoms::br)) {
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(atStartOfSelection,
+              .InsertBRElementWithTransaction(atStartOfSelection,
                                               nsIEditor::ePrevious);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
       return NS_OK;
@@ -3857,17 +3865,17 @@ nsresult HTMLEditRules::DidDeleteSelecti
         }
         if (NS_WARN_IF(NS_FAILED(rv))) {
           return rv;
         }
       }
       if (atCiteNode.IsSet() && seenBR) {
         RefPtr<Element> brElement =
             MOZ_KnownLive(HTMLEditorRef())
-                .InsertBrElementWithTransaction(atCiteNode);
+                .InsertBRElementWithTransaction(atCiteNode);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
         IgnoredErrorResult error;
         SelectionRefPtr()->Collapse(EditorRawDOMPoint(brElement), error);
@@ -4521,17 +4529,17 @@ nsresult HTMLEditRules::MakeBasicBlock(n
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(splitNodeResult.Failed())) {
         return splitNodeResult.Rv();
       }
       EditorDOMPoint pointToInsertBrNode(splitNodeResult.SplitPoint());
       // Put a <br> element at the split point
       brContent = MOZ_KnownLive(HTMLEditorRef())
-                      .InsertBrElementWithTransaction(pointToInsertBrNode);
+                      .InsertBRElementWithTransaction(pointToInsertBrNode);
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brContent)) {
         return NS_ERROR_FAILURE;
       }
       // Put selection at the split point
       EditorRawDOMPoint atBrNode(brContent);
@@ -4640,18 +4648,18 @@ nsresult HTMLEditRules::DidMakeBasicBloc
   nsRange* firstRange = SelectionRefPtr()->GetRangeAt(0);
   if (NS_WARN_IF(!firstRange)) {
     return NS_ERROR_FAILURE;
   }
   const RangeBoundary& atStartOfSelection = firstRange->StartRef();
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
-  nsresult rv =
-      InsertMozBRIfNeeded(MOZ_KnownLive(*atStartOfSelection.Container()));
+  nsresult rv = InsertPaddingBRElementForEmptyLastLineIfNeeded(
+      MOZ_KnownLive(*atStartOfSelection.Container()));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::WillIndent(bool* aCancel, bool* aHandled) {
   MOZ_ASSERT(IsEditorDataAvailable());
@@ -6133,20 +6141,24 @@ nsresult HTMLEditRules::AlignContentsAtS
     }
     // Remember our new block for postprocessing
     mNewBlock = div;
     // Set up the alignment on the div, using HTML or CSS
     rv = AlignBlock(*div, aAlignType, ResetAlignOf::OnlyDescendants);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
-    // Put in a moz-br so that it won't get deleted
-    CreateElementResult createMozBrResult = CreateMozBR(EditorDOMPoint(div, 0));
-    if (NS_WARN_IF(createMozBrResult.Failed())) {
-      return createMozBrResult.Rv();
+    // Put in a padding <br> element for empty last line so that it won't get
+    // deleted.
+    CreateElementResult createPaddingBRResult =
+        MOZ_KnownLive(HTMLEditorRef())
+            .InsertPaddingBRElementForEmptyLastLineWithTransaction(
+                EditorDOMPoint(div, 0));
+    if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+      return createPaddingBRResult.Rv();
     }
     EditorRawDOMPoint atStartOfDiv(div, 0);
     // Don't restore the selection
     restoreSelectionLater.Abort();
     ErrorResult error;
     SelectionRefPtr()->Collapse(atStartOfDiv, error);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       error.SuppressException();
@@ -6435,17 +6447,17 @@ nsresult HTMLEditRules::MaybeDeleteTopMo
       if (NS_WARN_IF(!atBlockParent.IsSet())) {
         return NS_ERROR_FAILURE;
       }
       // If the grand parent IS a list element, we'll adjust Selection in
       // AfterEdit().
       if (!HTMLEditUtils::IsList(atBlockParent.GetContainer())) {
         RefPtr<Element> brElement =
             MOZ_KnownLive(HTMLEditorRef())
-                .InsertBrElementWithTransaction(atBlockParent);
+                .InsertBRElementWithTransaction(atBlockParent);
         if (NS_WARN_IF(!CanHandleEditAction())) {
           return NS_ERROR_EDITOR_DESTROYED;
         }
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
         ErrorResult error;
         SelectionRefPtr()->Collapse(EditorRawDOMPoint(brElement), error);
@@ -7815,30 +7827,33 @@ nsresult HTMLEditRules::ReturnInHeader(E
               aHeader, EditorDOMPoint(node, aOffset),
               SplitAtEdges::eAllowToCreateEmptyContainer);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   NS_WARNING_ASSERTION(splitHeaderResult.Succeeded(),
                        "Failed to split aHeader");
 
-  // If the previous heading of split point is empty, put a mozbr into it.
+  // If the previous heading of split point is empty, put a padding <br>
+  // element for empty last line into it.
   nsCOMPtr<nsIContent> prevItem = HTMLEditorRef().GetPriorHTMLSibling(&aHeader);
   if (prevItem) {
     MOZ_DIAGNOSTIC_ASSERT(HTMLEditUtils::IsHeader(*prevItem));
     bool isEmptyNode;
     rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (isEmptyNode) {
-      CreateElementResult createMozBrResult =
-          CreateMozBR(EditorDOMPoint(prevItem, 0));
-      if (NS_WARN_IF(createMozBrResult.Failed())) {
-        return createMozBrResult.Rv();
+      CreateElementResult createPaddingBRResult =
+          MOZ_KnownLive(HTMLEditorRef())
+              .InsertPaddingBRElementForEmptyLastLineWithTransaction(
+                  EditorDOMPoint(prevItem, 0));
+      if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+        return createPaddingBRResult.Rv();
       }
     }
   }
 
   // If the new (righthand) header node is empty, delete it
   if (IsEmptyBlockElement(aHeader, IgnoreSingleBR::eYes)) {
     rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(aHeader);
     if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -7872,17 +7887,17 @@ nsresult HTMLEditRules::ReturnInHeader(E
       }
       if (NS_WARN_IF(!pNode)) {
         return NS_ERROR_FAILURE;
       }
 
       // Append a <br> to it
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
+              .InsertBRElementWithTransaction(EditorDOMPoint(pNode, 0));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
 
       // Set selection to before the break
@@ -8019,27 +8034,27 @@ EditActionResult HTMLEditRules::ReturnIn
     brContent = nullptr;
   } else if (atStartOfSelection.IsInTextNode()) {
     // at beginning of text node?
     if (atStartOfSelection.IsStartOfContainer()) {
       // is there a BR prior to it?
       brContent = HTMLEditorRef().GetPriorHTMLSibling(
           atStartOfSelection.GetContainer());
       if (!brContent || !HTMLEditorRef().IsVisibleBRElement(brContent) ||
-          TextEditUtils::HasMozAttr(brContent)) {
+          EditorBase::IsPaddingBRElementForEmptyLastLine(*brContent)) {
         pointToInsertBR.Set(atStartOfSelection.GetContainer());
         brContent = nullptr;
       }
     } else if (atStartOfSelection.IsEndOfContainer()) {
       // we're at the end of text node...
       // is there a BR after to it?
       brContent =
           HTMLEditorRef().GetNextHTMLSibling(atStartOfSelection.GetContainer());
       if (!brContent || !HTMLEditorRef().IsVisibleBRElement(brContent) ||
-          TextEditUtils::HasMozAttr(brContent)) {
+          EditorBase::IsPaddingBRElementForEmptyLastLine(*brContent)) {
         pointToInsertBR.Set(atStartOfSelection.GetContainer());
         DebugOnly<bool> advanced = pointToInsertBR.AdvanceOffset();
         NS_WARNING_ASSERTION(advanced,
                              "Failed to advance offset to after the container "
                              "of selection start");
         brContent = nullptr;
       }
     } else {
@@ -8067,37 +8082,37 @@ EditActionResult HTMLEditRules::ReturnIn
           "Failed to advance offset to after the container of selection start");
     }
   } else {
     // not in a text node.
     // is there a BR prior to it?
     nsCOMPtr<nsIContent> nearNode;
     nearNode = HTMLEditorRef().GetPreviousEditableHTMLNode(atStartOfSelection);
     if (!nearNode || !HTMLEditorRef().IsVisibleBRElement(nearNode) ||
-        TextEditUtils::HasMozAttr(nearNode)) {
+        EditorBase::IsPaddingBRElementForEmptyLastLine(*nearNode)) {
       // is there a BR after it?
       nearNode = HTMLEditorRef().GetNextEditableHTMLNode(atStartOfSelection);
       if (!nearNode || !HTMLEditorRef().IsVisibleBRElement(nearNode) ||
-          TextEditUtils::HasMozAttr(nearNode)) {
+          EditorBase::IsPaddingBRElementForEmptyLastLine(*nearNode)) {
         pointToInsertBR = atStartOfSelection;
         splitAfterNewBR = true;
       }
     }
     if (!pointToInsertBR.IsSet() && TextEditUtils::IsBreak(nearNode)) {
       brContent = nearNode;
     }
   }
   if (pointToInsertBR.IsSet()) {
     // if CR does not create a new P, default to BR creation
     if (NS_WARN_IF(!doesCRCreateNewP)) {
       return EditActionResult(NS_OK);
     }
 
     brContent = MOZ_KnownLive(HTMLEditorRef())
-                    .InsertBrElementWithTransaction(pointToInsertBR);
+                    .InsertBRElementWithTransaction(pointToInsertBR);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
     }
     NS_WARNING_ASSERTION(brContent, "Failed to create a <br> element");
     if (splitAfterNewBR) {
       // We split the parent after the br we've just inserted.
       pointToSplitParentDivOrP.Set(brContent);
       DebugOnly<bool> advanced = pointToSplitParentDivOrP.AdvanceOffset();
@@ -8170,21 +8185,21 @@ nsresult HTMLEditRules::SplitParagraph(
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // We need to ensure to both paragraphs visible even if they are empty.
-  // However, moz-<br> element isn't useful in this case because moz-<br>
-  // elements will be ignored by PlaintextSerializer.  Additionally,
-  // moz-<br> will be exposed as <br> with Element.innerHTML.  Therefore,
-  // we can use normal <br> elements for placeholder in this case.
-  // Note that Chromium also behaves so.
+  // However, padding <br> element for empty last line isn't useful in this
+  // case because it'll be ignored by PlaintextSerializer.  Additionally,
+  // it'll be exposed as <br> with Element.innerHTML.  Therefore, we can use
+  // normal <br> elements for placeholder in this case.  Note that Chromium
+  // also behaves so.
   rv = InsertBRIfNeeded(MOZ_KnownLive(*splitDivOrPResult.GetPreviousNode()));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   rv = InsertBRIfNeeded(MOZ_KnownLive(*splitDivOrPResult.GetNextNode()));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
@@ -8295,17 +8310,17 @@ nsresult HTMLEditRules::ReturnInListItem
       }
       if (NS_WARN_IF(!pNode)) {
         return NS_ERROR_FAILURE;
       }
 
       // Append a <br> to it
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorDOMPoint(pNode, 0));
+              .InsertBRElementWithTransaction(EditorDOMPoint(pNode, 0));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
 
       // Set selection to before the break
@@ -8356,20 +8371,22 @@ nsresult HTMLEditRules::ReturnInListItem
       HTMLEditorRef().GetPriorHTMLSibling(&aListItem);
   if (prevItem && HTMLEditUtils::IsListItem(prevItem)) {
     bool isEmptyNode;
     rv = HTMLEditorRef().IsEmptyNode(prevItem, &isEmptyNode);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (isEmptyNode) {
-      CreateElementResult createMozBrResult =
-          CreateMozBR(EditorDOMPoint(prevItem, 0));
-      if (NS_WARN_IF(createMozBrResult.Failed())) {
-        return createMozBrResult.Rv();
+      CreateElementResult createPaddingBRResult =
+          MOZ_KnownLive(HTMLEditorRef())
+              .InsertPaddingBRElementForEmptyLastLineWithTransaction(
+                  EditorDOMPoint(prevItem, 0));
+      if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+        return createPaddingBRResult.Rv();
       }
     } else {
       rv = HTMLEditorRef().IsEmptyNode(&aListItem, &isEmptyNode, true);
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return rv;
       }
       if (isEmptyNode) {
         RefPtr<nsAtom> nodeAtom = aListItem.NodeInfo()->NameAtom();
@@ -9238,29 +9255,29 @@ HTMLEditRules::InsertBRElementToEmptyLis
   nsTArray<OwningNonNull<nsINode>> nodeArray;
   EmptyEditableFunctor functor(&HTMLEditorRef());
   DOMIterator iter;
   if (NS_WARN_IF(NS_FAILED(iter.Init(*mDocChangeRange)))) {
     return NS_ERROR_FAILURE;
   }
   iter.AppendList(functor, nodeArray);
 
-  // Put moz-br's into these empty li's and td's
+  // Put padding <br> elements for empty <li> and <td>.
   for (auto& node : nodeArray) {
     // Need to put br at END of node.  It may have empty containers in it and
     // still pass the "IsEmptyNode" test, and we want the br's to be after
     // them.  Also, we want the br to be after the selection if the selection
     // is in this node.
     EditorDOMPoint endOfNode;
     endOfNode.SetToEndOf(node);
-    // XXX This method should return nsreuslt due to may be destroyed by this
-    //     CreateMozBr() call.
-    CreateElementResult createMozBrResult = CreateMozBR(endOfNode);
-    if (NS_WARN_IF(createMozBrResult.Failed())) {
-      return createMozBrResult.Rv();
+    CreateElementResult createPaddingBRResult =
+        MOZ_KnownLive(HTMLEditorRef())
+            .InsertPaddingBRElementForEmptyLastLineWithTransaction(endOfNode);
+    if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+      return createPaddingBRResult.Rv();
     }
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::PinSelectionToNewBlock() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
@@ -9450,57 +9467,63 @@ nsresult HTMLEditRules::AdjustSelection(
         HTMLEditorRef().CanContainTag(*point.GetContainer(), *nsGkAtoms::br)) {
       Element* rootElement = HTMLEditorRef().GetRoot();
       if (NS_WARN_IF(!rootElement)) {
         return NS_ERROR_FAILURE;
       }
       if (point.GetContainer() == rootElement) {
         // Our root node is completely empty. Don't add a <br> here.
         // AfterEditInner() will add one for us when it calls
-        // CreateBogusNodeIfNeeded()!
+        // CreatePaddingBRElementForEmptyEditorIfNeeded()!
         return NS_OK;
       }
 
       // we know we can skip the rest of this routine given the cirumstance
-      CreateElementResult createMozBrResult = CreateMozBR(point);
-      if (NS_WARN_IF(createMozBrResult.Failed())) {
-        return createMozBrResult.Rv();
+      CreateElementResult createPaddingBRResult =
+          MOZ_KnownLive(HTMLEditorRef())
+              .InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
+      if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+        return createPaddingBRResult.Rv();
       }
       return NS_OK;
     }
   }
 
   // are we in a text node?
   if (point.IsInTextNode()) {
     return NS_OK;  // we LIKE it when we are in a text node.  that RULZ
   }
 
-  // do we need to insert a special mozBR?  We do if we are:
+  // Do we need to insert a padding <br> element for empty last line?  We do
+  // if we are:
   // 1) prior node is in same block where selection is AND
   // 2) prior node is a br AND
   // 3) that br is not visible
 
   nsCOMPtr<nsIContent> nearNode =
       HTMLEditorRef().GetPreviousEditableHTMLNode(point);
   if (nearNode) {
     // is nearNode also a descendant of same block?
     RefPtr<Element> block = HTMLEditorRef().GetBlock(*point.GetContainer());
     RefPtr<Element> nearBlock = HTMLEditorRef().GetBlockNodeParent(nearNode);
     if (block && block == nearBlock) {
       if (nearNode && TextEditUtils::IsBreak(nearNode)) {
         if (!HTMLEditorRef().IsVisibleBRElement(nearNode)) {
           // need to insert special moz BR. Why?  Because if we don't
           // the user will see no new line for the break.  Also, things
           // like table cells won't grow in height.
-          CreateElementResult createMozBrResult = CreateMozBR(point);
-          if (NS_WARN_IF(createMozBrResult.Failed())) {
-            return createMozBrResult.Rv();
+          CreateElementResult createPaddingBRResult =
+              MOZ_KnownLive(HTMLEditorRef())
+                  .InsertPaddingBRElementForEmptyLastLineWithTransaction(point);
+          if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+            return createPaddingBRResult.Rv();
           }
-          point.Set(createMozBrResult.GetNewNode());
-          // selection stays *before* moz-br, sticking to it
+          point.Set(createPaddingBRResult.GetNewNode());
+          // Selection stays *before* padding <br> element for empty last line,
+          // sticking to it.
           ErrorResult error;
           SelectionRefPtr()->SetInterlinePosition(true, error);
           if (NS_WARN_IF(!CanHandleEditAction())) {
             error.SuppressException();
             return NS_ERROR_EDITOR_DESTROYED;
           }
           NS_WARNING_ASSERTION(!error.Failed(),
                                "Failed to set interline position");
@@ -9511,19 +9534,21 @@ nsresult HTMLEditRules::AdjustSelection(
             return NS_ERROR_EDITOR_DESTROYED;
           }
           if (NS_WARN_IF(error.Failed())) {
             return error.StealNSResult();
           }
         } else {
           nsCOMPtr<nsIContent> nextNode =
               HTMLEditorRef().GetNextEditableHTMLNodeInBlock(*nearNode);
-          if (nextNode && TextEditUtils::IsMozBR(nextNode)) {
-            // selection between br and mozbr.  make it stick to mozbr
-            // so that it will be on blank line.
+          if (nextNode &&
+              EditorBase::IsPaddingBRElementForEmptyLastLine(*nextNode)) {
+            // Selection between a <br> element and a padding <br> element for
+            // empty last line.  Make it stick to the padding <br> element so
+            // that it will be on blank line.
             IgnoredErrorResult ignoredError;
             SelectionRefPtr()->SetInterlinePosition(true, ignoredError);
             NS_WARNING_ASSERTION(!ignoredError.Failed(),
                                  "Failed to set interline position");
           }
         }
       }
     }
@@ -9762,17 +9787,17 @@ nsresult HTMLEditRules::RemoveEmptyNodes
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
     if (!isEmptyNode) {
       // We are deleting a cite that has just a br.  We want to delete cite,
       // but preserve br.
       RefPtr<Element> brElement =
           MOZ_KnownLive(HTMLEditorRef())
-              .InsertBrElementWithTransaction(EditorDOMPoint(delNode));
+              .InsertBRElementWithTransaction(EditorDOMPoint(delNode));
       if (NS_WARN_IF(!CanHandleEditAction())) {
         return NS_ERROR_EDITOR_DESTROYED;
       }
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
     }
     rv = MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*delNode);
@@ -10146,39 +10171,50 @@ nsresult HTMLEditRules::UpdateDocChangeR
         return error.StealNSResult();
       }
     }
   }
   return NS_OK;
 }
 
 nsresult HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
-                                                 bool aInsertMozBR) {
+                                                 bool aForPadding) {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   if (!IsBlockNode(aNode)) {
     return NS_OK;
   }
 
   bool isEmpty;
   nsresult rv = HTMLEditorRef().IsEmptyNode(&aNode, &isEmpty);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (!isEmpty) {
     return NS_OK;
   }
 
-  CreateElementResult createBrResult =
-      !aInsertMozBR ? CreateBR(EditorDOMPoint(&aNode, 0))
-                    : CreateMozBR(EditorDOMPoint(&aNode, 0));
-  if (NS_WARN_IF(createBrResult.Failed())) {
-    return createBrResult.Rv();
-  }
-  return NS_OK;
+  if (aForPadding) {
+    CreateElementResult createBRResult =
+        MOZ_KnownLive(HTMLEditorRef())
+            .InsertPaddingBRElementForEmptyLastLineWithTransaction(
+                EditorDOMPoint(&aNode, 0));
+    NS_WARNING_ASSERTION(createBRResult.Succeeded(),
+                         "Failed to create padding <br> element");
+    return createBRResult.Rv();
+  }
+
+  RefPtr<Element> brElement =
+      MOZ_KnownLive(HTMLEditorRef())
+          .InsertBRElementWithTransaction(EditorDOMPoint(&aNode, 0));
+  if (NS_WARN_IF(!CanHandleEditAction())) {
+    return NS_ERROR_EDITOR_DESTROYED;
+  }
+  NS_WARNING_ASSERTION(brElement, "Failed to create <br> element");
+  return brElement ? NS_OK : NS_ERROR_FAILURE;
 }
 
 void HTMLEditRules::DidCreateNode(Element& aNewElement) {
   if (!mListenerEnabled) {
     return;
   }
 
   if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -10488,17 +10524,17 @@ nsresult HTMLEditRules::MakeSureElemStar
     EditorDOMPoint pointToInsert;
     if (!aStarts) {
       pointToInsert.SetToEndOf(&aNode);
     } else {
       pointToInsert.Set(&aNode, 0);
     }
     RefPtr<Element> brElement =
         MOZ_KnownLive(HTMLEditorRef())
-            .InsertBrElementWithTransaction(pointToInsert);
+            .InsertBRElementWithTransaction(pointToInsert);
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     if (NS_WARN_IF(!brElement)) {
       return NS_ERROR_FAILURE;
     }
   }
   return NS_OK;
@@ -11090,27 +11126,30 @@ void HTMLEditRules::OnModifyDocument() {
   MOZ_ASSERT(mHTMLEditor);
 
   AutoSafeEditorData setData(*this, *mHTMLEditor);
 
   // DeleteNodeWithTransaction() below may cause a flush, which could destroy
   // the editor
   nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
 
-  // Delete our bogus node, if we have one, since the document might not be
-  // empty any more.
-  if (mBogusNode) {
-    // A mutation event listener may recreate bogus node again during the
-    // call of DeleteNodeWithTransaction().  So, move it first.
-    nsCOMPtr<nsIContent> bogusNode(std::move(mBogusNode));
-    DebugOnly<nsresult> rv =
-        MOZ_KnownLive(HTMLEditorRef()).DeleteNodeWithTransaction(*bogusNode);
-    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove the bogus node");
-  }
-
-  // Try to recreate the bogus node if needed.
-  DebugOnly<nsresult> rv = CreateBogusNodeIfNeeded();
+  // Delete our padding <br> element for empty editor, if we have one, since
+  // the document might not be empty any more.
+  if (mPaddingBRElementForEmptyEditor) {
+    // A mutation event listener may recreate padding <br> element for empty
+    // editor again during the call of DeleteNodeWithTransaction().  So, move
+    // it first.
+    RefPtr<HTMLBRElement> paddingBRElement(
+        std::move(mPaddingBRElementForEmptyEditor));
+    DebugOnly<nsresult> rv = MOZ_KnownLive(HTMLEditorRef())
+                                 .DeleteNodeWithTransaction(*paddingBRElement);
+    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                         "Failed to remove the padding <br> element");
+  }
+
+  // Try to recreate the padding <br> element for empty editor if needed.
+  DebugOnly<nsresult> rv = CreatePaddingBRElementForEmptyEditorIfNeeded();
   NS_WARNING_ASSERTION(
       rv.value != NS_ERROR_EDITOR_DESTROYED,
-      "The editor has been destroyed during creating a bogus node");
+      "The editor has been destroyed during creating a padding <br> element");
 }
 
 }  // namespace mozilla
--- a/editor/libeditor/HTMLEditRules.h
+++ b/editor/libeditor/HTMLEditRules.h
@@ -89,17 +89,17 @@ class HTMLEditRules : public TextEditRul
   //       may not causes running script.  So, ideal fix must be that we make
   //       each method callsed by this method public.
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult WillDoAction(EditSubActionInfo& aInfo, bool* aCancel,
                                 bool* aHandled) override;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult DidDoAction(EditSubActionInfo& aInfo,
                                nsresult aResult) override;
-  virtual bool DocumentIsEmpty() override;
+  virtual bool DocumentIsEmpty() const override;
 
   /**
    * DocumentModified() is called when editor content is changed.
    */
   MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult DocumentModified();
 
   MOZ_CAN_RUN_SCRIPT
   nsresult GetListState(bool* aMixed, bool* aOL, bool* aUL, bool* aDL);
@@ -188,17 +188,17 @@ class HTMLEditRules : public TextEditRul
   MOZ_MUST_USE nsresult WillInsertText(EditSubAction aEditSubAction,
                                        bool* aCancel, bool* aHandled,
                                        const nsAString* inString,
                                        nsAString* outString,
                                        int32_t aMaxLength);
 
   /**
    * WillLoadHTML() is called before loading enter document from source.
-   * This removes bogus node if there is.
+   * This removes padding <br> element for empty editor if there is.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult WillLoadHTML();
 
   /**
    * WillInsertParagraphSeparator() is called when insertParagraph command is
    * executed or something equivalent.  This method actually tries to insert
    * new paragraph or <br> element, etc.
    */
@@ -272,36 +272,38 @@ class HTMLEditRules : public TextEditRul
    * Insert normal <br> element into aNode when aNode is a block and it has
    * no children.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult InsertBRIfNeeded(nsINode& aNode) {
     return InsertBRIfNeededInternal(aNode, false);
   }
 
   /**
-   * Insert moz-<br> element (<br type="_moz">) into aNode when aNode is a
+   * Insert padding <br> element for empty last line into aNode when aNode is a
    * block and it has no children.
    */
-  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult InsertMozBRIfNeeded(nsINode& aNode) {
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+  InsertPaddingBRElementForEmptyLastLineIfNeeded(nsINode& aNode) {
     return InsertBRIfNeededInternal(aNode, true);
   }
 
   /**
-   * Insert a normal <br> element or a moz-<br> element to aNode when
-   * aNode is a block and it has no children.  Use InsertBRIfNeeded() or
-   * InsertMozBRIfNeeded() instead.
+   * Insert a normal <br> element or a padding <br> element for empty last line
+   * to aNode when aNode is a block and it has no children.  Use
+   * InsertBRIfNeeded() or InsertPaddingBRElementForEmptyLastLineIfNeeded()
+   * instead.
    *
-   * @param aNode           Reference to a block parent.
-   * @param aInsertMozBR    true if this should insert a moz-<br> element.
-   *                        Otherwise, i.e., this should insert a normal <br>
-   *                        element, false.
+   * @param aNode               Reference to a block parent.
+   * @param aForPadding         true if this should insert a <br> element for
+   *                            placing caret at empty last line.
+   *                            Otherwise, i.e., this should insert a normal
+   *                            <br> element, false.
    */
-  MOZ_CAN_RUN_SCRIPT
-  MOZ_MUST_USE nsresult InsertBRIfNeededInternal(nsINode& aNode,
-                                                 bool aInsertMozBR);
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+  InsertBRIfNeededInternal(nsINode& aNode, bool aForPadding);
 
   /**
    * GetGoodSelPointForNode() finds where at a node you would want to set the
    * selection if you were trying to have a caret next to it.  Always returns a
    * valid value (unless mHTMLEditor has gone away).
    *
    * @param aNode         The node
    * @param aAction       Which edge to find:
@@ -531,18 +533,18 @@ class HTMLEditRules : public TextEditRul
    *                            will be called.
    *                            Otherwise, ApplyBlockStyle() will be called.
    */
   MOZ_CAN_RUN_SCRIPT
   MOZ_MUST_USE nsresult MakeBasicBlock(nsAtom& aBlockType);
 
   /**
    * Called after creating a basic block, indenting, outdenting or aligning
-   * contents.  This method inserts moz-<br> element if start container of
-   * Selection needs it.
+   * contents.  This method inserts a padding <br> element for empty last line
+   * if start container of Selection needs it.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult DidMakeBasicBlock();
 
   /**
    * Called before changing an element to absolute positioned.
    * This method only prepares the operation since DidAbsolutePosition() will
    * change it actually later.  mNewBlock is set to the target element and
    * if necessary, some ancestor nodes of selection may be split.
--- a/editor/libeditor/HTMLEditUtils.cpp
+++ b/editor/libeditor/HTMLEditUtils.cpp
@@ -208,17 +208,19 @@ bool HTMLEditUtils::IsNamedAnchor(nsINod
 }
 
 /**
  * IsMozDiv() returns true if aNode is an html div node with |type = _moz|.
  */
 bool HTMLEditUtils::IsMozDiv(nsINode* aNode) {
   MOZ_ASSERT(aNode);
   return aNode->IsHTMLElement(nsGkAtoms::div) &&
-         TextEditUtils::HasMozAttr(aNode);
+         aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                                         NS_LITERAL_STRING("_moz"),
+                                         eIgnoreCase);
 }
 
 /**
  * IsMailCite() returns true if aNode is an html blockquote with |type=cite|.
  */
 bool HTMLEditUtils::IsMailCite(nsINode* aNode) {
   MOZ_ASSERT(aNode);
 
--- a/editor/libeditor/HTMLEditor.cpp
+++ b/editor/libeditor/HTMLEditor.cpp
@@ -5,16 +5,17 @@
 
 #include "mozilla/HTMLEditor.h"
 
 #include "mozilla/ComposerCommandsUpdater.h"
 #include "mozilla/ContentIterator.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
+#include "mozilla/EditorUtils.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/mozInlineSpellChecker.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/TextEvents.h"
 
 #include "nsCRT.h"
 
@@ -1181,20 +1182,20 @@ nsresult HTMLEditor::InsertBrElementAtSe
   }
 
   EditorDOMPoint atStartOfSelection(
       EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!atStartOfSelection.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
-  // InsertBrElementWithTransaction() will set selection after the new <br>
+  // InsertBRElementWithTransaction() will set selection after the new <br>
   // element.
   RefPtr<Element> newBrElement =
-      InsertBrElementWithTransaction(atStartOfSelection, eNext);
+      InsertBRElementWithTransaction(atStartOfSelection, eNext);
   if (NS_WARN_IF(!newBrElement)) {
     return NS_ERROR_FAILURE;
   }
   return NS_OK;
 }
 
 void HTMLEditor::CollapseSelectionToDeepestNonTableFirstChild(nsINode* aNode) {
   MOZ_ASSERT(IsEditActionDataAvailable());
@@ -1639,17 +1640,17 @@ nsresult HTMLEditor::InsertElementAtSele
       // check for inserting a whole table at the end of a block. If so insert
       // a br after it.
       if (HTMLEditUtils::IsTable(aElement) && IsLastEditableChild(aElement)) {
         DebugOnly<bool> advanced = insertedPoint.AdvanceOffset();
         NS_WARNING_ASSERTION(advanced,
                              "Failed to advance offset from inserted point");
         // Collapse selection to the new <br> element node after creating it.
         RefPtr<Element> newBrElement =
-            InsertBrElementWithTransaction(insertedPoint, ePrevious);
+            InsertBRElementWithTransaction(insertedPoint, ePrevious);
         if (NS_WARN_IF(!newBrElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
   rv = rules->DidDoAction(subActionInfo, rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -3398,17 +3399,17 @@ nsresult HTMLEditor::DeleteSelectionWith
 nsresult HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode) {
   if (NS_WARN_IF(!aNode.IsContent())) {
     return NS_ERROR_INVALID_ARG;
   }
   // Do nothing if the node is read-only.
   // XXX This is not a override method of EditorBase's method.  This might
   //     cause not called accidentally.  We need to investigate this issue.
   if (NS_WARN_IF(!IsModifiableNode(*aNode.AsContent()) &&
-                 !IsMozEditorBogusNode(aNode.AsContent()))) {
+                 !EditorBase::IsPaddingBRElementForEmptyEditor(aNode))) {
     return NS_ERROR_FAILURE;
   }
   nsresult rv = EditorBase::DeleteNodeWithTransaction(aNode);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   return NS_OK;
 }
@@ -3560,16 +3561,77 @@ nsresult HTMLEditor::InsertTextWithTrans
   if (!IsModifiableNode(*aPointToInsert.GetContainer())) {
     return NS_ERROR_FAILURE;
   }
 
   return EditorBase::InsertTextWithTransaction(
       aDocument, aStringToInsert, aPointToInsert, aPointAfterInsertedString);
 }
 
+already_AddRefed<Element> HTMLEditor::InsertBRElementWithTransaction(
+    const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
+  MOZ_ASSERT(IsEditActionDataAvailable());
+
+  EditorDOMPoint pointToInsert = PrepareToInsertBRElement(aPointToInsert);
+  if (NS_WARN_IF(!pointToInsert.IsSet())) {
+    return nullptr;
+  }
+
+  RefPtr<Element> newBRElement =
+      CreateNodeWithTransaction(*nsGkAtoms::br, pointToInsert);
+  if (NS_WARN_IF(!newBRElement)) {
+    return nullptr;
+  }
+
+  switch (aSelect) {
+    case eNone:
+      break;
+    case eNext: {
+      SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
+      // Collapse selection after the <br> node.
+      EditorRawDOMPoint afterBRElement(newBRElement);
+      if (afterBRElement.IsSet()) {
+        DebugOnly<bool> advanced = afterBRElement.AdvanceOffset();
+        NS_WARNING_ASSERTION(advanced,
+                             "Failed to advance offset after the <br> element");
+        ErrorResult error;
+        SelectionRefPtr()->Collapse(afterBRElement, error);
+        NS_WARNING_ASSERTION(
+            !error.Failed(),
+            "Failed to collapse selection after the <br> element");
+      } else {
+        NS_WARNING("The <br> node is not in the DOM tree?");
+      }
+      break;
+    }
+    case ePrevious: {
+      SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
+      // Collapse selection at the <br> node.
+      EditorRawDOMPoint atBRElement(newBRElement);
+      if (atBRElement.IsSet()) {
+        ErrorResult error;
+        SelectionRefPtr()->Collapse(atBRElement, error);
+        NS_WARNING_ASSERTION(
+            !error.Failed(),
+            "Failed to collapse selection at the <br> element");
+      } else {
+        NS_WARNING("The <br> node is not in the DOM tree?");
+      }
+      break;
+    }
+    default:
+      NS_WARNING(
+          "aSelect has invalid value, the caller need to set selection "
+          "by itself");
+      break;
+  }
+
+  return newBRElement.forget();
+}
+
 void HTMLEditor::ContentAppended(nsIContent* aFirstNewContent) {
   DoContentInserted(aFirstNewContent, eAppended);
 }
 
 void HTMLEditor::ContentInserted(nsIContent* aChild) {
   DoContentInserted(aChild, eInserted);
 }
 
@@ -3611,18 +3673,18 @@ void HTMLEditor::DoContentInserted(nsICo
 
   if (ShouldReplaceRootElement()) {
     UpdateRootElement();
     nsContentUtils::AddScriptRunner(NewRunnableMethod(
         "HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged));
   }
   // We don't need to handle our own modifications
   else if (!GetTopLevelEditSubAction() && container->IsEditable()) {
-    if (IsMozEditorBogusNode(aChild)) {
-      // Ignore insertion of the bogus node
+    if (EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) {
+      // Ignore insertion of the padding <br> element.
       return;
     }
     RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
     if (htmlRules) {
       htmlRules->DocumentModified();
     }
 
     // Update spellcheck for only the newly-inserted node (bug 743819)
@@ -3657,18 +3719,18 @@ void HTMLEditor::ContentRemoved(nsIConte
 
   if (SameCOMIdentity(aChild, mRootElement)) {
     mRootElement = nullptr;
     nsContentUtils::AddScriptRunner(NewRunnableMethod(
         "HTMLEditor::NotifyRootChanged", this, &HTMLEditor::NotifyRootChanged));
     // We don't need to handle our own modifications
   } else if (!GetTopLevelEditSubAction() &&
              aChild->GetParentNode()->IsEditable()) {
-    if (aChild && IsMozEditorBogusNode(aChild)) {
-      // Ignore removal of the bogus node
+    if (aChild && EditorBase::IsPaddingBRElementForEmptyEditor(*aChild)) {
+      // Ignore removal of the padding <br> element for empty editor.
       return;
     }
 
     RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
     if (htmlRules) {
       htmlRules->DocumentModified();
     }
   }
@@ -3746,17 +3808,17 @@ nsresult HTMLEditor::SelectEntireDocumen
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // If we're empty, don't select all children because that would select the
-  // bogus node.
+  // padding <br> element for empty editor.
   if (rules->DocumentIsEmpty()) {
     nsresult rv = SelectionRefPtr()->Collapse(rootElement, 0);
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to move caret to start of the editor root element");
     return rv;
   }
 
@@ -3975,17 +4037,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     // 3) first child of aNode is a block OR
     // 4) either is null
 
     nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling) &&
         !sibling->IsHTMLElement(nsGkAtoms::br) && !IsBlockNode(child)) {
       // Insert br node
       RefPtr<Element> brElement =
-          InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
+          InsertBRElementWithTransaction(EditorDOMPoint(&aElement, 0));
       if (NS_WARN_IF(!brElement)) {
         return NS_ERROR_FAILURE;
       }
     }
 
     // We need a br at end unless:
     // 1) following sibling of aNode is a block, OR
     // 2) last child of aNode is a block, OR
@@ -3995,17 +4057,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     sibling = GetNextHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling)) {
       child = GetLastEditableChild(aElement);
       MOZ_ASSERT(child, "aNode has first editable child but not last?");
       if (!IsBlockNode(child) && !child->IsHTMLElement(nsGkAtoms::br)) {
         // Insert br node
         EditorDOMPoint endOfNode;
         endOfNode.SetToEndOf(&aElement);
-        RefPtr<Element> brElement = InsertBrElementWithTransaction(endOfNode);
+        RefPtr<Element> brElement = InsertBRElementWithTransaction(endOfNode);
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   } else {
     // The case of aNode being empty.  We need a br at start unless:
     // 1) previous sibling of aNode is a block, OR
@@ -4016,17 +4078,17 @@ nsresult HTMLEditor::RemoveBlockContaine
     nsCOMPtr<nsIContent> sibling = GetPriorHTMLSibling(&aElement);
     if (sibling && !IsBlockNode(sibling) &&
         !sibling->IsHTMLElement(nsGkAtoms::br)) {
       sibling = GetNextHTMLSibling(&aElement);
       if (sibling && !IsBlockNode(sibling) &&
           !sibling->IsHTMLElement(nsGkAtoms::br)) {
         // Insert br node
         RefPtr<Element> brElement =
-            InsertBrElementWithTransaction(EditorDOMPoint(&aElement, 0));
+            InsertBRElementWithTransaction(EditorDOMPoint(&aElement, 0));
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
       }
     }
   }
 
   // Now remove container
@@ -4740,17 +4802,17 @@ nsresult HTMLEditor::CopyLastEditableChi
 
   if (!firstClonsedElement) {
     // XXX Even if no inline elements are cloned, shouldn't we create new
     //     <br> element for aNewBlock?
     return NS_OK;
   }
 
   RefPtr<Element> brElement =
-      InsertBrElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
+      InsertBRElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
   if (NS_WARN_IF(!brElement)) {
     return NS_ERROR_FAILURE;
   }
   *aNewBrElement = brElement.forget();
   return NS_OK;
 }
 
 nsresult HTMLEditor::GetElementOrigin(Element& aElement, int32_t& aX,
@@ -5247,17 +5309,18 @@ void HTMLEditor::OnModifyDocument() {
   MOZ_ASSERT(mRules);
 
   RefPtr<HTMLEditRules> htmlRules = mRules->AsHTMLEditRules();
   if (IsEditActionDataAvailable()) {
     htmlRules->OnModifyDocument();
     return;
   }
 
-  AutoEditActionDataSetter editActionData(*this, EditAction::eCreateBogusNode);
+  AutoEditActionDataSetter editActionData(
+      *this, EditAction::eCreatePaddingBRElementForEmptyEditor);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return;
   }
 
   htmlRules->OnModifyDocument();
 }
 
 }  // namespace mozilla
--- a/editor/libeditor/HTMLEditor.h
+++ b/editor/libeditor/HTMLEditor.h
@@ -583,16 +583,34 @@ class HTMLEditor final : public TextEdit
    * part of handling edit actions are allowed to call the following protected
    * methods.  However, those methods won't prepare caches of some objects
    * which are necessary for them.  So, if you want some following methods
    * to do that for you, you need to create a wrapper method in public scope
    * and call it.
    ****************************************************************************/
 
   /**
+   * InsertBRElementWithTransaction() creates a <br> element and inserts it
+   * before aPointToInsert.  Then, tries to collapse selection at or after the
+   * new <br> node if aSelect is not eNone.
+   *
+   * @param aPointToInsert      The DOM point where should be <br> node inserted
+   *                            before.
+   * @param aSelect             If eNone, this won't change selection.
+   *                            If eNext, selection will be collapsed after
+   *                            the <br> element.
+   *                            If ePrevious, selection will be collapsed at
+   *                            the <br> element.
+   * @return                    The new <br> node.  If failed to create new
+   *                            <br> node, returns nullptr.
+   */
+  MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBRElementWithTransaction(
+      const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
+
+  /**
    * DeleteSelectionWithTransaction() removes selected content or content
    * around caret with transactions.
    *
    * @param aDirection          How much range should be removed.
    * @param aStripWrappers      Whether the parent blocks should be removed
    *                            when they become empty.
    */
   MOZ_CAN_RUN_SCRIPT
@@ -836,21 +854,21 @@ class HTMLEditor final : public TextEdit
    */
   bool IsVisibleTextNode(Text& aText);
 
   /**
    * aNode must be a non-null text node.
    * outIsEmptyNode must be non-null.
    */
   nsresult IsEmptyNode(nsINode* aNode, bool* outIsEmptyBlock,
-                       bool aMozBRDoesntCount = false,
+                       bool aSingleBRDoesntCount = false,
                        bool aListOrCellNotEmpty = false,
                        bool aSafeToAskFrames = false);
   nsresult IsEmptyNodeImpl(nsINode* aNode, bool* outIsEmptyBlock,
-                           bool aMozBRDoesntCount, bool aListOrCellNotEmpty,
+                           bool aSingleBRDoesntCount, bool aListOrCellNotEmpty,
                            bool aSafeToAskFrames, bool* aSeenBR);
 
   static bool HasAttributes(Element* aElement) {
     MOZ_ASSERT(aElement);
     uint32_t attrCount = aElement->GetAttrCount();
     return attrCount > 1 ||
            (1 == attrCount &&
             !aElement->GetAttrNameAt(0)->Equals(nsGkAtoms::mozdirty));
--- a/editor/libeditor/TextEditRules.cpp
+++ b/editor/libeditor/TextEditRules.cpp
@@ -11,16 +11,17 @@
 #include "mozilla/EditAction.h"
 #include "mozilla/EditorDOMPoint.h"
 #include "mozilla/EditorUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEditor.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "mozilla/dom/NodeFilterBinding.h"
 #include "mozilla/dom/NodeIterator.h"
 #include "mozilla/dom/Selection.h"
 #include "nsAString.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsCRTGlue.h"
 #include "nsComponentManagerUtils.h"
@@ -57,29 +58,29 @@ using namespace dom;
 
 /********************************************************
  * mozilla::TextEditRules
  ********************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TextEditRules)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TextEditRules)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBogusNode)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPaddingBRElementForEmptyEditor)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedSelectionNode)
   if (HTMLEditRules* htmlEditRules = tmp->AsHTMLEditRules()) {
     HTMLEditRules* tmp = htmlEditRules;
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocChangeRange)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mUtilRange)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mNewBlock)
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRangeItem)
   }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TextEditRules)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBogusNode)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPaddingBRElementForEmptyEditor)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCachedSelectionNode)
   if (HTMLEditRules* htmlEditRules = tmp->AsHTMLEditRules()) {
     HTMLEditRules* tmp = htmlEditRules;
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocChangeRange)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUtilRange)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNewBlock)
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRangeItem)
   }
@@ -98,17 +99,17 @@ TextEditRules::TextEditRules()
       mDeleteBidiImmediately(false),
       mIsHTMLEditRules(false),
       mTopLevelEditSubAction(EditSubAction::eNone) {
   InitFields();
 }
 
 void TextEditRules::InitFields() {
   mTextEditor = nullptr;
-  mBogusNode = nullptr;
+  mPaddingBRElementForEmptyEditor = nullptr;
   mCachedSelectionNode = nullptr;
   mCachedSelectionOffset = 0;
   mActionNesting = 0;
   mLockRulesSniffing = false;
   mDidExplicitlySetInterline = false;
   mDeleteBidiImmediately = false;
   mTopLevelEditSubAction = EditSubAction::eNone;
 }
@@ -134,17 +135,17 @@ nsresult TextEditRules::Init(TextEditor*
   InitFields();
 
   // We hold a non-refcounted reference back to our editor.
   mTextEditor = aTextEditor;
   AutoSafeEditorData setData(*this, *mTextEditor);
 
   // Put in a magic <br> if needed. This method handles null selection,
   // which should never happen anyway
-  nsresult rv = CreateBogusNodeIfNeeded();
+  nsresult rv = CreatePaddingBRElementForEmptyEditorIfNeeded();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   // If the selection hasn't been set up yet, set it up collapsed to the end of
   // our editable content.
   if (!SelectionRefPtr()->RangeCount()) {
     rv = TextEditorRef().CollapseSelectionToEnd();
@@ -238,29 +239,29 @@ nsresult TextEditRules::AfterEdit(EditSu
 
     // if only trailing <br> remaining remove it
     rv = RemoveRedundantTrailingBR();
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // detect empty doc
-    rv = CreateBogusNodeIfNeeded();
+    rv = CreatePaddingBRElementForEmptyEditorIfNeeded();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     // ensure trailing br node
     rv = CreateTrailingBRIfNeeded();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    // Collapse the selection to the trailing moz-<br> if it's at the end of
-    // our text node.
+    // Collapse the selection to the trailing padding <br> element for empty
+    // last line if it's at the end of our text node.
     rv = CollapseSelectionToTrailingBRIfNeeded();
     if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to selection to after the text node in TextEditor");
   }
@@ -344,17 +345,17 @@ nsresult TextEditRules::DidDoAction(Edit
     case EditSubAction::eRedo:
       return DidRedo(aResult);
     default:
       // Don't fail on transactions we don't handle here!
       return NS_OK;
   }
 }
 
-bool TextEditRules::DocumentIsEmpty() {
+bool TextEditRules::DocumentIsEmpty() const {
   bool retVal = false;
   if (!mTextEditor || NS_FAILED(mTextEditor->IsEmpty(&retVal))) {
     retVal = true;
   }
 
   return retVal;
 }
 
@@ -373,29 +374,32 @@ nsresult TextEditRules::WillInsert(bool*
     *aCancel = false;
   }
 
   if (IsPasswordEditor() && IsMaskingPassword()) {
     TextEditorRef().MaskAllCharacters();
   }
 
   // check for the magic content node and delete it if it exists
-  if (!mBogusNode) {
+  if (!mPaddingBRElementForEmptyEditor) {
     return NS_OK;
   }
 
-  // A mutation event listener may recreate bogus node again during the
-  // call of DeleteNodeWithTransaction().  So, move it first.
-  nsCOMPtr<nsIContent> bogusNode(std::move(mBogusNode));
-  DebugOnly<nsresult> rv =
-      MOZ_KnownLive(TextEditorRef()).DeleteNodeWithTransaction(*bogusNode);
+  // A mutation event listener may recreate padding <br> element for empty
+  // editor again during the call of DeleteNodeWithTransaction().  So, move
+  // it first.
+  RefPtr<HTMLBRElement> paddingBRElement(
+      std::move(mPaddingBRElementForEmptyEditor));
+  DebugOnly<nsresult> rv = MOZ_KnownLive(TextEditorRef())
+                               .DeleteNodeWithTransaction(*paddingBRElement);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
-  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to remove the bogus node");
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "Failed to remove the padding <br> element");
   return NS_OK;
 }
 
 EditActionResult TextEditRules::WillInsertLineBreak(int32_t aMaxLength) {
   MOZ_ASSERT(IsEditorDataAvailable());
   MOZ_ASSERT(!IsSingleLineEditor());
 
   CANCEL_OPERATION_AND_RETURN_EDIT_ACTION_RESULT_IF_READONLY_OF_DISABLED
@@ -493,34 +497,36 @@ EditActionResult TextEditRules::WillInse
 
   return EditActionHandled();
 }
 
 nsresult TextEditRules::CollapseSelectionToTrailingBRIfNeeded() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // we only need to execute the stuff below if we are a plaintext editor.
-  // html editors have a different mechanism for putting in mozBR's
-  // (because there are a bunch more places you have to worry about it in html)
+  // html editors have a different mechanism for putting in padding <br>
+  // element's (because there are a bunch more places you have to worry about
+  // it in html)
   if (!IsPlaintextEditor()) {
     return NS_OK;
   }
 
   // If there is no selection ranges, we should set to the end of the editor.
   // This is usually performed in TextEditRules::Init(), however, if the
   // editor is reframed, this may be called by AfterEdit().
   if (!SelectionRefPtr()->RangeCount()) {
     TextEditorRef().CollapseSelectionToEnd();
     if (NS_WARN_IF(!CanHandleEditAction())) {
       return NS_ERROR_EDITOR_DESTROYED;
     }
   }
 
   // If we are at the end of the <textarea> element, we need to set the
-  // selection to stick to the moz-<br> at the end of the <textarea>.
+  // selection to stick to the padding <br> element for empty last line at the
+  // end of the <textarea>.
   EditorRawDOMPoint selectionStartPoint(
       EditorBase::GetStartPoint(*SelectionRefPtr()));
   if (NS_WARN_IF(!selectionStartPoint.IsSet())) {
     return NS_ERROR_FAILURE;
   }
 
   // Nothing to do if we're not at the end of the text node.
   if (!selectionStartPoint.IsInTextNode() ||
@@ -533,17 +539,17 @@ nsresult TextEditRules::CollapseSelectio
     return NS_ERROR_NULL_POINTER;
   }
   nsINode* parentNode = selectionStartPoint.GetContainer()->GetParentNode();
   if (parentNode != rootElement) {
     return NS_OK;
   }
 
   nsINode* nextNode = selectionStartPoint.GetContainer()->GetNextSibling();
-  if (!nextNode || !TextEditUtils::IsMozBR(nextNode)) {
+  if (!nextNode || !EditorBase::IsPaddingBRElementForEmptyLastLine(*nextNode)) {
     return NS_OK;
   }
 
   EditorRawDOMPoint afterStartContainer(selectionStartPoint.GetContainer());
   if (NS_WARN_IF(!afterStartContainer.AdvanceOffset())) {
     return NS_ERROR_FAILURE;
   }
   ErrorResult error;
@@ -881,38 +887,39 @@ nsresult TextEditRules::WillSetText(bool
   nsIContent* firstChild = rootElement->GetFirstChild();
 
   // We can use this fast path only when:
   //  - we need to insert a text node.
   //  - we need to replace content of existing text node.
   // Additionally, for avoiding odd result, we should check whether we're in
   // usual condition.
   if (IsSingleLineEditor()) {
-    // If we're a single line text editor, i.e., <input>, there is only bogus-
+    // If we're a single line text editor, i.e., <input>, there is only padding
     // <br> element.  Otherwise, there should be only one text node.  But note
-    // that even if there is a bogus node, it's already been removed by
-    // WillInsert().  So, at here, there should be only one text node or no
-    // children.
+    // that even if there is a padding <br> element for empty editor, it's
+    // already been removed by WillInsert().  So, at here, there should be only
+    // one text node or no children.
     if (firstChild &&
         (!EditorBase::IsTextNode(firstChild) || firstChild->GetNextSibling())) {
       return NS_OK;
     }
   } else {
-    // If we're a multiline text editor, i.e., <textarea>, there is a moz-<br>
-    // element followed by scrollbar/resizer elements.  Otherwise, a text node
-    // is followed by them.
+    // If we're a multiline text editor, i.e., <textarea>, there is a padding
+    // <br> element for empty last line followed by scrollbar/resizer elements.
+    // Otherwise, a text node is followed by them.
     if (!firstChild) {
       return NS_OK;
     }
     if (EditorBase::IsTextNode(firstChild)) {
       if (!firstChild->GetNextSibling() ||
-          !TextEditUtils::IsMozBR(firstChild->GetNextSibling())) {
+          !EditorBase::IsPaddingBRElementForEmptyLastLine(
+              *firstChild->GetNextSibling())) {
         return NS_OK;
       }
-    } else if (!TextEditUtils::IsMozBR(firstChild)) {
+    } else if (!EditorBase::IsPaddingBRElementForEmptyLastLine(*firstChild)) {
       return NS_OK;
     }
   }
 
   // XXX Password fields accept line breaks as normal characters with this code.
   //     Is this intentional?
   nsAutoString tString(*aString);
   if (IsSingleLineEditor() && !IsPasswordEditor()) {
@@ -1006,18 +1013,19 @@ nsresult TextEditRules::WillDeleteSelect
     return NS_ERROR_INVALID_ARG;
   }
   CANCEL_OPERATION_IF_READONLY_OR_DISABLED
 
   // initialize out param
   *aCancel = false;
   *aHandled = false;
 
-  // if there is only bogus content, cancel the operation
-  if (mBogusNode) {
+  // if there is only padding <br> element for empty editor, cancel the
+  // operation.
+  if (mPaddingBRElementForEmptyEditor) {
     *aCancel = true;
     return NS_OK;
   }
   nsresult rv =
       DeleteSelectionWithTransaction(aCollapsedAction, aCancel, aHandled);
   // DeleteSelectionWithTransaction() creates SelectionBatcher.  Therefore,
   // quitting from it might cause having destroyed the editor.
   if (NS_WARN_IF(!CanHandleEditAction())) {
@@ -1146,20 +1154,20 @@ nsresult TextEditRules::DidUndo(nsresult
   }
 
   // The idea here is to see if the magic empty node has suddenly reappeared as
   // the result of the undo.  If it has, set our state so we remember it.
   // There is a tradeoff between doing here and at redo, or doing it everywhere
   // else that might care.  Since undo and redo are relatively rare, it makes
   // sense to take the (small) performance hit here.
   nsIContent* node = TextEditorRef().GetLeftmostChild(rootElement);
-  if (node && TextEditorRef().IsMozEditorBogusNode(node)) {
-    mBogusNode = node;
+  if (node && EditorBase::IsPaddingBRElementForEmptyEditor(*node)) {
+    mPaddingBRElementForEmptyEditor = static_cast<HTMLBRElement*>(node);
   } else {
-    mBogusNode = nullptr;
+    mPaddingBRElementForEmptyEditor = nullptr;
   }
   return aResult;
 }
 
 nsresult TextEditRules::WillRedo(bool* aCancel, bool* aHandled) {
   if (NS_WARN_IF(!aCancel) || NS_WARN_IF(!aHandled)) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -1183,26 +1191,26 @@ nsresult TextEditRules::DidRedo(nsresult
   }
 
   nsCOMPtr<nsIHTMLCollection> nodeList =
       rootElement->GetElementsByTagName(NS_LITERAL_STRING("br"));
   MOZ_ASSERT(nodeList);
   uint32_t len = nodeList->Length();
 
   if (len != 1) {
-    // only in the case of one br could there be the bogus node
-    mBogusNode = nullptr;
+    // only in the case of one br could there be the padding <br> element.
+    mPaddingBRElementForEmptyEditor = nullptr;
     return NS_OK;
   }
 
   Element* brElement = nodeList->Item(0);
-  if (TextEditorRef().IsMozEditorBogusNode(brElement)) {
-    mBogusNode = brElement;
+  if (EditorBase::IsPaddingBRElementForEmptyEditor(*brElement)) {
+    mPaddingBRElementForEmptyEditor = static_cast<HTMLBRElement*>(brElement);
   } else {
-    mBogusNode = nullptr;
+    mPaddingBRElementForEmptyEditor = nullptr;
   }
   return NS_OK;
 }
 
 nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
                                        nsAString* aOutString, uint32_t aFlags,
                                        bool* aCancel, bool* aHandled) {
   MOZ_ASSERT(IsEditorDataAvailable());
@@ -1216,18 +1224,19 @@ nsresult TextEditRules::WillOutputText(c
   // initialize out param
   *aCancel = false;
   *aHandled = false;
 
   if (!aOutputFormat->LowerCaseEqualsLiteral("text/plain")) {
     return NS_OK;
   }
 
-  // If there is a bogus node, there's no content.  So output empty string.
-  if (mBogusNode) {
+  // If there is a padding <br> element, there's no content.  So output empty
+  // string.
+  if (mPaddingBRElementForEmptyEditor) {
     aOutString->Truncate();
     *aHandled = true;
     return NS_OK;
   }
 
   // If it's necessary to check selection range or the editor wraps hard,
   // we need some complicated handling.  In such case, we need to use the
   // expensive path.
@@ -1274,17 +1283,19 @@ nsresult TextEditRules::WillOutputText(c
   Text* text = firstChild->GetAsText();
   nsIContent* firstChildExceptText =
       text ? firstChild->GetNextSibling() : firstChild;
   // If the DOM tree is unexpected, fall back to the expensive path.
   bool isInput = IsSingleLineEditor();
   bool isTextarea = !isInput;
   if (NS_WARN_IF(isInput && firstChildExceptText) ||
       NS_WARN_IF(isTextarea && !firstChildExceptText) ||
-      NS_WARN_IF(isTextarea && !TextEditUtils::IsMozBR(firstChildExceptText) &&
+      NS_WARN_IF(isTextarea &&
+                 !EditorBase::IsPaddingBRElementForEmptyLastLine(
+                     *firstChildExceptText) &&
                  !firstChildExceptText->IsXULElement(nsGkAtoms::scrollbar))) {
     return NS_OK;
   }
 
   // If there is no text node in the expected DOM tree, we can say that it's
   // just empty.
   if (!text) {
     aOutString->Truncate();
@@ -1297,166 +1308,154 @@ nsresult TextEditRules::WillOutputText(c
 
   *aHandled = true;
   return NS_OK;
 }
 
 nsresult TextEditRules::RemoveRedundantTrailingBR() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  // If the bogus node exists, we have no work to do
-  if (mBogusNode) {
+  // If the passing <br> element exists, we have no work to do.
+  if (mPaddingBRElementForEmptyEditor) {
     return NS_OK;
   }
 
   // Likewise, nothing to be done if we could never have inserted a trailing br
   if (IsSingleLineEditor()) {
     return NS_OK;
   }
 
   Element* rootElement = TextEditorRef().GetRoot();
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_NULL_POINTER;
   }
 
-  uint32_t childCount = rootElement->GetChildCount();
-  if (childCount > 1) {
+  if (rootElement->GetChildCount() > 1) {
     // The trailing br is redundant if it is the only remaining child node
     return NS_OK;
   }
 
-  RefPtr<nsIContent> child = rootElement->GetFirstChild();
-  if (!child || !child->IsElement()) {
-    return NS_OK;
-  }
-
-  RefPtr<Element> childElement = child->AsElement();
-  if (!TextEditUtils::IsMozBR(childElement)) {
+  RefPtr<HTMLBRElement> brElement =
+      HTMLBRElement::FromNodeOrNull(rootElement->GetFirstChild());
+  if (!brElement ||
+      !EditorBase::IsPaddingBRElementForEmptyLastLine(*brElement)) {
     return NS_OK;
   }
 
   // Rather than deleting this node from the DOM tree we should instead
-  // morph this br into the bogus node
-  childElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::type, true);
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
+  // morph this <br> element into the padding <br> element for editor.
+  mPaddingBRElementForEmptyEditor = std::move(brElement);
+  mPaddingBRElementForEmptyEditor->UnsetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
+  mPaddingBRElementForEmptyEditor->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
 
-  // set mBogusNode to be this <br>
-  mBogusNode = childElement;
-
-  // give it the bogus node attribute
-  childElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
-                        kMOZEditorBogusNodeValue, false);
   return NS_OK;
 }
 
 nsresult TextEditRules::CreateTrailingBRIfNeeded() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
   // but only if we aren't a single line edit field
   if (IsSingleLineEditor()) {
     return NS_OK;
   }
 
   Element* rootElement = TextEditorRef().GetRoot();
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIContent> lastChild = rootElement->GetLastChild();
-  // assuming CreateBogusNodeIfNeeded() has been called first
-  if (NS_WARN_IF(!lastChild)) {
+  // Assuming CreatePaddingBRElementForEmptyEditorIfNeeded() has been
+  // called first.
+  if (NS_WARN_IF(!rootElement->GetLastChild())) {
     return NS_ERROR_FAILURE;
   }
 
-  if (!lastChild->IsHTMLElement(nsGkAtoms::br)) {
+  RefPtr<HTMLBRElement> brElement =
+      HTMLBRElement::FromNode(rootElement->GetLastChild());
+  if (!brElement) {
     AutoTransactionsConserveSelection dontChangeMySelection(TextEditorRef());
     EditorDOMPoint endOfRoot;
     endOfRoot.SetToEndOf(rootElement);
-    CreateElementResult createMozBrResult = CreateMozBR(endOfRoot);
-    if (NS_WARN_IF(createMozBrResult.Failed())) {
-      return createMozBrResult.Rv();
+    CreateElementResult createPaddingBRResult =
+        MOZ_KnownLive(TextEditorRef())
+            .InsertPaddingBRElementForEmptyLastLineWithTransaction(endOfRoot);
+    if (NS_WARN_IF(createPaddingBRResult.Failed())) {
+      return createPaddingBRResult.Rv();
     }
     return NS_OK;
   }
 
-  // Check to see if the trailing BR is a former bogus node - this will have
-  // stuck around if we previously morphed a trailing node into a bogus node.
-  if (!TextEditorRef().IsMozEditorBogusNode(lastChild)) {
+  // Check to see if the trailing BR is a former padding <br> element for empty
+  // editor - this will have stuck around if we previously morphed a trailing
+  // node into a padding <br> element.
+  if (!brElement->IsPaddingForEmptyEditor()) {
     return NS_OK;
   }
 
-  // Morph it back to a mozBR
-  lastChild->AsElement()->UnsetAttr(kNameSpaceID_None,
-                                    kMOZEditorBogusNodeAttrAtom, false);
-  lastChild->AsElement()->SetAttr(kNameSpaceID_None, nsGkAtoms::type,
-                                  NS_LITERAL_STRING("_moz"), true);
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return NS_ERROR_EDITOR_DESTROYED;
-  }
+  // Morph it back to a padding <br> element for empty last line.
+  brElement->UnsetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
+  brElement->SetFlags(NS_PADDING_FOR_EMPTY_LAST_LINE);
+
   return NS_OK;
 }
 
-nsresult TextEditRules::CreateBogusNodeIfNeeded() {
+nsresult TextEditRules::CreatePaddingBRElementForEmptyEditorIfNeeded() {
   MOZ_ASSERT(IsEditorDataAvailable());
 
-  if (mBogusNode) {
+  if (mPaddingBRElementForEmptyEditor) {
     // Let's not create more than one, ok?
     return NS_OK;
   }
 
   // tell rules system to not do any post-processing
   AutoTopLevelEditSubActionNotifier maybeTopLevelEditSubAction(
-      TextEditorRef(), EditSubAction::eCreateBogusNode, nsIEditor::eNone);
+      TextEditorRef(), EditSubAction::eCreatePaddingBRElementForEmptyEditor,
+      nsIEditor::eNone);
 
   RefPtr<Element> rootElement = TextEditorRef().GetRoot();
   if (!rootElement) {
-    // We don't even have a body yet, don't insert any bogus nodes at
+    // We don't even have a body yet, don't insert any padding <br> elements at
     // this point.
     return NS_OK;
   }
 
   // Now we've got the body element. Iterate over the body element's children,
   // looking for editable content. If no editable content is found, insert the
-  // bogus node.
+  // padding <br> element.
   bool isRootEditable = TextEditorRef().IsEditable(rootElement);
   for (nsIContent* rootChild = rootElement->GetFirstChild(); rootChild;
        rootChild = rootChild->GetNextSibling()) {
-    if (TextEditorRef().IsMozEditorBogusNode(rootChild) || !isRootEditable ||
-        TextEditorRef().IsEditable(rootChild) ||
+    if (EditorBase::IsPaddingBRElementForEmptyEditor(*rootChild) ||
+        !isRootEditable || TextEditorRef().IsEditable(rootChild) ||
         TextEditorRef().IsBlockNode(rootChild)) {
       return NS_OK;
     }
   }
 
-  // Skip adding the bogus node if body is read-only.
+  // Skip adding the padding <br> element for empty editor if body
+  // is read-only.
   if (!TextEditorRef().IsModifiableNode(*rootElement)) {
     return NS_OK;
   }
 
   // Create a br.
   RefPtr<Element> newBrElement =
       TextEditorRef().CreateHTMLContent(nsGkAtoms::br);
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
   if (NS_WARN_IF(!newBrElement)) {
     return NS_ERROR_FAILURE;
   }
 
-  // set mBogusNode to be the newly created <br>
-  mBogusNode = newBrElement;
+  mPaddingBRElementForEmptyEditor =
+      static_cast<HTMLBRElement*>(newBrElement.get());
 
   // Give it a special attribute.
-  newBrElement->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom,
-                        kMOZEditorBogusNodeValue, false);
-  if (NS_WARN_IF(mBogusNode != newBrElement)) {
-    return NS_ERROR_EDITOR_UNEXPECTED_DOM_TREE;
-  }
+  newBrElement->SetFlags(NS_PADDING_FOR_EMPTY_EDITOR);
 
   // Put the node in the document.
   nsresult rv = MOZ_KnownLive(TextEditorRef())
                     .InsertNodeWithTransaction(*newBrElement,
                                                EditorDOMPoint(rootElement, 0));
   if (NS_WARN_IF(!CanHandleEditAction())) {
     return NS_ERROR_EDITOR_DESTROYED;
   }
@@ -1552,54 +1551,16 @@ nsresult TextEditRules::TruncateInsertio
           *aTruncated = true;
         }
       }
     }
   }
   return NS_OK;
 }
 
-CreateElementResult TextEditRules::CreateBRInternal(
-    const EditorDOMPoint& aPointToInsert, bool aCreateMozBR) {
-  MOZ_ASSERT(IsEditorDataAvailable());
-
-  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
-    return CreateElementResult(NS_ERROR_FAILURE);
-  }
-
-  RefPtr<Element> brElement =
-      MOZ_KnownLive(TextEditorRef())
-          .InsertBrElementWithTransaction(aPointToInsert);
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
-  }
-  if (NS_WARN_IF(!brElement)) {
-    return CreateElementResult(NS_ERROR_FAILURE);
-  }
-
-  // give it special moz attr
-  if (!aCreateMozBR) {
-    return CreateElementResult(brElement.forget());
-  }
-
-  // XXX Why do we need to set this attribute with transaction?
-  nsresult rv = MOZ_KnownLive(TextEditorRef())
-                    .SetAttributeWithTransaction(*brElement, *nsGkAtoms::type,
-                                                 NS_LITERAL_STRING("_moz"));
-  // XXX Don't we need to remove the new <br> element from the DOM tree
-  //     in these case?
-  if (NS_WARN_IF(!CanHandleEditAction())) {
-    return CreateElementResult(NS_ERROR_EDITOR_DESTROYED);
-  }
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return CreateElementResult(NS_ERROR_FAILURE);
-  }
-  return CreateElementResult(brElement.forget());
-}
-
 bool TextEditRules::IsPasswordEditor() const {
   return mTextEditor ? mTextEditor->IsPasswordEditor() : false;
 }
 
 bool TextEditRules::IsMaskingPassword() const {
   MOZ_ASSERT(IsPasswordEditor());
   return mTextEditor ? mTextEditor->IsMaskingPassword() : true;
 }
--- a/editor/libeditor/TextEditRules.h
+++ b/editor/libeditor/TextEditRules.h
@@ -21,16 +21,17 @@
 
 namespace mozilla {
 
 class AutoLockRulesSniffing;
 class EditSubActionInfo;
 class HTMLEditor;
 class HTMLEditRules;
 namespace dom {
+class HTMLBRElement;
 class Selection;
 }  // namespace dom
 
 /**
  * Object that encapsulates HTML text-specific editing rules.
  *
  * To be a good citizen, edit rules must live by these restrictions:
  * 1. All data manipulation is through the editor.
@@ -95,17 +96,17 @@ class TextEditRules {
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   virtual nsresult DidDoAction(EditSubActionInfo& aInfo, nsresult aResult);
 
   /**
    * Return false if the editor has non-empty text nodes or non-text
    * nodes.  Otherwise, i.e., there is no meaningful content,
    * return true.
    */
-  virtual bool DocumentIsEmpty();
+  virtual bool DocumentIsEmpty() const;
 
   bool DontEchoPassword() const;
 
  protected:
   virtual ~TextEditRules() = default;
 
  public:
   /**
@@ -125,17 +126,19 @@ class TextEditRules {
    *     only remove the leading and trailing newlines.
    *   nsIPlaintextEditor::eNewlinesPasteToFirst (1) or any other value:
    *     remove the first newline and all characters following it.
    *
    * @param aString the string to be modified in place.
    */
   void HandleNewLines(nsString& aString);
 
-  bool HasBogusNode() { return !!mBogusNode; }
+  bool HasPaddingBRElementForEmptyEditor() const {
+    return !!mPaddingBRElementForEmptyEditor;
+  }
 
  protected:
   void InitFields();
 
   // TextEditRules implementation methods
 
   /**
    * Called before inserting text.
@@ -258,108 +261,57 @@ class TextEditRules {
   MOZ_MUST_USE nsresult RemoveRedundantTrailingBR();
 
   /**
    * Creates a trailing break in the text doc if there is not one already.
    */
   MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult CreateTrailingBRIfNeeded();
 
   /**
-   * Creates a bogus <br> node if the root element has no editable content.
+   * Creates a padding <br> element for empty editor if the root element has no
+   * editable content.
    */
-  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult CreateBogusNodeIfNeeded();
+  MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult
+  CreatePaddingBRElementForEmptyEditorIfNeeded();
 
   /**
    * Returns a truncated insertion string if insertion would place us over
    * aMaxLength
    */
   nsresult TruncateInsertionIfNeeded(const nsAString* aInString,
                                      nsAString* aOutString, int32_t aMaxLength,
                                      bool* aTruncated);
 
-  /**
-   * Create a normal <br> element and insert it to aPointToInsert.
-   *
-   * @param aPointToInsert  The point where the new <br> element will be
-   *                        inserted.
-   * @return                Returns created <br> element or an error code
-   *                        if couldn't create new <br> element.
-   */
-  MOZ_CAN_RUN_SCRIPT CreateElementResult
-  CreateBR(const EditorDOMPoint& aPointToInsert) {
-    CreateElementResult ret = CreateBRInternal(aPointToInsert, false);
-#ifdef DEBUG
-    // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
-    if (!CanHandleEditAction()) {
-      MOZ_ASSERT(ret.Rv() == NS_ERROR_EDITOR_DESTROYED);
-    }
-#endif  // #ifdef DEBUG
-    return ret;
-  }
-
-  /**
-   * Create a moz-<br> element and insert it to aPointToInsert.
-   *
-   * @param aPointToInsert  The point where the new moz-<br> element will be
-   *                        inserted.
-   * @return                Returns created <br> element or an error code
-   *                        if couldn't create new <br> element.
-   */
-  MOZ_CAN_RUN_SCRIPT CreateElementResult
-  CreateMozBR(const EditorDOMPoint& aPointToInsert) {
-    CreateElementResult ret = CreateBRInternal(aPointToInsert, true);
-#ifdef DEBUG
-    // If editor is destroyed, it must return NS_ERROR_EDITOR_DESTROYED.
-    if (!CanHandleEditAction()) {
-      MOZ_ASSERT(ret.Rv() == NS_ERROR_EDITOR_DESTROYED);
-    }
-#endif  // #ifdef DEBUG
-    return ret;
-  }
-
   void UndefineCaretBidiLevel();
 
   nsresult CheckBidiLevelForDeletion(const EditorRawDOMPoint& aSelectionPoint,
                                      nsIEditor::EDirection aAction,
                                      bool* aCancel);
 
   /**
    * CollapseSelectionToTrailingBRIfNeeded() collapses selection after the
    * text node if:
    * - the editor is text editor
    * - and Selection is collapsed at the end of the text node
-   * - and the text node is followed by moz-<br>.
+   * - and the text node is followed by a padding <br> element for empty last
+   *   line.
    */
   MOZ_MUST_USE nsresult CollapseSelectionToTrailingBRIfNeeded();
 
   bool IsPasswordEditor() const;
   bool IsMaskingPassword() const;
   bool IsSingleLineEditor() const;
   bool IsPlaintextEditor() const;
   bool IsReadonly() const;
   bool IsDisabled() const;
   bool IsMailEditor() const;
 
  private:
   TextEditor* MOZ_NON_OWNING_REF mTextEditor;
 
-  /**
-   * Create a normal <br> element or a moz-<br> element and insert it to
-   * aPointToInsert.
-   *
-   * @param aParentToInsert     The point where the new <br> element will be
-   *                            inserted.
-   * @param aCreateMozBR        true if the caller wants to create a moz-<br>
-   *                            element.  Otherwise, false.
-   * @return                    Returns created <br> element and error code.
-   *                            If it succeeded, never returns nullptr.
-   */
-  MOZ_CAN_RUN_SCRIPT CreateElementResult
-  CreateBRInternal(const EditorDOMPoint& aPointToInsert, bool aCreateMozBR);
-
  protected:
   /**
    * AutoSafeEditorData grabs editor instance and related instances during
    * handling an edit action.  When this is created, its pointer is set to
    * the mSafeData, and this guarantees the lifetime of grabbing objects
    * until it's destroyed.
    */
   class MOZ_STACK_CLASS AutoSafeEditorData {
@@ -431,18 +383,19 @@ class TextEditRules {
   /**
    * GetTextNodeAroundSelectionStartContainer() may return a Text node around
    * start container of Selection.  If current selection container is not
    * a text node, this will look for descendants and next siblings of the
    * container.
    */
   inline already_AddRefed<nsINode> GetTextNodeAroundSelectionStartContainer();
 
-  // Magic node acts as placeholder in empty doc.
-  nsCOMPtr<nsIContent> mBogusNode;
+  // mPaddingBRElementForEmptyEditor should be used for placing caret
+  // at proper position when editor is empty.
+  RefPtr<dom::HTMLBRElement> mPaddingBRElementForEmptyEditor;
   // Cached selected node.
   nsCOMPtr<nsINode> mCachedSelectionNode;
   // Cached selected offset.
   uint32_t mCachedSelectionOffset;
   uint32_t mActionNesting;
   bool mLockRulesSniffing;
   bool mDidExplicitlySetInterline;
   // In bidirectional text, delete characters not visually adjacent to the
--- a/editor/libeditor/TextEditUtils.cpp
+++ b/editor/libeditor/TextEditUtils.cpp
@@ -3,28 +3,31 @@
  * 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 "TextEditUtils.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TextEditor.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "nsAString.h"
 #include "nsCOMPtr.h"
 #include "nsCaseTreatment.h"
 #include "nsDebug.h"
 #include "nsError.h"
 #include "nsGkAtoms.h"
 #include "nsNameSpaceManager.h"
 #include "nsLiteralString.h"
 #include "nsString.h"
 
 namespace mozilla {
 
+using namespace dom;
+
 /******************************************************************************
  * TextEditUtils
  ******************************************************************************/
 
 /**
  * IsBody() returns true if aNode is an html body node.
  */
 bool TextEditUtils::IsBody(nsINode* aNode) {
@@ -35,38 +38,16 @@ bool TextEditUtils::IsBody(nsINode* aNod
 /**
  * IsBreak() returns true if aNode is an html break node.
  */
 bool TextEditUtils::IsBreak(nsINode* aNode) {
   MOZ_ASSERT(aNode);
   return aNode->IsHTMLElement(nsGkAtoms::br);
 }
 
-/**
- * IsMozBR() returns true if aNode is an html br node with |type = _moz|.
- */
-bool TextEditUtils::IsMozBR(nsINode* aNode) {
-  MOZ_ASSERT(aNode);
-  return aNode->IsHTMLElement(nsGkAtoms::br) &&
-         aNode->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
-                                         NS_LITERAL_STRING("_moz"),
-                                         eIgnoreCase);
-}
-
-/**
- * HasMozAttr() returns true if aNode has type attribute and its value is
- * |_moz|. (Used to indicate div's and br's we use in mail compose rules)
- */
-bool TextEditUtils::HasMozAttr(nsINode* aNode) {
-  MOZ_ASSERT(aNode);
-  return aNode->IsElement() && aNode->AsElement()->AttrValueIs(
-                                   kNameSpaceID_None, nsGkAtoms::type,
-                                   NS_LITERAL_STRING("_moz"), eIgnoreCase);
-}
-
 /******************************************************************************
  * AutoEditInitRulesTrigger
  ******************************************************************************/
 
 AutoEditInitRulesTrigger::AutoEditInitRulesTrigger(TextEditor* aTextEditor,
                                                    nsresult& aResult)
     : mTextEditor(aTextEditor), mResult(aResult) {
   if (mTextEditor) {
--- a/editor/libeditor/TextEditUtils.h
+++ b/editor/libeditor/TextEditUtils.h
@@ -14,18 +14,16 @@ namespace mozilla {
 
 class TextEditor;
 
 class TextEditUtils final {
  public:
   // from TextEditRules:
   static bool IsBody(nsINode* aNode);
   static bool IsBreak(nsINode* aNode);
-  static bool IsMozBR(nsINode* aNode);
-  static bool HasMozAttr(nsINode* aNode);
 };
 
 /***************************************************************************
  * stack based helper class for detecting end of editor initialization, in
  * order to trigger "end of init" initialization of the edit rules.
  */
 class AutoEditInitRulesTrigger final {
  private:
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -435,115 +435,16 @@ nsresult TextEditor::InsertLineBreakAsAc
   AutoPlaceholderBatch treatAsOneTransaction(*this, *nsGkAtoms::TypingTxnName);
   nsresult rv = InsertLineBreakAsSubAction();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
-already_AddRefed<Element> TextEditor::InsertBrElementWithTransaction(
-    const EditorDOMPoint& aPointToInsert, EDirection aSelect /* = eNone */) {
-  MOZ_ASSERT(IsEditActionDataAvailable());
-
-  if (NS_WARN_IF(!aPointToInsert.IsSet())) {
-    return nullptr;
-  }
-
-  // We need to insert a <br> node.
-  RefPtr<Element> newBRElement;
-  if (aPointToInsert.IsInTextNode()) {
-    EditorDOMPoint pointInContainer;
-    if (aPointToInsert.IsStartOfContainer()) {
-      // Insert before the text node.
-      pointInContainer.Set(aPointToInsert.GetContainer());
-      if (NS_WARN_IF(!pointInContainer.IsSet())) {
-        return nullptr;
-      }
-    } else if (aPointToInsert.IsEndOfContainer()) {
-      // Insert after the text node.
-      pointInContainer.Set(aPointToInsert.GetContainer());
-      if (NS_WARN_IF(!pointInContainer.IsSet())) {
-        return nullptr;
-      }
-      DebugOnly<bool> advanced = pointInContainer.AdvanceOffset();
-      NS_WARNING_ASSERTION(advanced,
-                           "Failed to advance offset to after the text node");
-    } else {
-      MOZ_DIAGNOSTIC_ASSERT(aPointToInsert.IsSetAndValid());
-      // Unfortunately, we need to split the text node at the offset.
-      ErrorResult error;
-      nsCOMPtr<nsIContent> newLeftNode =
-          SplitNodeWithTransaction(aPointToInsert, error);
-      if (NS_WARN_IF(error.Failed())) {
-        error.SuppressException();
-        return nullptr;
-      }
-      Unused << newLeftNode;
-      // Insert new <br> before the right node.
-      pointInContainer.Set(aPointToInsert.GetContainer());
-    }
-    // Create a <br> node.
-    newBRElement = CreateNodeWithTransaction(*nsGkAtoms::br, pointInContainer);
-    if (NS_WARN_IF(!newBRElement)) {
-      return nullptr;
-    }
-  } else {
-    newBRElement = CreateNodeWithTransaction(*nsGkAtoms::br, aPointToInsert);
-    if (NS_WARN_IF(!newBRElement)) {
-      return nullptr;
-    }
-  }
-
-  switch (aSelect) {
-    case eNone:
-      break;
-    case eNext: {
-      SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
-      // Collapse selection after the <br> node.
-      EditorRawDOMPoint afterBRElement(newBRElement);
-      if (afterBRElement.IsSet()) {
-        DebugOnly<bool> advanced = afterBRElement.AdvanceOffset();
-        NS_WARNING_ASSERTION(advanced,
-                             "Failed to advance offset after the <br> element");
-        ErrorResult error;
-        SelectionRefPtr()->Collapse(afterBRElement, error);
-        NS_WARNING_ASSERTION(
-            !error.Failed(),
-            "Failed to collapse selection after the <br> element");
-      } else {
-        NS_WARNING("The <br> node is not in the DOM tree?");
-      }
-      break;
-    }
-    case ePrevious: {
-      SelectionRefPtr()->SetInterlinePosition(true, IgnoreErrors());
-      // Collapse selection at the <br> node.
-      EditorRawDOMPoint atBRElement(newBRElement);
-      if (atBRElement.IsSet()) {
-        ErrorResult error;
-        SelectionRefPtr()->Collapse(atBRElement, error);
-        NS_WARNING_ASSERTION(
-            !error.Failed(),
-            "Failed to collapse selection at the <br> element");
-      } else {
-        NS_WARNING("The <br> node is not in the DOM tree?");
-      }
-      break;
-    }
-    default:
-      NS_WARNING(
-          "aSelect has invalid value, the caller need to set selection "
-          "by itself");
-      break;
-  }
-
-  return newBRElement.forget();
-}
-
 nsresult TextEditor::ExtendSelectionForDelete(nsIEditor::EDirection* aAction) {
   MOZ_ASSERT(IsEditActionDataAvailable());
 
   bool bCollapsed = SelectionRefPtr()->IsCollapsed();
 
   if (*aAction == eNextWord || *aAction == ePreviousWord ||
       (*aAction == eNext && bCollapsed) ||
       (*aAction == ePrevious && bCollapsed) || *aAction == eToBeginningOfLine ||
@@ -1436,22 +1337,23 @@ already_AddRefed<Element> TextEditor::Ge
 
 nsresult TextEditor::IsEmpty(bool* aIsEmpty) const {
   if (NS_WARN_IF(!mRules)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   *aIsEmpty = true;
 
-  if (mRules->HasBogusNode()) {
+  if (mRules->HasPaddingBRElementForEmptyEditor()) {
     return NS_OK;
   }
 
-  // Even if there is no bogus node, we should be detected as empty editor
-  // if all the children are text nodes and these have no content.
+  // Even if there is no padding <br> element for empty editor, we should be
+  // detected as empty editor if all the children are text nodes and these
+  // have no content.
   Element* rootElement = GetRoot();
   if (!rootElement) {
     // XXX Why don't we return an error in such case??
     return NS_OK;
   }
 
   for (nsIContent* child = rootElement->GetFirstChild(); child;
        child = child->GetNextSibling()) {
@@ -1476,17 +1378,18 @@ TextEditor::GetDocumentIsEmpty(bool* aDo
 
 NS_IMETHODIMP
 TextEditor::GetTextLength(int32_t* aCount) {
   MOZ_ASSERT(aCount);
 
   // initialize out params
   *aCount = 0;
 
-  // special-case for empty document, to account for the bogus node
+  // special-case for empty document, to account for the padding <br> element
+  // for empty editor.
   bool isEmpty = false;
   nsresult rv = IsEmpty(&isEmpty);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (isEmpty) {
     return NS_OK;
   }
@@ -2158,17 +2061,17 @@ nsresult TextEditor::SelectEntireDocumen
   if (NS_WARN_IF(!rootElement)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Protect the edit rules object from dying
   RefPtr<TextEditRules> rules(mRules);
 
   // If we're empty, don't select all children because that would select the
-  // bogus node.
+  // padding <br> element for empty editor.
   if (rules->DocumentIsEmpty()) {
     nsresult rv = SelectionRefPtr()->Collapse(rootElement, 0);
     NS_WARNING_ASSERTION(
         NS_SUCCEEDED(rv),
         "Failed to move caret to start of the editor root element");
     return rv;
   }
 
@@ -2178,24 +2081,24 @@ nsresult TextEditor::SelectEntireDocumen
                                             getter_AddRefs(childNode));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   if (childNode) {
     childNode = childNode->GetPreviousSibling();
   }
 
-  if (childNode && TextEditUtils::IsMozBR(childNode)) {
+  if (childNode && EditorBase::IsPaddingBRElementForEmptyLastLine(*childNode)) {
     ErrorResult error;
     MOZ_KnownLive(SelectionRefPtr())
         ->SetStartAndEndInLimiter(RawRangeBoundary(rootElement, 0),
                                   EditorRawDOMPoint(childNode), error);
     NS_WARNING_ASSERTION(!error.Failed(),
                          "Failed to select all children of the editor root "
-                         "element except the moz-<br> element");
+                         "element except the padding <br> element");
     return error.StealNSResult();
   }
 
   ErrorResult error;
   SelectionRefPtr()->SelectAllChildren(*rootElement, error);
   NS_WARNING_ASSERTION(
       !error.Failed(),
       "Failed to select all children of the editor root element");
--- a/editor/libeditor/TextEditor.h
+++ b/editor/libeditor/TextEditor.h
@@ -162,19 +162,19 @@ class TextEditor : public EditorBase,
 
   // Overrides of EditorBase
   MOZ_CAN_RUN_SCRIPT
   virtual nsresult Init(Document& aDoc, Element* aRoot,
                         nsISelectionController* aSelCon, uint32_t aFlags,
                         const nsAString& aValue) override;
 
   /**
-   * IsEmpty() checks whether the editor is empty.  If editor has only bogus
-   * node, returns true.  If editor's root element has non-empty text nodes or
-   * other nodes like <br>, returns false.
+   * IsEmpty() checks whether the editor is empty.  If editor has only padding
+   * <br> element for empty editor, returns true.  If editor's root element has
+   * non-empty text nodes or other nodes like <br>, returns false.
    */
   nsresult IsEmpty(bool* aIsEmpty) const;
   bool IsEmpty() const {
     bool isEmpty = false;
     nsresult rv = IsEmpty(&isEmpty);
     NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                          "Checking whether the editor is empty failed");
     return NS_SUCCEEDED(rv) && isEmpty;
@@ -462,34 +462,16 @@ class TextEditor : public EditorBase,
    * ReplaceSelectionAsSubAction() replaces selection with aString.
    *
    * @param aString    The string to replace.
    */
   MOZ_CAN_RUN_SCRIPT
   nsresult ReplaceSelectionAsSubAction(const nsAString& aString);
 
   /**
-   * InsertBrElementWithTransaction() creates a <br> element and inserts it
-   * before aPointToInsert.  Then, tries to collapse selection at or after the
-   * new <br> node if aSelect is not eNone.
-   *
-   * @param aPointToInsert      The DOM point where should be <br> node inserted
-   *                            before.
-   * @param aSelect             If eNone, this won't change selection.
-   *                            If eNext, selection will be collapsed after
-   *                            the <br> element.
-   *                            If ePrevious, selection will be collapsed at
-   *                            the <br> element.
-   * @return                    The new <br> node.  If failed to create new
-   *                            <br> node, returns nullptr.
-   */
-  MOZ_CAN_RUN_SCRIPT already_AddRefed<Element> InsertBrElementWithTransaction(
-      const EditorDOMPoint& aPointToInsert, EDirection aSelect = eNone);
-
-  /**
    * Extends the selection for given deletion operation
    * If done, also update aAction to what's actually left to do after the
    * extension.
    */
   nsresult ExtendSelectionForDelete(nsIEditor::EDirection* aAction);
 
   static void GetDefaultEditorPrefs(int32_t& aNewLineHandling,
                                     int32_t& aCaretStyle);
--- a/editor/libeditor/WSRunObject.cpp
+++ b/editor/libeditor/WSRunObject.cpp
@@ -232,17 +232,17 @@ already_AddRefed<Element> WSRunObject::I
       if (NS_WARN_IF(NS_FAILED(rv))) {
         return nullptr;
       }
     }
   }
 
   RefPtr<Element> newBrElement =
       MOZ_KnownLive(mHTMLEditor)
-          ->InsertBrElementWithTransaction(pointToInsert, aSelect);
+          ->InsertBRElementWithTransaction(pointToInsert, aSelect);
   if (NS_WARN_IF(!newBrElement)) {
     return nullptr;
   }
   return newBrElement.forget();
 }
 
 nsresult WSRunObject::InsertText(Document& aDocument,
                                  const nsAString& aStringToInsert,
@@ -1840,17 +1840,17 @@ nsresult WSRunObject::CheckTrailingNBSPO
         // space, and then "foo  " jumps down to the next line.  Ugh.  The best
         // way I can find out of this is to throw in a harmless <br> here,
         // which allows us to do: |<body>foo.&nbsp <br></body>|, which doesn't
         // cause foo to jump lines, doesn't cause spaces to show up at the
         // beginning of soft wrapped lines, and lets the user see 2 spaces when
         // they type 2 spaces.
 
         RefPtr<Element> brElement =
-            htmlEditor->InsertBrElementWithTransaction(aRun->EndPoint());
+            htmlEditor->InsertBRElementWithTransaction(aRun->EndPoint());
         if (NS_WARN_IF(!brElement)) {
           return NS_ERROR_FAILURE;
         }
 
         // Refresh thePoint, prevPoint
         thePoint = GetPreviousCharPoint(aRun->EndPoint());
         prevPoint = GetPreviousCharPoint(thePoint);
         rightCheck = true;
--- a/editor/libeditor/tests/test_bug1368544.html
+++ b/editor/libeditor/tests/test_bug1368544.html
@@ -52,17 +52,18 @@ SimpleTest.waitForFocus(() => {
       is(textarea.value, "",
          "textarea should become empty when setUserInput() is called with empty string");
       if (navigator.appVersion.includes("Android")) {
         todo(!editor.rootElement.hasChildNodes(),
              "editor of textarea should have no children when user input emulation set the value to empty");
         if (editor.rootElement.childNodes.length > 0) {
           is(editor.rootElement.childNodes.length, 1, "There should be only one <br> node");
           is(editor.rootElement.firstChild.tagName.toLowerCase(), "br", "The node should be a <br> element node");
-          is(editor.rootElement.firstChild.getAttribute("_moz_editor_bogus_node"), null, "The <br> should not be a bogus node");
+          ok(!SpecialPowers.wrap(editor.rootElement.firstChild).isPaddingForEmptyEditor,
+             "The <br> should not be a padding <br> element");
         }
       } else {
         ok(!editor.rootElement.hasChildNodes(),
            "editor of textarea should have no children when user input emulation set the value to empty");
       }
       textarea.value = "ABC";
       synthesizeKey("KEY_Enter", {repeat: 2});
       textarea.value = "";
--- a/editor/libeditor/tests/test_bug1385905.html
+++ b/editor/libeditor/tests/test_bug1385905.html
@@ -13,39 +13,39 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="display"></div>
 <div id="editor" contenteditable style="padding: 5px;"><div>contents</div></div>
 <pre id="test">
 </pre>
 
 <script class="testbody" type="application/javascript">
 SimpleTest.waitForExplicitFinish();
 SimpleTest.waitForFocus(() => {
-  function ensureNoMozBR() {
+  function ensureNoPaddingBR() {
     for (let br of document.querySelectorAll("#editor > div > br")) {
-      isnot(br.getAttribute("type"), "_moz",
-            "mozBR shouldn't be used with this test");
+      ok(!SpecialPowers.wrap(br).isPaddingForEmptyLastLine,
+         "padding <br> element shouldn't be used with this test");
     }
   }
   document.execCommand("defaultparagraphseparator", false, "div");
   var editor = document.getElementById("editor");
   // Click the left blank area of the first line to set cursor to the start of "contents".
   synthesizeMouse(editor, 3, 10, {});
   synthesizeKey("KEY_Enter");
   is(editor.innerHTML, "<div><br></div><div>contents</div>",
      "Typing Enter at start of the <div> element should split the <div> element");
   synthesizeKey("KEY_ArrowUp");
   sendString("x");
   is(editor.innerHTML, "<div>x<br></div><div>contents</div>",
      "Typing 'x' at the empty <div> element should just insert 'x' into the <div> element");
-  ensureNoMozBR();
+  ensureNoPaddingBR();
   synthesizeKey("KEY_Enter");
   is(editor.innerHTML, "<div>x</div><div><br></div><div>contents</div>",
      "Typing Enter next to 'x' in the first <div> element should split the <div> element and inserts <br> element to a new <div> element");
-  ensureNoMozBR();
+  ensureNoPaddingBR();
   synthesizeKey("KEY_Enter");
   is(editor.innerHTML, "<div>x</div><div><br></div><div><br></div><div>contents</div>",
      "Typing Enter in the empty <div> should split the <div> element and inserts <br> element to a new <div> element");
-  ensureNoMozBR();
+  ensureNoPaddingBR();
   SimpleTest.finish();
 });
 </script>
 </body>
 </html>
--- a/editor/libeditor/tests/test_bug471319.html
+++ b/editor/libeditor/tests/test_bug471319.html
@@ -25,52 +25,52 @@ https://bugzilla.mozilla.org/show_bug.cg
       /** Test for Bug 471319 **/
 
       SimpleTest.waitForExplicitFinish();
 
       function doTest() {
         let t1 = SpecialPowers.wrap($("t1"));
 
         // Test 1: Undo on an empty editor - the editor should not forget about
-        // the bogus node
+        // the padding <br> element for empty editor.
         let t1Editor = t1.editor;
 
-        // Did the editor recognize the new bogus node?
+        // Did the editor recognize the new padding <br> element?
         t1Editor.undo(1);
-        ok(!t1.value, "<br> still recognized as bogus node on undo");
+        ok(!t1.value, "<br> still recognized as padding on undo");
 
 
         // Test 2: Redo on an empty editor - the editor should not forget about
-        // the bogus node
+        // the padding <br> element for empty editor.
         let t2 = SpecialPowers.wrap($("t2"));
         let t2Editor = t2.editor;
 
-        // Did the editor recognize the new bogus node?
+        // Did the editor recognize the new padding <br> element?
         t2Editor.redo(1);
-        ok(!t2.value, "<br> still recognized as bogus node on redo");
+        ok(!t2.value, "<br> still recognized as padding on redo");
 
 
         // Test 3: Undoing a batched transaction where both end points of the
-        // transaction are the bogus node - the bogus node should still be
-        // recognized as bogus
+        // transaction are the padding <br> element for empty editor - the
+        // <br> element should still be recognized as padding.
         t1Editor.transactionManager.beginBatch(null);
         t1.value = "mozilla";
         t1.value = "";
         t1Editor.transactionManager.endBatch(false);
         t1Editor.undo(1);
         ok(!t1.value,
-           "recreated <br> from undo transaction recognized as bogus");
+           "recreated <br> from undo transaction recognized as padding");
 
 
         // Test 4: Redoing a batched transaction where both end points of the
-        // transaction are the bogus node - the bogus node should still be
-        // recognized as bogus
+        // transaction are the padding <br> element for empty editor - the
+        // <br> element should still be recognized padding.
         t1Editor.redo(1);
         ok(!t1.value,
-           "recreated <br> from redo transaction recognized as bogus");
+           "recreated <br> from redo transaction recognized as padding");
         SimpleTest.finish();
      }
    </script>
   </pre>
 
   <input type="text" id="t1" />
   <input type="text" id="t2" />
 </body>
--- a/editor/libeditor/tests/test_bug471722.html
+++ b/editor/libeditor/tests/test_bug471722.html
@@ -36,40 +36,41 @@ https://bugzilla.mozilla.org/show_bug.cg
         t1.select();
 
         try {
           // Cut the initial text in the textbox
           ok(editor.canCut(), "can cut text");
           editor.cut();
           is(t1.value, "", "initial text was removed");
 
-          // So now we will have emptied the textfield
-          // and the editor will have created a bogus node
+          // So now we will have emptied the textfield and the editor will have
+          // created a padding <br> element for empty editor.
           // Check the transaction is in the undo stack...
           var t1Enabled = {};
           var t1CanUndo = {};
           editor.canUndo(t1Enabled, t1CanUndo);
           ok(t1CanUndo.value, "undo is enabled");
 
           // Undo the cut
           editor.undo(1);
           is(t1.value, "minefield", "text reinserted");
 
-          // So now, the cut should be in the redo stack,
-          // so executing the redo will clear the text once again
-          // and reinsert the bogus node that was removed after undo.
-          // This will require the editor to figure out that we have a
-          // bogus node again...
+          // So now, the cut should be in the redo stack, so executing the redo
+          // will clear the text once again and reinsert the padding <br>
+          // element for empty editor that was removed after undo.
+          // This will require the editor to figure out that we have a padding
+          // <br> element again...
           var t1CanRedo = {};
           editor.canRedo(t1Enabled, t1CanRedo);
           ok(t1CanRedo.value, "redo is enabled");
           editor.redo(1);
 
-          // Did the editor notice a bogus node reappeared?
-          is(t1.value, "", "editor found bogus node");
+          // Did the editor notice a padding <br> element for empty editor
+          // reappeared?
+          is(t1.value, "", "editor found padding <br> element");
        } catch (e) {
          ok(false, "test failed with error " + e);
        }
        SimpleTest.finish();
      }
    </script>
   </pre>
 
--- a/editor/libeditor/tests/test_bug611182.html
+++ b/editor/libeditor/tests/test_bug611182.html
@@ -64,17 +64,18 @@ SimpleTest.waitForFocus(function() {
       iframe.focus();
       var textNode = findTextNode(doc);
       var sel = win.getSelection();
       sel.collapse(textNode, 4);
       synthesizeKey("KEY_Backspace");
       is(textNode.textContent, "foo bar", "Backspace should work correctly");
 
       var snapshot = snapshotWindow(win, false);
-      ok(compareSnapshots(snapshot, ref, true)[0], "No bogus node should exist in the document");
+      ok(compareSnapshots(snapshot, ref, true)[0],
+         "No padding <br> element should exist in the document");
 
       callback();
     }, {once: true});
     iframe.src = src;
   }
 
   var totalTests = 0;
   var currentTest = 0;
--- a/editor/libeditor/tests/test_bug740784.html
+++ b/editor/libeditor/tests/test_bug740784.html
@@ -29,17 +29,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       SimpleTest.waitForFocus(function() {
         var t1 = $("t1");
 
         t1.focus();
         synthesizeKey("KEY_End");
         synthesizeKey("KEY_Backspace");
         synthesizeKey("z", {accelKey: true});
 
-        // Was the former bogus node changed to a mozBR?
         is(t1.value, "a", "trailing <br> correctly ignored");
 
         SimpleTest.finish();
       });
    </script>
   </pre>
 
   <textarea id="t1" rows="2" columns="80">a</textarea>
--- a/ipc/chromium/src/chrome/common/ipc_message_utils.h
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h
@@ -7,16 +7,17 @@
 #ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_
 #define CHROME_COMMON_IPC_MESSAGE_UTILS_H_
 
 #include <string>
 #include <vector>
 #include <map>
 
 #include "base/file_path.h"
+#include "base/process.h"
 #include "base/string_util.h"
 #include "base/string16.h"
 #include "base/time.h"
 
 #if defined(OS_POSIX)
 #  include "chrome/common/file_descriptor_set_posix.h"
 #endif
 #if defined(OS_WIN)
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -17,16 +17,18 @@ def building_js(build_project):
 # will still need to be set depending on building_js above.
 option(env='JS_STANDALONE', default=building_js,
        help='Reserved for internal use')
 
 include('../build/moz.configure/rust.configure',
         when='--enable-compile-environment')
 include('../build/moz.configure/bindgen.configure',
         when='--enable-compile-environment')
+include('../build/moz.configure/lto-pgo.configure',
+        when='--enable-compile-environment')
 
 @depends('JS_STANDALONE')
 def js_standalone(value):
     if value:
         return True
 set_config('JS_STANDALONE', js_standalone)
 set_define('JS_STANDALONE', js_standalone)
 add_old_configure_assignment('JS_STANDALONE', js_standalone)
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -3904,16 +3904,29 @@ bool js::IsPromiseForAsync(JSObject* pro
          PromiseHasAnyFlag(promise->as<PromiseObject>(), PROMISE_FLAG_ASYNC);
 }
 
 // ES2019 draft rev 7428c89bef626548084cd4e697a19ece7168f24c
 // 25.7.5.1 AsyncFunctionStart, steps 3.f-g.
 MOZ_MUST_USE bool js::AsyncFunctionThrown(JSContext* cx,
                                           Handle<PromiseObject*> resultPromise,
                                           HandleValue reason) {
+  if (resultPromise->state() != JS::PromiseState::Pending) {
+    // OOM after resolving promise.
+    // Report a warning and ignore the result.
+    if (!JS_ReportErrorFlagsAndNumberASCII(
+            cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
+            JSMSG_UNHANDLABLE_PROMISE_REJECTION_WARNING)) {
+      if (cx->isExceptionPending()) {
+        cx->clearPendingException();
+      }
+    }
+    return true;
+  }
+
   return RejectPromiseInternal(cx, resultPromise, reason);
 }
 
 // ES2019 draft rev 7428c89bef626548084cd4e697a19ece7168f24c
 // 25.7.5.1 AsyncFunctionStart, steps 3.d-e, 3.g.
 MOZ_MUST_USE bool js::AsyncFunctionReturned(
     JSContext* cx, Handle<PromiseObject*> resultPromise, HandleValue value) {
   return ResolvePromiseInternal(cx, resultPromise, value);
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -237,20 +237,18 @@ bool BytecodeEmitter::emitCheck(JSOp op,
   if (MOZ_UNLIKELY(newLength > MaxBytecodeLength)) {
     ReportAllocationOverflow(cx);
     return false;
   }
 
   if (!bytecodeSection().code().growByUninitialized(delta)) {
     return false;
   }
-
-  // If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
-  // reserve a type set to store its result.
-  if (CodeSpec[op].format & JOF_TYPESET) {
+  
+  if (BytecodeOpHasTypeSet(op)) {
     bytecodeSection().incrementNumTypeSets();
   }
 
   if (BytecodeOpHasIC(op)) {
     // Even if every bytecode op is a JOF_IC op and the function has ARGC_LIMIT
     // arguments, numICEntries cannot overflow.
     static_assert(MaxBytecodeLength + 1 /* this */ + ARGC_LIMIT <= UINT32_MAX,
                   "numICEntries must not overflow");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/async/debugger-reject-after-fulfill.js
@@ -0,0 +1,227 @@
+// Throwing error after resolving async function's promise should not
+// overwrite the promise's state or value/reason.
+// This situation can happen either with debugger interaction or OOM.
+
+// This testcase relies on the fact that there's a breakpoint after resolving
+// the async function's promise, before leaving the async function's frame.
+// This function searches for the last breakpoint before leaving the frame.
+//
+//  - `declCode` should declare an async function `f`, and the function should
+//    set global variable `returning` to `true` just before return
+//  - `callCode` should call the function `f` and make sure the function's
+//    execution reaches the last breakpoint
+function searchLastBreakpointBeforeReturn(declCode, callCode) {
+  const g = newGlobal({ newCompartment: true });
+  const dbg = new Debugger(g);
+  g.eval(declCode);
+
+  let hit = false;
+  let offset = 0;
+  dbg.onEnterFrame = function(frame) {
+    if (frame.callee && frame.callee.name == "f") {
+      frame.onStep = () => {
+        if (!g.returning) {
+          return undefined;
+        }
+
+        offset = frame.offset;
+        return undefined;
+      };
+    }
+  };
+
+  g.eval(callCode);
+
+  drainJobQueue();
+
+  assertEq(offset != 0, true);
+
+  return offset;
+}
+
+function testWithoutAwait() {
+  const declCode = `
+  var returning = false;
+  async function f() {
+    return (returning = true, "expected");
+  };
+  `;
+
+  const callCode = `
+  var p = f();
+  `;
+
+  const offset = searchLastBreakpointBeforeReturn(declCode, callCode);
+
+  const g = newGlobal({ newCompartment: true });
+  const dbg = new Debugger(g);
+  g.eval(declCode);
+
+  let onPromiseSettledCount = 0;
+  dbg.onPromiseSettled = function(promise) {
+    onPromiseSettledCount++;
+    // No promise should be rejected.
+    assertEq(promise.promiseState, "fulfilled");
+
+    // Async function's promise should have expected value.
+    if (onPromiseSettledCount == 1) {
+      assertEq(promise.promiseValue, "expected");
+    }
+  };
+
+  let hitBreakpoint = false;
+  dbg.onEnterFrame = function(frame) {
+    if (frame.callee && frame.callee.name == "f") {
+      frame.script.setBreakpoint(offset, {
+        hit() {
+          hitBreakpoint = true;
+          return { throw: "unexpected" };
+        }
+      });
+    }
+  };
+
+  enableLastWarning();
+
+  g.eval(`
+  var fulfilledValue;
+  var rejected = false;
+  `);
+
+  g.eval(callCode);
+
+  // The execution reaches to the last breakpoint without running job queue.
+  assertEq(hitBreakpoint, true);
+
+  const warn = getLastWarning();
+  assertEq(warn.message,
+           "unhandlable error after resolving async function's promise");
+  clearLastWarning();
+
+  // Add reaction handler after resolution.
+  // This handler's job will be enqueued immediately.
+  g.eval(`
+  p.then(x => {
+    fulfilledValue = x;
+  }, e => {
+    rejected = true;
+  });
+  `);
+
+  // Run the above handler.
+  drainJobQueue();
+
+  assertEq(g.fulfilledValue, "expected");
+  assertEq(onPromiseSettledCount >= 1, true);
+}
+
+function testWithAwait() {
+  const declCode = `
+  var resolve;
+  var p = new Promise(r => { resolve = r });
+  var returning = false;
+  async function f() {
+    await p;
+    return (returning = true, "expected");
+  };
+  `;
+
+  const callCode = `
+  var p = f();
+  `;
+
+  const resolveCode = `
+  resolve("resolve");
+  `;
+
+  const offset = searchLastBreakpointBeforeReturn(declCode,
+                                                  callCode + resolveCode);
+
+  const g = newGlobal({newCompartment: true});
+  const dbg = new Debugger(g);
+  g.eval(declCode);
+
+  let onPromiseSettledCount = 0;
+  dbg.onPromiseSettled = function(promise) {
+    onPromiseSettledCount++;
+
+    // No promise should be rejected.
+    assertEq(promise.promiseState, "fulfilled");
+
+    // Async function's promise should have expected value.
+    if (onPromiseSettledCount == 3) {
+      assertEq(promise.promiseValue, "expected");
+    }
+  };
+
+  let hitBreakpoint = false;
+  dbg.onEnterFrame = function(frame) {
+    if (frame.callee && frame.callee.name == "f") {
+      frame.script.setBreakpoint(offset, {
+        hit() {
+          hitBreakpoint = true;
+          return { throw: "unexpected" };
+        }
+      });
+    }
+  };
+
+  enableLastWarning();
+
+  g.eval(`
+  var fulfilledValue1;
+  var fulfilledValue2;
+  var rejected = false;
+  `);
+
+  g.eval(callCode);
+
+  assertEq(getLastWarning(), null);
+
+  // Add reaction handler before resolution.
+  // This handler's job will be enqueued when `p` is resolved.
+  g.eval(`
+  p.then(x => {
+    fulfilledValue1 = x;
+  }, e => {
+    rejected = true;
+  });
+  `);
+
+  g.eval(resolveCode);
+
+  // Run the remaining part of async function, and the above handler.
+  drainJobQueue();
+
+  // The execution reaches to the last breakpoint after running job queue for
+  // resolving `p`.
+  assertEq(hitBreakpoint, true);
+
+  const warn = getLastWarning();
+  assertEq(warn.message,
+           "unhandlable error after resolving async function's promise");
+  clearLastWarning();
+
+  assertEq(g.fulfilledValue1, "expected");
+  assertEq(g.rejected, false);
+
+  // Add reaction handler after resolution.
+  // This handler's job will be enqueued immediately.
+  g.eval(`
+  p.then(x => {
+    fulfilledValue2 = x;
+  }, e => {
+    rejected = true;
+  });
+  `);
+
+  // Run the above handler.
+  drainJobQueue();
+
+  assertEq(g.fulfilledValue2, "expected");
+  assertEq(g.rejected, false);
+  assertEq(onPromiseSettledCount >= 3, true);
+}
+
+testWithoutAwait();
+testWithAwait();
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -17,16 +17,17 @@
 #include "jit/Ion.h"
 #include "jit/JitSpewer.h"
 #include "jit/mips32/Simulator-mips32.h"
 #include "jit/mips64/Simulator-mips64.h"
 #include "jit/Recover.h"
 #include "jit/RematerializedFrame.h"
 #include "js/Utility.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/BytecodeUtil.h"
 #include "vm/TraceLogging.h"
 
 #include "jit/JitFrames-inl.h"
 #include "vm/JSScript-inl.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -1129,17 +1130,17 @@ static bool InitFromBailout(JSContext* c
       // the correct pc offset of the throwing op instead of its
       // successor.
       jsbytecode* throwPC = script->offsetToPC(iter.pcOffset());
       blFrame->setInterpreterFields(script, throwPC);
       resumeAddr = baselineInterp.interpretOpAddr().value;
     } else {
       // If the opcode is monitored we should monitor the top stack value when
       // we finish the bailout in FinishBailoutToBaseline.
-      if (resumeAfter && (CodeSpec[op].format & JOF_TYPESET)) {
+      if (resumeAfter && BytecodeOpHasTypeSet(op)) {
         builder.setMonitorPC(pc);
       }
       jsbytecode* resumePC = GetResumePC(script, pc, resumeAfter);
       blFrame->setInterpreterFields(script, resumePC);
       resumeAddr = baselineInterp.interpretOpAddr().value;
     }
     builder.setResumeAddr(resumeAddr);
     JitSpew(JitSpew_BaselineBailouts, "      Set resumeAddr=%p", resumeAddr);
@@ -1321,17 +1322,17 @@ static bool InitFromBailout(JSContext* c
 
   // Push BaselineStub frame descriptor
   if (!builder.writeWord(baselineStubFrameDescr, "Descriptor")) {
     return false;
   }
 
   // Ensure we have a TypeMonitor fallback stub so we don't crash in JIT code
   // when we try to enter it. See callers of offsetOfFallbackMonitorStub.
-  if (CodeSpec[*pc].format & JOF_TYPESET) {
+  if (BytecodeOpHasTypeSet(JSOp(*pc))) {
     ICFallbackStub* fallbackStub = icEntry.fallbackStub();
     if (!fallbackStub->toMonitoredFallbackStub()->getFallbackMonitorStub(
             cx, script)) {
       return false;
     }
   }
 
   // Push return address into ICCall_Scripted stub, immediately after the call.
@@ -1864,17 +1865,17 @@ bool jit::FinishBailoutToBaseline(Baseli
     if (!CheckGlobalDeclarationConflicts(cx, script, lexicalEnv,
                                          cx->global())) {
       return false;
     }
   }
 
   // Monitor the top stack value if we are resuming after a JOF_TYPESET op.
   if (jsbytecode* monitorPC = bailoutInfo->monitorPC) {
-    MOZ_ASSERT(CodeSpec[*monitorPC].format & JOF_TYPESET);
+    MOZ_ASSERT(BytecodeOpHasTypeSet(JSOp(*monitorPC)));
     MOZ_ASSERT(GetNextPc(monitorPC) == topFrame->interpreterPC());
 
     RootedScript script(cx, topFrame->script());
     uint32_t monitorOffset = script->pcToOffset(monitorPC);
     ICEntry& icEntry = script->jitScript()->icEntryFromPCOffset(monitorOffset);
     ICFallbackStub* fallbackStub = icEntry.fallbackStub();
 
     // Not every monitored op has a monitored fallback stub, e.g.
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -29,16 +29,17 @@
 #include "jit/Lowering.h"
 #ifdef JS_ION_PERF
 #  include "jit/PerfSpewer.h"
 #endif
 #include "jit/SharedICHelpers.h"
 #include "jit/VMFunctions.h"
 #include "js/Conversions.h"
 #include "js/GCVector.h"
+#include "vm/BytecodeUtil.h"
 #include "vm/JSFunction.h"
 #include "vm/Opcodes.h"
 #include "vm/SelfHosting.h"
 #include "vm/TypedArrayObject.h"
 #include "vtune/VTuneWrapper.h"
 
 #include "builtin/Boolean-inl.h"
 
@@ -219,17 +220,17 @@ bool JitScript::initICEntriesAndBytecode
   // For JOF_IC ops: initialize ICEntries and fallback stubs.
   // For JOF_TYPESET ops: initialize bytecode type map entries.
   jsbytecode const* pcEnd = script->codeEnd();
   for (jsbytecode* pc = script->code(); pc < pcEnd; pc = GetNextPc(pc)) {
     JSOp op = JSOp(*pc);
 
     // Note: if the script is very large there will be more JOF_TYPESET ops
     // than bytecode type sets. See JSScript::MaxBytecodeTypeSets.
-    if ((CodeSpec[op].format & JOF_TYPESET) &&
+    if (BytecodeOpHasTypeSet(op) &&
         typeMapIndex < JSScript::MaxBytecodeTypeSets) {
       typeMap[typeMapIndex] = script->pcToOffset(pc);
       typeMapIndex++;
     }
 
     // Assert the frontend stored the correct IC index in jump target ops.
     MOZ_ASSERT_IF(BytecodeIsJumpTarget(op), GET_ICINDEX(pc) == icEntryIndex);
 
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -17,16 +17,17 @@
 #include "jit/CacheIR.h"
 #include "jit/Ion.h"
 #include "jit/IonControlFlow.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/JitSpewer.h"
 #include "jit/Lowering.h"
 #include "jit/MIRGraph.h"
 #include "vm/ArgumentsObject.h"
+#include "vm/BytecodeUtil.h"
 #include "vm/EnvironmentObject.h"
 #include "vm/Instrumentation.h"
 #include "vm/Opcodes.h"
 #include "vm/RegExpStatics.h"
 #include "vm/SelfHosting.h"
 #include "vm/TraceLogging.h"
 
 #include "gc/Nursery-inl.h"
@@ -649,17 +650,17 @@ AbortReasonOr<Ok> IonBuilder::analyzeNew
     }
 
     MPhi* phi = entry->getSlot(slot)->toPhi();
 
     if (*last == JSOP_POS || *last == JSOP_TONUMERIC) {
       last = earlier;
     }
 
-    if (CodeSpec[*last].format & JOF_TYPESET) {
+    if (BytecodeOpHasTypeSet(JSOp(*last))) {
       TemporaryTypeSet* typeSet = bytecodeTypes(last);
       if (!typeSet->empty()) {
         MIRType type = typeSet->getKnownMIRType();
         if (!phi->addBackedgeType(alloc(), type, typeSet)) {
           return abort(AbortReason::Alloc);
         }
       }
     } else if (*last == JSOP_GETLOCAL || *last == JSOP_GETARG) {
--- a/js/src/jit/JitScript-inl.h
+++ b/js/src/jit/JitScript-inl.h
@@ -6,16 +6,17 @@
 
 #ifndef jit_JitScript_inl_h
 #define jit_JitScript_inl_h
 
 #include "jit/JitScript.h"
 
 #include "mozilla/BinarySearch.h"
 
+#include "vm/BytecodeUtil.h"
 #include "vm/JSScript.h"
 #include "vm/TypeInference.h"
 
 #include "vm/JSContext-inl.h"
 
 namespace js {
 namespace jit {
 
@@ -37,17 +38,17 @@ inline StackTypeSet* JitScript::argTypes
 }
 
 template <typename TYPESET>
 /* static */ inline TYPESET* JitScript::BytecodeTypes(JSScript* script,
                                                       jsbytecode* pc,
                                                       uint32_t* bytecodeMap,
                                                       uint32_t* hint,
                                                       TYPESET* typeArray) {
-  MOZ_ASSERT(CodeSpec[*pc].format & JOF_TYPESET);
+  MOZ_ASSERT(BytecodeOpHasTypeSet(JSOp(*pc)));
   uint32_t offset = script->pcToOffset(pc);
 
   // See if this pc is the next typeset opcode after the last one looked up.
   size_t numBytecodeTypeSets = script->numBytecodeTypeSets();
   if ((*hint + 1) < numBytecodeTypeSets && bytecodeMap[*hint + 1] == offset) {
     (*hint)++;
     return typeArray + *hint;
   }
--- a/js/src/jit/JitScript.cpp
+++ b/js/src/jit/JitScript.cpp
@@ -8,16 +8,17 @@
 
 #include "mozilla/BinarySearch.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Move.h"
 #include "mozilla/ScopeExit.h"
 
 #include "jit/BaselineIC.h"
 #include "jit/BytecodeAnalysis.h"
+#include "vm/BytecodeUtil.h"
 #include "vm/JSScript.h"
 #include "vm/Stack.h"
 #include "vm/TypeInference.h"
 #include "wasm/WasmInstance.h"
 
 #include "gc/FreeOp-inl.h"
 #include "jit/JSJitFrameIter-inl.h"
 #include "vm/JSScript-inl.h"
@@ -253,17 +254,17 @@ void JitScript::printTypes(JSContext* cx
       Sprinter sprinter(cx);
       if (!sprinter.init()) {
         return;
       }
       Disassemble1(cx, script, pc, script->pcToOffset(pc), true, &sprinter);
       fprintf(stderr, "%s", sprinter.string());
     }
 
-    if (CodeSpec[*pc].format & JOF_TYPESET) {
+    if (BytecodeOpHasTypeSet(JSOp(*pc))) {
       StackTypeSet* types = bytecodeTypes(sweep, script, pc);
       fprintf(stderr, "  typeset %u:", unsigned(types - typeArray(sweep)));
       types->print();
       fprintf(stderr, "\n");
     }
   }
 
   fprintf(stderr, "\n");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -639,16 +639,19 @@ MSG_DEF(JSMSG_PROMISE_CAPABILITY_HAS_SOM
 MSG_DEF(JSMSG_PROMISE_RESOLVE_FUNCTION_NOT_CALLABLE,    0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the resolve function.")
 MSG_DEF(JSMSG_PROMISE_REJECT_FUNCTION_NOT_CALLABLE,     0, JSEXN_TYPEERR, "A Promise subclass passed a non-callable value as the reject function.")
 MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "Promise rejection value is a non-unwrappable cross-compartment wrapper.")
 
 // Iterator
 MSG_DEF(JSMSG_RETURN_NOT_CALLABLE,     0, JSEXN_TYPEERR, "property 'return' of iterator is not callable")
 MSG_DEF(JSMSG_ITERATOR_NO_THROW,       0, JSEXN_TYPEERR, "iterator does not have a 'throw' method")
 
+// Async Function
+MSG_DEF(JSMSG_UNHANDLABLE_PROMISE_REJECTION_WARNING, 0, JSEXN_WARN, "unhandlable error after resolving async function's promise")
+
 // Async Iteration
 MSG_DEF(JSMSG_FOR_AWAIT_NOT_OF,        0, JSEXN_SYNTAXERR, "'for await' loop should be used with 'of'")
 MSG_DEF(JSMSG_NOT_AN_ASYNC_GENERATOR,  0, JSEXN_TYPEERR, "Not an async generator")
 MSG_DEF(JSMSG_NOT_AN_ASYNC_ITERATOR,   0, JSEXN_TYPEERR, "Not an async from sync iterator")
 MSG_DEF(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE, 0, JSEXN_TYPEERR, "[Symbol.asyncIterator]() returned a non-object value")
 
 // ReadableStream
 MSG_DEF(JSMSG_READABLESTREAM_UNDERLYINGSOURCE_TYPE_WRONG,0, JSEXN_RANGEERR,"'underlyingSource.type' must be \"bytes\" or undefined.")
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -11249,18 +11249,18 @@ int main(int argc, char** argv, char** e
   JS::SetProcessBuildIdOp(ShellBuildId);
 
   // The fake CPU count must be set before initializing the Runtime,
   // which spins up the thread pool.
   int32_t cpuCount = op.getIntOption("cpu-count");  // What we're really setting
   if (cpuCount < 0) {
     cpuCount = op.getIntOption("thread-count");  // Legacy name
   }
-  if (cpuCount >= 0) {
-    SetFakeCPUCount(cpuCount);
+  if (cpuCount >= 0 && !SetFakeCPUCount(cpuCount)) {
+      return 1;
   }
 
   size_t nurseryBytes = JS::DefaultNurseryBytes;
   nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
 
   /* Use the same parameters as the browser in xpcjsruntime.cpp. */
   JSContext* const cx = JS_NewContext(JS::DefaultHeapMaxBytes, nurseryBytes);
   if (!cx) {
--- a/js/src/vm/BytecodeUtil.h
+++ b/js/src/vm/BytecodeUtil.h
@@ -628,16 +628,20 @@ static inline int32_t GetBytecodeInteger
       return GET_INT32(pc);
     default:
       MOZ_CRASH("Bad op");
   }
 }
 
 inline bool BytecodeOpHasIC(JSOp op) { return CodeSpec[op].format & JOF_IC; }
 
+inline bool BytecodeOpHasTypeSet(JSOp op) {
+  return CodeSpec[op].format & JOF_TYPESET;
+}
+
 /*
  * Counts accumulated for a single opcode in a script. The counts tracked vary
  * between opcodes, and this structure ensures that counts are accessed in a
  * coherent fashion.
  */
 class PCCounts {
   /*
    * Offset of the pc inside the script. This fields is used to lookup opcode
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -66,17 +66,17 @@ GlobalHelperThreadState* gHelperThreadSt
 bool js::CreateHelperThreadsState() {
   MOZ_ASSERT(!gHelperThreadState);
   UniquePtr<GlobalHelperThreadState> helperThreadState =
       MakeUnique<GlobalHelperThreadState>();
   if (!helperThreadState) {
     return false;
   }
   gHelperThreadState = helperThreadState.release();
-  if (!gHelperThreadState->initializeHelperContexts()) {
+  if (!gHelperThreadState->ensureContextListForThreadCount()) {
     js_delete(gHelperThreadState);
     return false;
   }
   return true;
 }
 
 void js::DestroyHelperThreadsState() {
   MOZ_ASSERT(gHelperThreadState);
@@ -101,22 +101,27 @@ static size_t ClampDefaultCPUCount(size_
 
 static size_t ThreadCountForCPUCount(size_t cpuCount) {
   // We need at least two threads for tier-2 wasm compilations, because
   // there's a master task that holds a thread while other threads do the
   // compilation.
   return Max<size_t>(cpuCount, 2);
 }
 
-void js::SetFakeCPUCount(size_t count) {
+bool js::SetFakeCPUCount(size_t count) {
   // This must be called before the threads have been initialized.
   MOZ_ASSERT(!HelperThreadState().threads);
 
   HelperThreadState().cpuCount = count;
   HelperThreadState().threadCount = ThreadCountForCPUCount(count);
+
+  if (!HelperThreadState().ensureContextListForThreadCount()) {
+    return false;
+  }
+  return true;
 }
 
 void JS::SetProfilingThreadCallbacks(
     JS::RegisterThreadCallback registerThread,
     JS::UnregisterThreadCallback unregisterThread) {
   HelperThreadState().registerThread = registerThread;
   HelperThreadState().unregisterThread = unregisterThread;
 }
@@ -1197,19 +1202,26 @@ void GlobalHelperThreadState::finishThre
 
   MOZ_ASSERT(CanUseExtraThreads());
   for (auto& thread : *threads) {
     thread.destroy();
   }
   threads.reset(nullptr);
 }
 
-bool GlobalHelperThreadState::initializeHelperContexts() {
+bool GlobalHelperThreadState::ensureContextListForThreadCount() {
+  if (helperContexts_.length() >= threadCount) {
+    return true;
+  }
   AutoLockHelperThreadState lock;
-  for (size_t i = 0; i < threadCount; i++) {
+
+  // SetFakeCPUCount() may cause the context list to contain less contexts
+  // than there are helper threads, which could potentially lead to a crash.
+  // Append more initialized contexts to the list until there are enough.
+  while (helperContexts_.length() < threadCount) {
     UniquePtr<JSContext> cx =
         js::MakeUnique<JSContext>(nullptr, JS::ContextOptions());
     // To initialize context-specific protected data, the context must
     // temporarily set itself to the main thread. After initialization,
     // cx can clear itself from the thread.
     cx->setHelperThread(lock);
     if (!cx->init(ContextKind::HelperThread)) {
       return false;
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -166,17 +166,17 @@ class GlobalHelperThreadState {
   size_t maxGCParallelThreads() const;
 
   GlobalHelperThreadState();
 
   bool ensureInitialized();
   void finish();
   void finishThreads();
 
-  MOZ_MUST_USE bool initializeHelperContexts();
+  MOZ_MUST_USE bool ensureContextListForThreadCount();
   JSContext* getFirstUnusedContext(AutoLockHelperThreadState& locked);
   void destroyHelperContexts(AutoLockHelperThreadState& lock);
 
 #ifdef DEBUG
   bool isLockedByCurrentThread() const;
 #endif
 
   enum CondVar {
@@ -487,17 +487,17 @@ bool CreateHelperThreadsState();
 // Destroy data structures used by helper threads.
 void DestroyHelperThreadsState();
 
 // Initialize helper threads unless already initialized.
 bool EnsureHelperThreadsInitialized();
 
 // This allows the JS shell to override GetCPUCount() when passed the
 // --thread-count=N option.
-void SetFakeCPUCount(size_t count);
+bool SetFakeCPUCount(size_t count);
 
 // Get the current helper thread, or null.
 HelperThread* CurrentHelperThread();
 
 // Enqueues a wasm compilation task.
 bool StartOffThreadWasmCompile(wasm::CompileTask* task, wasm::CompileMode mode);
 
 namespace wasm {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3439,17 +3439,17 @@ void JitScript::MonitorBytecodeTypeSlow(
   InferSpew(ISpewOps, "bytecodeType: %p %05zu: %s", script,
             script->pcToOffset(pc), TypeSet::TypeString(type).get());
   types->addType(sweep, cx, type);
 }
 
 /* static */
 void JitScript::MonitorBytecodeType(JSContext* cx, JSScript* script,
                                     jsbytecode* pc, const js::Value& rval) {
-  MOZ_ASSERT(CodeSpec[*pc].format & JOF_TYPESET);
+  MOZ_ASSERT(BytecodeOpHasTypeSet(JSOp(*pc)));
 
   if (!script->jitScript()) {
     return;
   }
 
   MonitorBytecodeType(cx, script, pc, TypeSet::GetValueType(rval));
 }
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -10798,16 +10798,19 @@ void nsCSSFrameConstructor::FinishBuildi
   nsContainerFrame* prevColumnSet = aColumnContent->GetParent();
 
   MOZ_ASSERT(prevColumnSet->IsColumnSetFrame() &&
                  prevColumnSet->GetParent() == aColumnSetWrapper,
              "Should have established column hierarchy!");
 
   nsFrameList finalList;
   while (aColumnContentSiblings.NotEmpty()) {
+    // Tag every ColumnSet except the last one.
+    prevColumnSet->SetProperty(nsIFrame::HasColumnSpanSiblings(), true);
+
     nsIFrame* f = aColumnContentSiblings.RemoveFirstChild();
     if (f->IsColumnSpan()) {
       // Do nothing for column-span wrappers. Just move it to the final
       // items.
       finalList.AppendFrame(aColumnSetWrapper, f);
     } else {
       auto* continuingColumnSet = static_cast<nsContainerFrame*>(
           CreateContinuingFrame(mPresShell->GetPresContext(), prevColumnSet,
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -240,17 +240,16 @@ nsresult nsLayoutStatics::Initialize() {
     NS_ERROR("Could not initialize nsFocusManager");
     return rv;
   }
 
   DecoderDoctorLogger::Init();
   MediaManager::StartupInit();
   CubebUtils::InitLibrary();
 
-  nsContentSink::InitializeStatics();
   nsHtml5Module::InitializeStatics();
   mozilla::dom::FallbackEncoding::Initialize();
   nsLayoutUtils::Initialize();
   PointerEventHandler::InitializeStatics();
   TouchManager::InitializeStatics();
 
   nsCORSListenerProxy::Startup();
 
--- a/layout/generic/BRFrame.cpp
+++ b/layout/generic/BRFrame.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* rendering object for HTML <br> elements */
 
 #include "mozilla/PresShell.h"
+#include "mozilla/dom/HTMLBRElement.h"
 #include "gfxContext.h"
 #include "nsCOMPtr.h"
 #include "nsContainerFrame.h"
 #include "nsFontMetrics.h"
 #include "nsFrame.h"
 #include "nsPresContext.h"
 #include "nsLineLayout.h"
 #include "nsStyleConsts.h"
@@ -242,21 +243,20 @@ nsIFrame::FrameSearchResult BRFrame::Pee
     int32_t* aOffset, PeekWordState* aState, bool aTrimSpaces) {
   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
   // Keep going. The actual line jumping will stop us.
   return CONTINUE;
 }
 
 #ifdef ACCESSIBILITY
 a11y::AccType BRFrame::AccessibleType() {
-  nsIContent* parent = mContent->GetParent();
-  if (parent && parent->IsRootOfNativeAnonymousSubtree() &&
-      parent->GetChildCount() == 1) {
-    // This <br> is the only node in a text control, therefore it is the hacky
-    // "bogus node" used when there is no text in the control
+  dom::HTMLBRElement* brElement = dom::HTMLBRElement::FromNode(mContent);
+  if (brElement->IsPaddingForEmptyEditor()) {
+    // This <br> is a "padding <br> element" used when there is no text in the
+    // editor.
     return a11y::eNoType;
   }
 
   // Trailing HTML br element don't play any difference. We don't need to expose
   // it to AT (see bug https://bugzilla.mozilla.org/show_bug.cgi?id=899433#c16
   // for details).
   if (!mContent->GetNextSibling() && !GetNextSibling()) {
     return a11y::eNoType;
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -3570,16 +3570,18 @@ void nsBlockFrame::ReflowBlockFrame(Bloc
       // column block size.
       if (frame->IsColumnSetFrame()) {
         if (availSize.BSize(wm) != NS_UNCONSTRAINEDSIZE) {
           // If the available size is constrained, we need to subtract
           // ColumnSetWrapper's block-end border and padding.
           availSize.BSize(wm) -= aState.BorderPadding().BEnd(wm);
         }
 
+        // Bug 1569701: We need to use GetEffectiveComputedBSize() to get
+        // correct block-size if ColumnSetWrapper is fragmented.
         nscoord contentBSize = aState.mReflowInput.ComputedBSize();
         if (aState.mReflowInput.ComputedMaxBSize() != NS_UNCONSTRAINEDSIZE) {
           contentBSize =
               std::min(contentBSize, aState.mReflowInput.ComputedMaxBSize());
         }
         if (contentBSize != NS_UNCONSTRAINEDSIZE) {
           // ColumnSet is not the outermost frame in the column container, so it
           // cannot have any margin. We don't need to consider any margin that
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -912,47 +912,81 @@ nsColumnSetFrame::ColumnBalanceData nsCo
     }
   }
 
   colData.mMaxBSize = contentBEnd;
   LogicalSize contentSize = LogicalSize(wm, contentRect.Size());
   contentSize.BSize(wm) = std::max(contentSize.BSize(wm), contentBEnd);
   mLastFrameStatus = aStatus;
 
-  // Apply computed and min/max values
-  if (aConfig.mComputedBSize != NS_UNCONSTRAINEDSIZE) {
-    if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
+  if (StaticPrefs::layout_css_column_span_enabled()) {
+    MOZ_ASSERT(borderPadding.IsAllZero(),
+               "Only our parent ColumnSetWrapper can have border and padding!");
+
+    if (computedSize.BSize(wm) != NS_UNCONSTRAINEDSIZE &&
+        !GetProperty(nsIFrame::HasColumnSpanSiblings())) {
+      MOZ_ASSERT(aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE,
+                 "Available block-size should be constrained because it's "
+                 "restricted by the computed block-size when our reflow input "
+                 "is created in nsBlockFrame::ReflowBlockFrame()!");
+
+      // If a) our parent ColumnSetWrapper has constrained block-size
+      // (nsBlockFrame::ReflowBlockFrame() applies the block-size constraint
+      // when creating BlockReflowInput for ColumnSetFrame); and b) we are the
+      // sole ColumnSet or the last ColumnSet continuation split by column-spans
+      // in a ColumnSetWrapper, extend our block-size to consume the available
+      // block-size so that the column-rules are drawn to the content block-end
+      // edge of the multicol container.
       contentSize.BSize(wm) =
-          std::min(contentSize.BSize(wm), aConfig.mComputedBSize);
-    } else {
-      contentSize.BSize(wm) = aConfig.mComputedBSize;
+          std::max(contentSize.BSize(wm), aReflowInput.AvailableBSize());
+
+      // But don't consume more block-size than what is left in the
+      // ColumnSetWrapper.
+      //
+      // Bug 1569701: If we use the effective computed block-size of
+      // ColumnSetWrapper when creating BlockReflowInput for ColumnSet, the
+      // available block-size should always less than or equal to the effective
+      // computed block-size. This std::min() won't be needed.
+      contentSize.BSize(wm) =
+          std::min(contentSize.BSize(wm), computedSize.BSize(wm));
     }
-  } else if (aReflowInput.mStyleDisplay->IsContainSize()) {
-    // If we are intrinsically sized, but are size contained,
-    // we need to behave as if we have no contents. Our BSize
-    // should be zero or minBSize if specified.
-    contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(0);
   } else {
-    // We add the "consumed" block-size back in so that we're applying
-    // constraints to the correct bSize value, then subtract it again
-    // after we've finished with the min/max calculation. This prevents us from
-    // having a last continuation that is smaller than the min bSize. but which
-    // has prev-in-flows, trigger a larger bSize than actually required.
-    contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(
-        contentSize.BSize(wm), aConfig.mConsumedBSize);
-  }
-  if (aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE) {
-    contentSize.ISize(wm) = aReflowInput.ComputedISize();
-  } else {
-    contentSize.ISize(wm) =
-        aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm));
+    // Apply computed and min/max values
+    if (aConfig.mComputedBSize != NS_UNCONSTRAINEDSIZE) {
+      if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE) {
+        contentSize.BSize(wm) =
+            std::min(contentSize.BSize(wm), aConfig.mComputedBSize);
+      } else {
+        contentSize.BSize(wm) = aConfig.mComputedBSize;
+      }
+    } else if (aReflowInput.mStyleDisplay->IsContainSize()) {
+      // If we are intrinsically sized, but are size contained,
+      // we need to behave as if we have no contents. Our BSize
+      // should be zero or minBSize if specified.
+      contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(0);
+    } else {
+      // We add the "consumed" block-size back in so that we're applying
+      // constraints to the correct bSize value, then subtract it again
+      // after we've finished with the min/max calculation. This prevents us
+      // from having a last continuation that is smaller than the min bSize. but
+      // which has prev-in-flows, trigger a larger bSize than actually required.
+      contentSize.BSize(wm) = aReflowInput.ApplyMinMaxBSize(
+          contentSize.BSize(wm), aConfig.mConsumedBSize);
+    }
+    if (aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE) {
+      contentSize.ISize(wm) = aReflowInput.ComputedISize();
+    } else {
+      contentSize.ISize(wm) =
+          aReflowInput.ApplyMinMaxISize(contentSize.ISize(wm));
+    }
+
+    contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
+    contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
   }
 
-  contentSize.ISize(wm) += borderPadding.IStartEnd(wm);
-  contentSize.BSize(wm) += borderPadding.BStartEnd(wm);
   aDesiredSize.SetSize(wm, contentSize);
   aDesiredSize.mOverflowAreas = overflowRects;
   aDesiredSize.UnionOverflowAreasWithDesiredBounds();
 
   // In vertical-rl mode, make a second pass if necessary to reposition the
   // columns with the correct container width. (In other writing modes,
   // correct containerSize was not required for column positioning so we don't
   // need this fixup.)
--- a/layout/reftests/columns/reftest.list
+++ b/layout/reftests/columns/reftest.list
@@ -44,12 +44,12 @@ pref(layout.css.column-span.enabled,true
 pref(layout.css.column-span.enabled,true) == first-line-first-letter.html first-line-first-letter-ref.html
 == zero-height-nondirty-reflow.html zero-height-nondirty-reflow-ref.html
 
 # The following lines are duplicates of other lines from further up in this
 # manifest. They're listed again here so we can re-run these tests with
 # column-span enabled. These lines can be removed once the pref becomes
 # default-enabled (Bug 1426010).
 default-preferences pref(layout.css.column-span.enabled,true)
-fails == min-width-2.html min-width-2-ref.html # Bug 1548100
+== min-width-2.html min-width-2-ref.html
 == column-balancing-nested-001.html column-balancing-nested-001-ref.html
 == zero-height-nondirty-reflow.html zero-height-nondirty-reflow-ref.html
 default-preferences
--- a/layout/reftests/w3c-css/failures.list
+++ b/layout/reftests/w3c-css/failures.list
@@ -255,18 +255,17 @@ fuzzy(0-225,0-2529) css-multicol/multico
 fuzzy(0-225,0-2529) css-multicol/multicol-nested-margin-005.xht
 fuzzy(0-135,0-142) css-multicol/multicol-overflow-000.xht
 fuzzy(0-204,0-1844) fuzzy-if(skiaContent,0-208,0-1844) css-multicol/multicol-overflowing-001.xht
 fuzzy-if(skiaContent,0-64,0-2) css-multicol/multicol-reduce-000.xht
 css-multicol/multicol-rule-000.xht
 fuzzy-if(!OSX,0-135,0-1584) css-multicol/multicol-rule-001.xht
 fails-if(!OSX) random-if(OSX) css-multicol/multicol-rule-002.xht
 css-multicol/multicol-rule-003.xht
-# Bug 1548100
-pref(layout.css.column-span.enabled,true) fails css-multicol/multicol-rule-004.xht
+pref(layout.css.column-span.enabled,true) css-multicol/multicol-rule-004.xht
 css-multicol/multicol-rule-color-001.xht
 fuzzy(0-106,0-354) css-multicol/multicol-rule-dashed-000.xht
 fuzzy(0-106,0-354) css-multicol/multicol-rule-dotted-000.xht
 fuzzy(0-106,0-354) css-multicol/multicol-rule-double-000.xht
 css-multicol/multicol-rule-fraction-001.xht
 css-multicol/multicol-rule-fraction-002.xht
 fuzzy-if(winWidget||Android,0-113,0-792) css-multicol/multicol-rule-fraction-003.xht
 fuzzy(0-127,0-500) css-multicol/multicol-rule-groove-000.xht
--- a/layout/reftests/w3c-css/received/reftest.list
+++ b/layout/reftests/w3c-css/received/reftest.list
@@ -120,17 +120,17 @@ fuzzy(0-225,0-2529) == css-multicol/mult
 fuzzy(0-225,0-2529) == css-multicol/multicol-nested-margin-005.xht css-multicol/multicol-nested-margin-004-ref.xht
 fuzzy(0-135,0-142) == css-multicol/multicol-overflow-000.xht css-multicol/multicol-overflow-000-ref.xht
 fuzzy(0-204,0-1844) fuzzy-if(skiaContent,0-208,0-1844) == css-multicol/multicol-overflowing-001.xht css-multicol/multicol-overflowing-001-ref.xht
 fuzzy-if(skiaContent,0-64,0-2) == css-multicol/multicol-reduce-000.xht css-multicol/multicol-reduce-000-ref.xht
 == css-multicol/multicol-rule-000.xht css-multicol/multicol-rule-000-ref.xht
 fuzzy-if(!OSX,0-135,0-1584) == css-multicol/multicol-rule-001.xht css-multicol/multicol-rule-001-ref.xht
 fails-if(!OSX) random-if(OSX) == css-multicol/multicol-rule-002.xht css-multicol/multicol-rule-ref.xht
 == css-multicol/multicol-rule-003.xht css-multicol/multicol-rule-003-ref.xht
-pref(layout.css.column-span.enabled,true) fails == css-multicol/multicol-rule-004.xht css-multicol/multicol-rule-004-ref.xht
+pref(layout.css.column-span.enabled,true) == css-multicol/multicol-rule-004.xht css-multicol/multicol-rule-004-ref.xht
 == css-multicol/multicol-rule-color-001.xht css-multicol/multicol-rule-color-001-ref.xht
 == css-multicol/multicol-rule-color-inherit-001.xht css-multicol/multicol-rule-color-inherit-001-ref.xht
 == css-multicol/multicol-rule-color-inherit-002.xht css-multicol/multicol-rule-color-inherit-001-ref.xht
 fuzzy(0-106,0-354) == css-multicol/multicol-rule-dashed-000.xht css-multicol/multicol-rule-dashed-000-ref.xht
 fuzzy(0-106,0-354) == css-multicol/multicol-rule-dotted-000.xht css-multicol/multicol-rule-dotted-000-ref.xht
 fuzzy(0-106,0-354) == css-multicol/multicol-rule-double-000.xht css-multicol/multicol-rule-double-000-ref.xht
 == css-multicol/multicol-rule-fraction-001.xht css-multicol/multicol-rule-fraction-001-ref.xht
 == css-multicol/multicol-rule-fraction-002.xht css-multicol/multicol-rule-fraction-002-ref.xht
--- a/memory/replace/phc/PHC.cpp
+++ b/memory/replace/phc/PHC.cpp
@@ -132,46 +132,33 @@ class InfallibleAllocPolicy {
 //---------------------------------------------------------------------------
 // Stack traces
 //---------------------------------------------------------------------------
 
 // This code is similar to the equivalent code within DMD.
 
 class StackTrace : public phc::StackTrace {
  public:
-  StackTrace() : phc::StackTrace(), mSkipped(false) {}
-
-  bool IsEmpty() const { return mLength == 0 && !mSkipped; }
+  StackTrace() : phc::StackTrace() {}
 
   void Clear() {
     mLength = 0;
-    mSkipped = false;
   }
 
   void Fill();
 
-  void FillSkipped() {
-    mLength = 0;
-    mSkipped = true;
-  }
-
  private:
   static void StackWalkCallback(uint32_t aFrameNumber, void* aPc, void* aSp,
                                 void* aClosure) {
     StackTrace* st = (StackTrace*)aClosure;
     MOZ_ASSERT(st->mLength < kMaxFrames);
     st->mPcs[st->mLength] = aPc;
     st->mLength++;
     MOZ_ASSERT(st->mLength == aFrameNumber);
   }
-
-  // There are some rare cases (see FillSkipped's call sites) where we want to
-  // get a stack trace but cannot do so safely. When this field is set it
-  // indicates such a stack trace.
-  bool mSkipped;
 };
 
 // WARNING WARNING WARNING: this function must only be called when GMut::sMutex
 // is *not* locked, otherwise we might get deadlocks.
 //
 // How? On Windows, MozStackWalk() can lock a mutex, M, from the shared library
 // loader. Another thread might call malloc() while holding M locked (when
 // loading a shared library) and try to lock GMut::sMutex, causing a deadlock.
@@ -185,17 +172,16 @@ class StackTrace : public phc::StackTrac
 // here, so we just require that stack traces be obtained before locking
 // GMut::sMutex.
 //
 // Unfortunately, there is no reliable way at compile-time or run-time to ensure
 // this pre-condition. Hence this large comment.
 //
 void StackTrace::Fill() {
   mLength = 0;
-  mSkipped = false;
 
 #if defined(XP_WIN) && defined(_M_IX86)
   // This avoids MozStackWalk(), which causes unusably slow startup on Win32
   // when it is called during static initialization (see bug 1241684).
   //
   // This code is cribbed from the Gecko Profiler, which also uses
   // FramePointerStackWalk() on Win32: Registers::SyncPopulate() for the
   // frame pointer, and GetStackTop() for the stack end.
@@ -553,26 +539,26 @@ class GMut {
 
     // The usable size, which could be bigger than the requested size.
     // - NeverAllocated: must be 0.
     // - InUse: must be > 0.
     // - Freed: must be > 0.
     size_t mUsableSize;
 
     // The allocation stack.
-    // - NeverAllocated: empty.
-    // - InUse: non-empty.
-    // - Freed: non-empty.
-    StackTrace mAllocStack;
+    // - NeverAllocated: Nothing.
+    // - InUse: Some.
+    // - Freed: Some.
+    Maybe<StackTrace> mAllocStack;
 
     // The free stack.
-    // - NeverAllocated: empty.
-    // - InUse: empty.
-    // - Freed: non-empty.
-    StackTrace mFreeStack;
+    // - NeverAllocated: Nothing.
+    // - InUse: Some.
+    // - Freed: Some.
+    Maybe<StackTrace> mFreeStack;
 
     // The time at which the page is available for reuse, as measured against
     // GAtomic::sNow. When the page is in use this value will be kMaxTime.
     // - NeverAllocated: must be 0.
     // - InUse: must be kMaxTime.
     // - Freed: must be > 0 and < kMaxTime.
     Time mReuseTime;
   };
@@ -615,18 +601,18 @@ class GMut {
     MOZ_ASSERT(aUsableSize == sMallocTable.malloc_good_size(aUsableSize));
 
     PageInfo& page = mPages[aIndex];
     AssertPageNotInUse(aLock, page);
 
     page.mState = PageState::InUse;
     page.mArenaId = aArenaId;
     page.mUsableSize = aUsableSize;
-    page.mAllocStack = aAllocStack;
-    page.mFreeStack.Clear();
+    page.mAllocStack = Some(aAllocStack);
+    page.mFreeStack = Nothing();
     page.mReuseTime = kMaxTime;
   }
 
   void ResizePageInUse(GMutLock aLock, uintptr_t aIndex,
                        const Maybe<arena_id_t>& aArenaId, size_t aNewUsableSize,
                        const StackTrace& aAllocStack) {
     MOZ_ASSERT(aNewUsableSize == sMallocTable.malloc_good_size(aNewUsableSize));
 
@@ -636,17 +622,17 @@ class GMut {
     // page.mState is not changed.
     if (aArenaId.isSome()) {
       // Crash if the arenas don't match.
       MOZ_RELEASE_ASSERT(page.mArenaId == aArenaId);
     }
     page.mUsableSize = aNewUsableSize;
     // We could just keep the original alloc stack, but the realloc stack is
     // more recent and therefore seems more useful.
-    page.mAllocStack = aAllocStack;
+    page.mAllocStack = Some(aAllocStack);
     // page.mFreeStack is not changed.
     // page.mReuseTime is not changed.
   };
 
   void SetPageFreed(GMutLock aLock, uintptr_t aIndex,
                     const Maybe<arena_id_t>& aArenaId,
                     const StackTrace& aFreeStack, Delay aReuseDelay) {
     PageInfo& page = mPages[aIndex];
@@ -662,17 +648,17 @@ class GMut {
     }
 
     // page.musableSize is left unchanged, for reporting on UAF, and for
     // jemalloc_ptr_info() calls that occur after freeing (e.g. in the PtrInfo
     // test in TestJemalloc.cpp).
 
     // page.mAllocStack is left unchanged, for reporting on UAF.
 
-    page.mFreeStack = aFreeStack;
+    page.mFreeStack = Some(aFreeStack);
     page.mReuseTime = GAtomic::Now() + aReuseDelay;
   }
 
   void EnsureInUse(GMutLock, void* aPtr, uintptr_t aIndex) {
     const PageInfo& page = mPages[aIndex];
     MOZ_RELEASE_ASSERT(page.mState != PageState::NeverAllocated);
     if (page.mState == PageState::Freed) {
       LOG("EnsureInUse(%p), failure\n", aPtr);
@@ -771,31 +757,31 @@ class GMut {
     }
     return seed;
   }
 
   void AssertPageInUse(GMutLock, const PageInfo& aPage) {
     MOZ_ASSERT(aPage.mState == PageState::InUse);
     // There is nothing to assert about aPage.mArenaId.
     MOZ_ASSERT(aPage.mUsableSize > 0);
-    MOZ_ASSERT(!aPage.mAllocStack.IsEmpty());
-    MOZ_ASSERT(aPage.mFreeStack.IsEmpty());
+    MOZ_ASSERT(aPage.mAllocStack.isSome());
+    MOZ_ASSERT(aPage.mFreeStack.isNothing());
     MOZ_ASSERT(aPage.mReuseTime == kMaxTime);
   }
 
   void AssertPageNotInUse(GMutLock, const PageInfo& aPage) {
     // We can assert a lot about `NeverAllocated` pages, but not much about
     // `Freed` pages.
 #ifdef DEBUG
     bool isFresh = aPage.mState == PageState::NeverAllocated;
     MOZ_ASSERT(isFresh || aPage.mState == PageState::Freed);
     MOZ_ASSERT_IF(isFresh, aPage.mArenaId == Nothing());
     MOZ_ASSERT(isFresh == (aPage.mUsableSize == 0));
-    MOZ_ASSERT(isFresh == (aPage.mAllocStack.IsEmpty()));
-    MOZ_ASSERT(isFresh == (aPage.mFreeStack.IsEmpty()));
+    MOZ_ASSERT(isFresh == (aPage.mAllocStack.isNothing()));
+    MOZ_ASSERT(isFresh == (aPage.mFreeStack.isNothing()));
     MOZ_ASSERT(aPage.mReuseTime != kMaxTime);
 #endif
   }
 
   // RNG for deciding which allocations to treat specially. It doesn't need to
   // be high quality.
   //
   // This is a raw pointer for the reason explained in the comment above
@@ -1018,18 +1004,17 @@ MOZ_ALWAYS_INLINE static void* PageReall
 
   // A page-to-something transition.
 
   // Note that `disable` has no effect unless it is emplaced below.
   Maybe<AutoDisableOnCurrentThread> disable;
   // Get the stack trace *before* locking the mutex.
   StackTrace stack;
   if (GTls::IsDisabledOnCurrentThread()) {
-    // PHC is disabled on this thread. Get a dummy stack.
-    stack.FillSkipped();
+    // PHC is disabled on this thread. Leave the stack empty.
   } else {
     // Disable on this thread *before* getting the stack trace.
     disable.emplace();
     stack.Fill();
   }
 
   MutexAutoLock lock(GMut::sMutex);
 
@@ -1092,18 +1077,17 @@ MOZ_ALWAYS_INLINE static void PageFree(c
                              : sMallocTable.free(aPtr);
   }
 
   // Note that `disable` has no effect unless it is emplaced below.
   Maybe<AutoDisableOnCurrentThread> disable;
   // Get the stack trace *before* locking the mutex.
   StackTrace freeStack;
   if (GTls::IsDisabledOnCurrentThread()) {
-    // PHC is disabled on this thread. Get a dummy stack.
-    freeStack.FillSkipped();
+    // PHC is disabled on this thread. Leave the stack empty.
   } else {
     // Disable on this thread *before* getting the stack trace.
     disable.emplace();
     freeStack.Fill();
   }
 
   MutexAutoLock lock(GMut::sMutex);
 
@@ -1148,17 +1132,17 @@ static size_t replace_malloc_usable_size
   if (i.isNothing()) {
     // Not a page allocation. Measure it normally.
     return sMallocTable.malloc_usable_size(aPtr);
   }
 
   MutexAutoLock lock(GMut::sMutex);
 
   // Check for malloc_usable_size() of a freed block.
-  gMut->EnsureInUse(lock, aPtr, *i);
+  gMut->EnsureInUse(lock, const_cast<void*>(aPtr), *i);
 
   return gMut->PageUsableSize(lock, *i);
 }
 
 void replace_jemalloc_stats(jemalloc_stats_t* aStats) {
   sMallocTable.jemalloc_stats(aStats);
 
   // Add all the pages to `mapped`.
--- a/memory/replace/phc/PHC.h
+++ b/memory/replace/phc/PHC.h
@@ -3,23 +3,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/. */
 
 #ifndef PHC_h
 #define PHC_h
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Maybe.h"
 #include <stdint.h>
 #include <stdlib.h>
 
 namespace mozilla {
 namespace phc {
 
-// Note: a more compact stack trace representation could be achieved with
+// Note: a stack trace may have no frames due to a collection problem.
+//
+// Also note: a more compact stack trace representation could be achieved with
 // some effort.
 struct StackTrace {
  public:
   static const size_t kMaxFrames = 16;
 
   // The number of PCs in the stack trace.
   size_t mLength;
 
@@ -65,18 +68,18 @@ class AddrInfo {
   // The base address of the containing PHC allocation, if there is one.
   const void* mBaseAddr;
 
   // The usable size of the containing PHC allocation, if there is one.
   size_t mUsableSize;
 
   // The allocation and free stack traces of the containing PHC allocation, if
   // there is one.
-  StackTrace mAllocStack;
-  StackTrace mFreeStack;
+  mozilla::Maybe<StackTrace> mAllocStack;
+  mozilla::Maybe<StackTrace> mFreeStack;
 
   // Default to no PHC info.
   AddrInfo()
       : mKind(Kind::Unknown),
         mBaseAddr(nullptr),
         mUsableSize(0),
         mAllocStack(),
         mFreeStack() {}
--- a/memory/replace/phc/test/gtest/TestPHC.cpp
+++ b/memory/replace/phc/test/gtest/TestPHC.cpp
@@ -11,20 +11,20 @@
 
 using namespace mozilla;
 
 bool PHCInfoEq(phc::AddrInfo& aInfo, phc::AddrInfo::Kind aKind, void* aBaseAddr,
                size_t aUsableSize, bool aHasAllocStack, bool aHasFreeStack) {
   return aInfo.mKind == aKind && aInfo.mBaseAddr == aBaseAddr &&
          aInfo.mUsableSize == aUsableSize &&
          // Proper stack traces will have at least 3 elements.
-         (aHasAllocStack ? (aInfo.mAllocStack.mLength > 2)
-                         : (aInfo.mAllocStack.mLength == 0)) &&
-         (aHasFreeStack ? (aInfo.mFreeStack.mLength > 2)
-                        : (aInfo.mFreeStack.mLength == 0));
+         (aHasAllocStack ? (aInfo.mAllocStack->mLength > 2)
+                         : (aInfo.mAllocStack.isNothing())) &&
+         (aHasFreeStack ? (aInfo.mFreeStack->mLength > 2)
+                        : (aInfo.mFreeStack.isNothing()));
 }
 
 bool JeInfoEq(jemalloc_ptr_info_t& aInfo, PtrInfoTag aTag, void* aAddr,
               size_t aSize, arena_id_t arenaId) {
   return aInfo.tag == aTag && aInfo.addr == aAddr && aInfo.size == aSize
 #ifdef MOZ_DEBUG
          && aInfo.arenaId == arenaId
 #endif
@@ -65,25 +65,25 @@ TEST(PHC, TestPHCBasics)
   if (!p) {
     MOZ_CRASH("failed to get a PHC allocation");
   }
 
   // Test an in-use PHC allocation, via its base address.
   ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p, &phcInfo));
   ASSERT_TRUE(
       PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, p, 32ul, true, false));
-  ASSERT_EQ(malloc_usable_size(p), 32ul);
+  ASSERT_EQ(moz_malloc_usable_size(p), 32ul);
   jemalloc_ptr_info(p, &jeInfo);
   ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, p, 32, 0));
 
   // Test an in-use PHC allocation, via an address in its middle.
   ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p + 10, &phcInfo));
   ASSERT_TRUE(
       PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, p, 32ul, true, false));
-  ASSERT_EQ(malloc_usable_size(p), 32ul);
+  ASSERT_EQ(moz_malloc_usable_size(p), 32ul);
   jemalloc_ptr_info(p + 10, &jeInfo);
   ASSERT_TRUE(JeInfoEq(jeInfo, TagLiveAlloc, p, 32, 0));
 
   // Test an in-use PHC allocation, via an address past its end. The results
   // for phcInfo should be the same, but be different for jeInfo.
   ASSERT_TRUE(ReplaceMalloc::IsPHCAllocation(p + 64, &phcInfo));
   ASSERT_TRUE(
       PHCInfoEq(phcInfo, phc::AddrInfo::Kind::InUsePage, p, 32ul, true, false));
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -18,17 +18,17 @@
 # (Except under unusual circumstances where the value defined here must be
 # overridden, e.g. for some Thunderbird prefs. In those cases the default
 # value from the data file will override the static default value defined
 # here.)
 #
 # Please follow the existing prefs naming convention when considering adding a
 # new pref, and don't create a new pref group unless it's appropriate and there
 # are likely to be multiple prefs within that group. (If you do, you'll need to
-# update the `groups` variable in modules/libpref/moz.build.)
+# update the `pref_groups` variable in modules/libpref/moz.build.)
 #
 # Definitions
 # -----------
 # A pref definition looks like this:
 #
 #   - name: <pref-name>                    # mandatory
 #     type: <cpp-type>                     # mandatory
 #     value: <default-value>               # mandatory
@@ -970,16 +970,109 @@
 # Disable sending console to logcat on release builds.
 - name: consoleservice.logcat
   type: RelaxedAtomicBool
   value: @IS_NOT_RELEASE_OR_BETA@
   mirror: always
 #endif
 
 #---------------------------------------------------------------------------
+# Prefs starting with "content."
+#---------------------------------------------------------------------------
+
+# Back off timer notification after count.
+# -1 means never.
+- name: content.notify.backoffcount
+  type: int32_t
+  value: -1
+  mirror: always
+
+# Notification interval in microseconds.
+# The notification interval has a dramatic effect on how long it takes to
+# initially display content for slow connections. The current value
+# provides good incremental display of content without causing an increase
+# in page load time. If this value is set below 1/10 of a second it starts
+# to impact page load performance.
+# See bugzilla bug 72138 for more info.
+- name: content.notify.interval
+  type: int32_t
+  value: 120000
+  mirror: always
+
+# Do we notify based on time?
+- name: content.notify.ontimer
+  type: bool
+  value: true
+  mirror: always
+
+# How many times to deflect in interactive mode.
+- name: content.sink.interactive_deflect_count
+  type: int32_t
+  value: 0
+  mirror: always
+
+# How many times to deflect in perf mode.
+- name: content.sink.perf_deflect_count
+  type: int32_t
+  value: 200
+  mirror: always
+
+# Parse mode for handling pending events.
+# 0 = don't check for pending events
+# 1 = don't deflect if there are pending events
+# 2 = bail if there are pending events
+- name: content.sink.pending_event_mode
+  type: int32_t
+# ifdef XP_WIN
+  value: 1
+# else
+  value: 0
+# endif
+  mirror: always
+
+# How often to probe for pending events. 1 = every token.
+- name: content.sink.event_probe_rate
+  type: int32_t
+  value: 1
+  mirror: always
+
+# How long to stay off the event loop in interactive mode.
+- name: content.sink.interactive_parse_time
+  type: int32_t
+  value: 3000
+  mirror: always
+
+# How long to stay off the event loop in perf mode.
+- name: content.sink.perf_parse_time
+  type: int32_t
+  value: 360000
+  mirror: always
+
+#  How long to be in interactive mode after an event.
+- name: content.sink.interactive_time
+  type: uint32_t
+  value: 750000
+  mirror: always
+
+# How long to stay in perf mode after initial loading.
+- name: content.sink.initial_perf_time
+  type: uint32_t
+  value: 2000000
+  mirror: always
+
+# Should we switch between perf-mode and interactive-mode?
+# 0 = Switch
+# 1 = Interactive mode
+# 2 = Perf mode
+- name: content.sink.enable_perf_mode
+  type: int32_t
+  value: 0
+  mirror: always
+
+#---------------------------------------------------------------------------
 # Prefs starting with "device."
 #---------------------------------------------------------------------------
 
 # Is support for the device sensors API enabled?
 - name: device.sensors.enabled
   type: bool
   value: true
   mirror: always
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1112,21 +1112,16 @@ pref("dom.cycle_collector.incremental", 
 
 // Whether to shim a Components object on untrusted windows.
 #ifdef NIGHTLY_BUILD
 pref("dom.use_components_shim", false);
 #else // NIGHTLY_BUILD
 pref("dom.use_components_shim", true);
 #endif // NIGHTLY_BUILD
 
-// Parsing perf prefs. For now just mimic what the old code did.
-#ifndef XP_WIN
-pref("content.sink.pending_event_mode", 0);
-#endif
-
 // Disable popups from plugins by default
 //   0 = openAllowed
 //   1 = openControlled
 //   2 = openBlocked
 //   3 = openAbused
 pref("privacy.popups.disable_from_plugins", 3);
 
 // Enable Paritioned LocalStorage for a list of hosts when detected as trackers
--- a/modules/libpref/moz.build
+++ b/modules/libpref/moz.build
@@ -27,16 +27,17 @@ XPIDL_MODULE = 'pref'
 pref_groups = [
     'accessibility',
     'apz',
     'beacon',
     'browser',
     'canvas',
     'channelclassifier',
     'clipboard',
+    'content',
     'device',
     'devtools',
     'dom',
     'editor',
     'extensions',
     'fission',
     'full_screen_api',
     'general',
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "mozilla/dom/nsCSPService.h"
 
 #include "GeckoProfiler.h"
 #include "mozAutoDocUpdate.h"
 #include "mozilla/IdleTaskRunner.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_content.h"
 #include "mozilla/StaticPrefs_security.h"
 #include "mozilla/StaticPrefs_view_source.h"
 #include "mozilla/css/Loader.h"
 #include "nsContentUtils.h"
 #include "nsDocShell.h"
 #include "nsError.h"
 #include "nsHtml5AutoPauseUpdate.h"
 #include "nsHtml5Parser.h"
@@ -310,19 +311,20 @@ void nsHtml5TreeOpExecutor::ContinueInte
     if (gBackgroundFlushRunner) {
       return;
     }
     // Now we set up a repetitive idle scheduler for flushing background list.
     gBackgroundFlushRunner = IdleTaskRunner::Create(
         &BackgroundFlushCallback,
         "nsHtml5TreeOpExecutor::BackgroundFlushCallback",
         250,  // The hard deadline: 250ms.
-        nsContentSink::sInteractiveParseTime / 1000,  // Required budget.
-        true,                                         // repeating
-        [] { return false; });                        // MayStopProcessing
+        StaticPrefs::content_sink_interactive_parse_time() /
+            1000,               // Required budget.
+        true,                   // repeating
+        [] { return false; });  // MayStopProcessing
   }
 }
 
 void nsHtml5TreeOpExecutor::FlushSpeculativeLoads() {
   nsTArray<nsHtml5SpeculativeLoad> speculativeLoadQueue;
   mStage.MoveSpeculativeLoadsTo(speculativeLoadQueue);
   nsHtml5SpeculativeLoad* start = speculativeLoadQueue.Elements();
   nsHtml5SpeculativeLoad* end = start + speculativeLoadQueue.Length();
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-multicol/multicol-breaking-nobackground-000.html.ini
@@ -0,0 +1,2 @@
+[multicol-breaking-nobackground-000.html]
+  prefs: [layout.css.column-span.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/css-multicol/multicol-breaking-nobackground-001.html.ini
@@ -0,0 +1,2 @@
+[multicol-breaking-nobackground-001.html]
+  prefs: [layout.css.column-span.enabled:true]
copy from testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
copy to testing/web-platform/meta/css/css-multicol/multicol-rule-nested-balancing-001.html.ini
--- a/testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
+++ b/testing/web-platform/meta/css/css-multicol/multicol-rule-nested-balancing-001.html.ini
@@ -1,2 +1,2 @@
-[multicol-breaking-000.html]
+[multicol-rule-nested-balancing-001.html]
   prefs: [layout.css.column-span.enabled:true]
copy from testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
copy to testing/web-platform/meta/css/css-multicol/multicol-rule-nested-balancing-002.html.ini
--- a/testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
+++ b/testing/web-platform/meta/css/css-multicol/multicol-rule-nested-balancing-002.html.ini
@@ -1,2 +1,2 @@
-[multicol-breaking-000.html]
+[multicol-rule-nested-balancing-002.html]
   prefs: [layout.css.column-span.enabled:true]
copy from testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
copy to testing/web-platform/meta/css/css-multicol/multicol-span-all-rule-001.html.ini
--- a/testing/web-platform/meta/css/css-multicol/multicol-breaking-000.html.ini
+++ b/testing/web-platform/meta/css/css-multicol/multicol-span-all-rule-001.html.ini
@@ -1,2 +1,2 @@
-[multicol-breaking-000.html]
+[multicol-span-all-rule-001.html]
   prefs: [layout.css.column-span.enabled:true]
deleted file mode 100644
--- a/testing/web-platform/meta/svg/types/scripted/event-handler-all-document-element-events.svg.ini
+++ /dev/null
@@ -1,19 +0,0 @@
-[event-handler-all-document-element-events.svg]
-  [oncut: the content attribute must be compiled into a function as the corresponding property]
-    expected: FAIL
-
-  [oncut: the content attribute must execute when an event is dispatched]
-    expected: FAIL
-
-  [oncopy: the content attribute must be compiled into a function as the corresponding property]
-    expected: FAIL
-
-  [oncopy: the content attribute must execute when an event is dispatched]
-    expected: FAIL
-
-  [onpaste: the content attribute must be compiled into a function as the corresponding property]
-    expected: FAIL
-
-  [onpaste: the content attribute must execute when an event is dispatched]
-    expected: FAIL
-
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-000-ref.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-000-ref.html
@@ -47,17 +47,17 @@
   padding-left: 7px;
 }
 
 </style>
 
 <div class="outer">
   <div class="blueborders"></div>
   <div class="innerbg" style="left: 0"></div>
-  <div class="inner lefthalf" style="left: 0; height: 60px">
+  <div class="inner lefthalf" style="left: 0">
     AAAAA<br>
     BBBBB<br>
     CCCCC
   </div>
   <div class="inner righthalf" style="left: 95px">
     DDDDD<br>
     EEEEE
   </div>
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-000.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-000.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <title>CSS Test: breaking of a multicolumn</title>
 <meta charset="utf-8">
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="https://mozilla.org/">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#cf">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
 <link rel="match" href="multicol-breaking-000-ref.html">
 <style>
 
 .outer {
   height: 100px;
   column-fill: auto;
   width: 800px;
   column-count: 4;
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-001-ref.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-001-ref.html
@@ -62,17 +62,17 @@
   <div class="inner righthalf" style="left: 95px">
     FFFFF<br>
     GGGGG<br>
     HHHHH<br>
     IIIII<br>
     JJJJJ
   </div>
   <div class="innerbg" style="left: 204px"></div>
-  <div class="inner lefthalf" style="left: 204px; height: 80px">
+  <div class="inner lefthalf" style="left: 204px">
     KKKKK<br>
     LLLLL<br>
     MMMMM<br>
     NNNNN
   </div>
   <div class="inner righthalf" style="left: 299px">
     OOOOO<br>
     PPPPP<br>
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-001.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-001.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <title>CSS Test: breaking of a multicolumn</title>
 <meta charset="utf-8">
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="https://mozilla.org/">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#cf">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
 <link rel="match" href="multicol-breaking-001-ref.html">
 <style>
 
 .outer {
   height: 100px;
   column-fill: auto;
   width: 800px;
   column-count: 4;
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-004-ref.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-004-ref.html
@@ -72,17 +72,17 @@
     FFFFF<br>
     GGGGG<br>
     HHHHH<br>
     IIIII<br>
     JJJJJ
   </div>
   <div class="border-bottom" style="left: 0"></div>
   <div class="innerbg" style="left: 204px"></div>
-  <div class="inner lefthalf" style="left: 204px; height: 80px">
+  <div class="inner lefthalf" style="left: 204px">
     KKKKK<br>
     LLLLL<br>
     MMMMM<br>
     NNNNN
   </div>
   <div class="border-bottom" style="left: 204px;"></div>
   <div class="inner righthalf" style="left: 299px">
     OOOOO<br>
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-004.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-004.html
@@ -1,16 +1,17 @@
 <!DOCTYPE HTML>
 <title>CSS Test: breaking of a multicolumn</title>
 <meta charset="utf-8">
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
 <link rel="author" title="Mozilla" href="https://mozilla.org/">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#cf">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
 <link rel="help" href="https://drafts.csswg.org/css-break/#break-decoration">
 <link rel="match" href="multicol-breaking-004-ref.html">
 <style>
 
 .outer {
   height: 125px;
   column-fill: auto;
   width: 800px;
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-000-ref.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-000-ref.html
@@ -27,17 +27,17 @@
 
 .righthalf {
   padding-left: 7px;
 }
 
 </style>
 
 <div class="outer">
-  <div class="inner lefthalf" style="left: 0; height: 60px">
+  <div class="inner lefthalf" style="left: 0">
     AAAAA<br>
     BBBBB<br>
     CCCCC
   </div>
   <div class="inner righthalf" style="left: 95px">
     DDDDD<br>
     EEEEE
   </div>
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-000.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-000.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <title>CSS Test: breaking of a multicolumn</title>
 <meta charset="utf-8">
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="https://mozilla.org/">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#cf">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
 <link rel="match" href="multicol-breaking-nobackground-000-ref.html">
 <style>
 
 .outer {
   height: 100px;
   column-fill: auto;
   width: 800px;
   column-count: 4;
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-001-ref.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-001-ref.html
@@ -41,17 +41,17 @@
   </div>
   <div class="inner righthalf" style="left: 95px">
     FFFFF<br>
     GGGGG<br>
     HHHHH<br>
     IIIII<br>
     JJJJJ
   </div>
-  <div class="inner lefthalf" style="left: 204px; height: 80px">
+  <div class="inner lefthalf" style="left: 204px">
     KKKKK<br>
     LLLLL<br>
     MMMMM<br>
     NNNNN
   </div>
   <div class="inner righthalf" style="left: 299px">
     OOOOO<br>
     PPPPP<br>
--- a/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-001.html
+++ b/testing/web-platform/tests/css/css-multicol/multicol-breaking-nobackground-001.html
@@ -1,15 +1,16 @@
 <!DOCTYPE HTML>
 <title>CSS Test: breaking of a multicolumn</title>
 <meta charset="utf-8">
 <link rel="author" title="L. David Baron" href="https://dbaron.org/">
 <link rel="author" title="Mozilla" href="https://mozilla.org/">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#column-gaps-and-rules">
 <link rel="help" href="https://drafts.csswg.org/css-multicol/#cf">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
 <link rel="match" href="multicol-breaking-nobackground-001-ref.html">
 <style>
 
 .outer {
   height: 100px;
   column-fill: auto;
   width: 800px;
   column-count: 4;
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-rule-nested-balancing-001-ref.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test Reference: Test the column rules' block-size with nested balancing multicol container</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+
+  <style>
+  .outer {
+    column-count: 2;
+    column-rule: 6px solid black;
+    column-fill: auto;
+    width: 400px;
+    height: 250px;
+  }
+  .inner {
+    column-count: 2;
+    column-rule: 3px solid gray;
+    column-fill: auto;
+    height: 200px;
+  }
+  .outer-block {
+    background-color: lightgreen;
+    height: 200px;
+  }
+  .inner-block {
+    background-color: lightblue;
+    height: 150px;
+  }
+  .space {
+    height: 50px;
+  }
+  </style>
+
+  <article class="outer">
+    <div class="outer-block"></div>
+    <div class="space"></div>
+    <article class="inner">
+      <div class="inner-block"></div><div class="space"></div>
+      <div class="inner-block"></div><div class="space"></div>
+    </article>
+  </article>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-rule-nested-balancing-001.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Test the column rules' block-size with nested balancing multicol container</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#cf">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-gaps-and-rules">
+  <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
+  <link rel="match" href="multicol-rule-nested-balancing-001-ref.html">
+  <meta name="assert" content="This test verifies that the column-rules are extended to the content block-end edges of their corresponding inner and outer multicol container.">
+
+  <style>
+  .outer {
+    column-count: 2;
+    column-rule: 6px solid black;
+    width: 400px;
+    height: 250px;
+  }
+  .inner {
+    column-count: 2;
+    column-rule: 3px solid gray;
+    height: 200px;
+  }
+  .outer-block {
+    background-color: lightgreen;
+    height: 200px;
+  }
+  .inner-block {
+    background-color: lightblue;
+    height: 300px;
+  }
+  </style>
+
+  <article class="outer">
+    <div class="outer-block"></div>
+    <article class="inner">
+      <div class="inner-block"></div>
+    </article>
+  </article>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-rule-nested-balancing-002-ref.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test Reference: Test the column rules' block-size with nested balancing multicol container</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+
+  <style>
+  .outer {
+    column-count: 2;
+    column-rule: 6px solid black;
+    column-fill: auto;
+    width: 400px;
+    height: 250px;
+  }
+  .inner {
+    column-count: 2;
+    column-rule: 3px solid gray;
+    column-fill: auto;
+    height: 200px;
+  }
+  .outer-block {
+    background-color: lightgreen;
+    height: 200px;
+  }
+  .inner-block {
+    background-color: lightblue;
+    height: 200px;
+  }
+  .space {
+    height: 50px;
+  }
+  </style>
+
+  <article class="outer">
+    <div class="outer-block"></div>
+    <div class="space"></div>
+    <article class="inner">
+      <div class="inner-block"></div>
+      <div class="inner-block"></div>
+    </article>
+  </article>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-rule-nested-balancing-002.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Test the column rules' block-size with nested balancing multicol container</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#cf">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-gaps-and-rules">
+  <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
+  <link rel="match" href="multicol-rule-nested-balancing-002-ref.html">
+  <meta name="assert" content="This test verifies that the column-rules are extended to the content block-end edges of their corresponding inner and outer multicol container, where the inner container has height: auto.">
+
+  <style>
+  .outer {
+    column-count: 2;
+    column-rule: 6px solid black;
+    width: 400px;
+    height: 250px;
+  }
+  .inner {
+    column-count: 2;
+    column-rule: 3px solid gray;
+    height: auto;
+  }
+  .outer-block {
+    background-color: lightgreen;
+    height: 200px;
+  }
+  .inner-block {
+    background-color: lightblue;
+    height: 400px;
+  }
+  </style>
+
+  <article class="outer">
+    <div class="outer-block"></div>
+    <article class="inner">
+      <div class="inner-block"></div>
+    </article>
+  </article>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-span-all-rule-001-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Test the column-rule's block-size</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+
+  <style>
+  article {
+    column-count: 2;
+    column-rule: 6px solid;
+    width: 400px;
+    height: 500px;
+    background-color: lightgreen;
+    border: 2em solid purple;
+    padding: 2em;
+  }
+  div.block {
+    width: 100px;
+    height: 200px;
+  }
+  div.column-span {
+    column-span: all;
+    height: 50px;
+    background-color: lightblue;
+  }
+  </style>
+
+  <article>
+    <div class="block">block1</div>
+    <div class="column-span">column-span1</div>
+    <div class="block">block2</div>
+    <div class="column-span">column-span2</div>
+    <div class="block" style="height: 400px;">block3</div>
+  </article>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-multicol/multicol-span-all-rule-001.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+  <meta charset="utf-8">
+  <title>CSS Multi-column Layout Test: Test the column rule's block-size</title>
+  <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+  <link rel="author" title="Mozilla" href="https://www.mozilla.org/">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-span">
+  <link rel="help" href="https://drafts.csswg.org/css-multicol-1/#column-gaps-and-rules">
+  <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/2309">
+  <link rel="match" href="multicol-span-all-rule-001-ref.html">
+  <meta name="assert" content="This test verifies that the column-rule after the last column-span is extended to the content block-end edge of the multicol container.">
+
+  <style>
+  article {
+    column-count: 2;
+    column-rule: 6px solid;
+    width: 400px;
+    height: 500px;
+    background-color: lightgreen;
+    border: 2em solid purple;
+    padding: 2em;
+  }
+  div.block {
+    width: 100px;
+    height: 200px;
+  }
+  div.column-span {
+    column-span: all;
+    height: 50px;
+    background-color: lightblue;
+  }
+  </style>
+
+  <article>
+    <!-- Each block spreads its height evenly into two columns, and
+         each column contains 100px height. -->
+    <div class="block">block1</div>
+    <div class="column-span">column-span1</div>
+    <div class="block">block2</div>
+    <div class="column-span">column-span2</div>
+    <!-- The column rule after column-span2 should extend to the content edge
+         of the multicol container as if block3 has "height: 400px;"  -->
+    <div class="block">block3</div>
+  </article>
+</html>
--- a/toolkit/components/passwordmgr/LoginManagerPrompter.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerPrompter.jsm
@@ -924,38 +924,55 @@ LoginManagerPrompter.prototype = {
   set openerBrowser(aOpenerBrowser) {
     this._openerBrowser = aOpenerBrowser;
   },
 
   promptToSavePassword(aLogin, dismissed = false, notifySaved = false) {
     this.log("promptToSavePassword");
     var notifyObj = this._getPopupNote();
     if (notifyObj) {
-      this._showLoginCaptureDoorhanger(aLogin, "password-save", {
-        dismissed: this._inPrivateBrowsing || dismissed,
-        notifySaved,
-        extraAttr: notifySaved ? "attention" : "",
-      });
+      this._showLoginCaptureDoorhanger(
+        aLogin,
+        "password-save",
+        {
+          dismissed: this._inPrivateBrowsing || dismissed,
+          extraAttr: notifySaved ? "attention" : "",
+        },
+        {
+          notifySaved,
+        }
+      );
       Services.obs.notifyObservers(aLogin, "passwordmgr-prompt-save");
     } else {
       this._showSaveLoginDialog(aLogin);
     }
   },
 
   /**
    * Displays the PopupNotifications.jsm doorhanger for password save or change.
    *
    * @param {nsILoginInfo} login
    *        Login to save or change. For changes, this login should contain the
    *        new password.
    * @param {string} type
    *        This is "password-save" or "password-change" depending on the
    *        original notification type. This is used for telemetry and tests.
+   * @param {object} showOptions
+   *        Options to pass along to PopupNotifications.show().
+   * @param {bool} [options.notifySaved = false]
+   *        Whether to indicate to the user that the login was already saved.
+   * @param {string} [options.messageStringID = undefined]
+   *        An optional string ID to override the default message.
    */
-  _showLoginCaptureDoorhanger(login, type, options = {}) {
+  _showLoginCaptureDoorhanger(
+    login,
+    type,
+    showOptions = {},
+    { notifySaved = false, messageStringID } = {}
+  ) {
     let { browser } = this._getNotifyWindow();
     if (!browser) {
       return;
     }
 
     let saveMsgNames = {
       prompt: login.username === "" ? "saveLoginMsgNoUser" : "saveLoginMsg",
       buttonLabel: "saveLoginButtonAllow.label",
@@ -970,16 +987,20 @@ LoginManagerPrompter.prototype = {
       buttonAccessKey: "updateLoginButtonAccessKey",
       secondaryButtonLabel: "updateLoginButtonDeny.label",
       secondaryButtonAccessKey: "updateLoginButtonDeny.accesskey",
     };
 
     let initialMsgNames =
       type == "password-save" ? saveMsgNames : changeMsgNames;
 
+    if (messageStringID) {
+      changeMsgNames.prompt = messageStringID;
+    }
+
     let brandBundle = Services.strings.createBundle(BRAND_BUNDLE);
     let brandShortName = brandBundle.GetStringFromName("brandShortName");
     let host = this._getShortDisplayHost(login.origin);
     let promptMsg =
       type == "password-save"
         ? this._getLocalizedString(saveMsgNames.prompt, [brandShortName, host])
         : this._getLocalizedString(changeMsgNames.prompt);
 
@@ -1288,21 +1309,21 @@ LoginManagerPrompter.prototype = {
                 chromeDoc
                   .getElementById("password-notification-visibilityToggle")
                   .removeEventListener("command", onVisibilityToggle);
                 break;
             }
             return false;
           },
         },
-        options
+        showOptions
       )
     );
 
-    if (options.notifySaved) {
+    if (notifySaved) {
       let notification = popupNote.getNotification(notificationID);
       let anchor = notification.anchorElement;
       anchor.ownerGlobal.ConfirmationHint.show(anchor, "passwordSaved");
     }
   },
 
   _removeLoginNotifications() {
     var popupNote = this._getPopupNote();
@@ -1432,21 +1453,41 @@ LoginManagerPrompter.prototype = {
     dismissed = false,
     notifySaved = false
   ) {
     let login = aOldLogin.clone();
     login.origin = aNewLogin.origin;
     login.formActionOrigin = aNewLogin.formActionOrigin;
     login.password = aNewLogin.password;
     login.username = aNewLogin.username;
-    this._showLoginCaptureDoorhanger(login, "password-change", {
-      dismissed,
-      notifySaved,
-      extraAttr: notifySaved ? "attention" : "",
-    });
+
+    let messageStringID;
+    if (
+      aOldLogin.username === "" &&
+      login.username !== "" &&
+      login.password == aOldLogin.password
+    ) {
+      // If the saved password matches the password we're prompting with then we
+      // are only prompting to let the user add a username since there was one in
+      // the form. Change the message so the purpose of the prompt is clearer.
+      messageStringID = "updateLoginMsgAddUsername";
+    }
+
+    this._showLoginCaptureDoorhanger(
+      login,
+      "password-change",
+      {
+        dismissed,
+        extraAttr: notifySaved ? "attention" : "",
+      },
+      {
+        notifySaved,
+        messageStringID,
+      }
+    );
 
     let oldGUID = aOldLogin.QueryInterface(Ci.nsILoginMetaInfo).guid;
     Services.obs.notifyObservers(
       aNewLogin,
       "passwordmgr-prompt-change",
       oldGUID
     );
   },
--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger.js
@@ -309,16 +309,21 @@ add_task(async function test_pwOnlyLogin
 
   await testSubmittingLoginForm("subtst_notifications_1.html", function(
     fieldValues
   ) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "notifyp1", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "checking for notification popup");
+    is(
+      notif.message,
+      "Would you like to add a username to the saved password?",
+      "Check message"
+    );
     notif.remove();
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
   is(login.username, "", "Check the username");
   is(login.password, "notifyp1", "Check the password");
@@ -384,16 +389,17 @@ add_task(async function test_changeUPLog
 
   await testSubmittingLoginForm("subtst_notifications_8.html", async function(
     fieldValues
   ) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+    is(notif.message, "Would you like to update this login?", "Check message");
 
     await checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, DONT_CHANGE_BUTTON);
   });
 
   let logins = Services.logins.getAllLogins();
   is(logins.length, 1, "Should only have 1 login");
   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
@@ -410,16 +416,17 @@ add_task(async function test_changeUPLog
 
   await testSubmittingLoginForm("subtst_notifications_8.html", async function(
     fieldValues
   ) {
     is(fieldValues.username, "notifyu1", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+    is(notif.message, "Would you like to update this login?", "Check message");
 
     await checkDoorhangerUsernamePassword("notifyu1", "pass2");
     clickDoorhangerButton(notif, CHANGE_BUTTON);
 
     ok(!getCaptureDoorhanger("password-change"), "popup should be gone");
   });
 
   let logins = Services.logins.getAllLogins();
@@ -433,26 +440,27 @@ add_task(async function test_changeUPLog
 
   // cleanup
   login1.password = "pass2";
   Services.logins.removeLogin(login1);
   login1.password = "notifyp1";
 });
 
 add_task(async function test_changePLoginOnUPForm() {
-  info("Check for change-password popup, p-only login on u+p form.");
+  info("Check for change-password popup, p-only login on u+p form (empty u).");
   Services.logins.addLogin(login2);
 
   await testSubmittingLoginForm("subtst_notifications_9.html", async function(
     fieldValues
   ) {
     is(fieldValues.username, "", "Checking submitted username");
     is(fieldValues.password, "pass2", "Checking submitted password");
     let notif = getCaptureDoorhanger("password-change");
     ok(notif, "got notification popup");
+    is(notif