зеркало из https://github.com/mozilla/gecko-dev.git
Merge autoland to mozilla-central. a=merge
This commit is contained in:
Коммит
b3fd653bc6
|
@ -33,12 +33,13 @@
|
|||
#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>
|
||||
|
||||
|
@ -255,13 +256,10 @@ uint32_t HyperTextAccessible::DOMPointToOffset(nsINode* aNode,
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,12 @@ OuterDocAccessible::OuterDocAccessible(nsIContent* aContent,
|
|||
: 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) {
|
||||
|
|
|
@ -767,7 +767,12 @@ void DocAccessibleParent::SendParentCOMProxy(Accessible* aOuterDoc) {
|
|||
|
||||
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)));
|
||||
|
|
|
@ -218,6 +218,9 @@ class DocAccessibleParent : public ProxyAccessible,
|
|||
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.
|
||||
|
|
|
@ -46,7 +46,7 @@ void DocAccessibleChild::Shutdown() {
|
|||
|
||||
ipc::IPCResult DocAccessibleChild::RecvParentCOMProxy(
|
||||
const IDispatchHolder& aParentCOMProxy) {
|
||||
MOZ_ASSERT(!mParentProxy && !aParentCOMProxy.IsNull());
|
||||
MOZ_ASSERT(!aParentCOMProxy.IsNull());
|
||||
mParentProxy.reset(const_cast<IDispatchHolder&>(aParentCOMProxy).Release());
|
||||
SetConstructedInParentProcess();
|
||||
|
||||
|
|
|
@ -1547,10 +1547,9 @@ pref("browser.ping-centre.production.endpoint", "https://tiles.services.mozilla.
|
|||
// Enable GMP support in the addon manager.
|
||||
pref("media.gmp-provider.enabled", true);
|
||||
|
||||
#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
|
||||
// Enable blocking access to storage from tracking resources by default.
|
||||
pref("network.cookie.cookieBehavior", 4 /* BEHAVIOR_REJECT_TRACKER */);
|
||||
#ifdef EARLY_BETA_OR_EARLIER
|
||||
// Enable fingerprinting blocking by default only in nightly and early beta.
|
||||
pref("privacy.trackingprotection.fingerprinting.enabled", true);
|
||||
#endif
|
||||
|
@ -1740,12 +1739,8 @@ 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.
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
# -*- 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/.
|
||||
|
||||
# PGO
|
||||
# ==============================================================
|
||||
@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)
|
||||
|
||||
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)
|
||||
@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))
|
||||
|
||||
|
||||
@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')
|
||||
|
||||
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)
|
|
@ -96,6 +96,7 @@ def rustc_info(rustc):
|
|||
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)))
|
||||
|
|
|
@ -1411,54 +1411,6 @@ 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.
|
||||
|
@ -1482,149 +1434,6 @@ def is_windows(target, host):
|
|||
|
||||
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
|
||||
# ==============================================================
|
||||
|
||||
|
|
|
@ -132,7 +132,11 @@ 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)
|
||||
|
|
|
@ -25,7 +25,6 @@ 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
|
||||
|
||||
|
@ -50,7 +49,6 @@ 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
|
||||
|
@ -106,26 +104,10 @@ skipPausingTooltip.label=Deactivate breakpoints
|
|||
# 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
|
||||
|
@ -141,76 +123,14 @@ threadsHeader=Threads
|
|||
# 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
|
||||
|
@ -283,14 +203,6 @@ projectTextSearch.placeholder=Find in files…
|
|||
# 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
|
||||
|
@ -322,50 +234,6 @@ sourceSearch.search.againPrev.key3=Cmd+Shift+G
|
|||
# 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
|
||||
|
@ -377,7 +245,6 @@ 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
|
||||
|
@ -403,27 +270,14 @@ 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
|
||||
|
@ -434,10 +288,6 @@ 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
|
||||
|
@ -479,18 +329,11 @@ callStack.group.expandTooltip=Show %S frames
|
|||
# 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
|
||||
|
@ -503,10 +346,6 @@ editor.searchResults.nextResult=Next result
|
|||
# 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
|
||||
|
@ -538,14 +377,6 @@ editor.addConditionBreakpoint.accesskey=c
|
|||
# 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
|
||||
|
@ -569,10 +400,6 @@ editor.conditionalPanel.placeholder2=Breakpoint condition, e.g. items.length > 0
|
|||
# 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
|
||||
|
@ -608,7 +435,6 @@ expressions.placeholder=Add watch expression
|
|||
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
|
||||
|
@ -662,7 +488,6 @@ 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.
|
||||
|
@ -676,10 +501,6 @@ sourceFooter.mappedSourceTooltip=(Source mapped from %S)
|
|||
# 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)
|
||||
|
@ -724,9 +545,6 @@ 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
|
||||
|
@ -773,10 +591,6 @@ welcome.findInFiles=%S to find in files
|
|||
# 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
|
||||
|
@ -789,18 +603,6 @@ sourceSearch.search=Search sources…
|
|||
# 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
|
||||
|
@ -818,101 +620,6 @@ wasmIsNotAvailable=Please refresh to debug this module
|
|||
# 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…
|
||||
|
@ -959,19 +666,6 @@ symbolSearch.searchModifier.caseSensitive=Case sensitive
|
|||
# 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
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
#include "nsSandboxFlags.h"
|
||||
#include "Link.h"
|
||||
#include "HTMLLinkElement.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::css;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -136,53 +135,6 @@ nsContentSink::~nsContentSink() {
|
|||
}
|
||||
}
|
||||
|
||||
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");
|
||||
|
@ -213,10 +165,10 @@ nsresult nsContentSink::Init(Document* aDoc, nsIURI* aURI,
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -1271,8 +1223,8 @@ nsContentSink::Notify(nsITimer* timer) {
|
|||
}
|
||||
|
||||
bool nsContentSink::IsTimeToNotify() {
|
||||
if (!sNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
|
||||
mInMonolithicContainer) {
|
||||
if (!StaticPrefs::content_notify_ontimer() || !mLayoutStarted ||
|
||||
!mBackoffCount || mInMonolithicContainer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1302,7 +1254,7 @@ nsresult nsContentSink::WillInterruptImpl() {
|
|||
#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();
|
||||
|
@ -1379,8 +1331,9 @@ nsresult nsContentSink::DidProcessATokenImpl() {
|
|||
++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;
|
||||
|
@ -1388,14 +1341,16 @@ nsresult nsContentSink::DidProcessATokenImpl() {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1522,7 +1477,7 @@ nsresult nsContentSink::WillParseImpl(void) {
|
|||
|
||||
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;
|
||||
|
@ -1530,8 +1485,10 @@ nsresult nsContentSink::WillParseImpl(void) {
|
|||
|
||||
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);
|
||||
|
@ -1543,8 +1500,9 @@ nsresult nsContentSink::WillParseImpl(void) {
|
|||
mHasPendingEvent = false;
|
||||
|
||||
mCurrentParseEndTime =
|
||||
currentTime +
|
||||
(mDynamicLowerValue ? sInteractiveParseTime : sPerfParseTime);
|
||||
currentTime + (mDynamicLowerValue
|
||||
? StaticPrefs::content_sink_interactive_parse_time()
|
||||
: StaticPrefs::content_sink_perf_parse_time());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/StaticPrefs_content.h"
|
||||
|
||||
class nsIURI;
|
||||
class nsIChannel;
|
||||
|
@ -117,8 +118,6 @@ class nsContentSink : public nsICSSLoaderObserver,
|
|||
bool LinkContextIsOurDocument(const nsAString& aAnchor);
|
||||
bool Decode5987Format(nsAString& aEncoded);
|
||||
|
||||
static void InitializeStatics();
|
||||
|
||||
protected:
|
||||
nsContentSink();
|
||||
virtual ~nsContentSink();
|
||||
|
@ -256,7 +255,7 @@ class nsContentSink : public nsICSSLoaderObserver,
|
|||
return 1000;
|
||||
}
|
||||
|
||||
return sNotificationInterval;
|
||||
return mozilla::StaticPrefs::content_notify_interval();
|
||||
}
|
||||
|
||||
virtual nsresult FlushTags() = 0;
|
||||
|
@ -267,7 +266,9 @@ class nsContentSink : public nsICSSLoaderObserver,
|
|||
|
||||
void DoProcessLinkHeader();
|
||||
|
||||
void StopDeflecting() { mDeflectedCount = sPerfDeflectCount; }
|
||||
void StopDeflecting() {
|
||||
mDeflectedCount = mozilla::StaticPrefs::content_sink_perf_deflect_count();
|
||||
}
|
||||
|
||||
protected:
|
||||
RefPtr<Document> mDocument;
|
||||
|
@ -335,31 +336,6 @@ class nsContentSink : public nsICSSLoaderObserver,
|
|||
|
||||
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_
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
#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"
|
||||
|
@ -1727,9 +1728,8 @@ nsCOMPtr<nsINode> nsHTMLCopyEncoder::GetChildAt(nsINode* aParent,
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
@ -3736,7 +3736,18 @@ double nsGlobalWindowOuter::GetDevicePixelRatioOuter(CallerType aCallerType) {
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -63,7 +63,6 @@ bool nsHTMLContentSerializer::SerializeHTMLAttributes(
|
|||
|
||||
nsresult rv;
|
||||
nsAutoString valueStr;
|
||||
NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
|
||||
|
||||
for (int32_t index = 0; index < count; index++) {
|
||||
const nsAttrName* name = aElement->GetAttrNameAt(index);
|
||||
|
@ -78,16 +77,6 @@ bool nsHTMLContentSerializer::SerializeHTMLAttributes(
|
|||
}
|
||||
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) {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#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"
|
||||
|
@ -1039,9 +1040,12 @@ nsresult nsPlainTextSerializer::DoAddLeaf(nsAtom* aTag) {
|
|||
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 &&
|
||||
|
|
|
@ -208,8 +208,6 @@ bool nsXHTMLContentSerializer::SerializeAttributes(
|
|||
PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
|
||||
}
|
||||
|
||||
NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
|
||||
|
||||
count = aElement->GetAttrCount();
|
||||
|
||||
// Now serialize each of the attributes
|
||||
|
@ -252,15 +250,6 @@ bool nsXHTMLContentSerializer::SerializeAttributes(
|
|||
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()
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#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"
|
||||
|
@ -483,20 +484,17 @@ nsresult ContentEventHandler::QueryContentRect(
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -1484,7 +1482,7 @@ ContentEventHandler::GetFirstFrameInRangeForTextRect(
|
|||
// 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);
|
||||
}
|
||||
}
|
||||
|
@ -1571,7 +1569,7 @@ ContentEventHandler::GetLastFrameInRangeForTextRect(const RawRange& aRawRange) {
|
|||
}
|
||||
|
||||
if (ShouldBreakLineBefore(node->AsContent(), mRootContent) ||
|
||||
IsMozBR(node->AsContent())) {
|
||||
IsPaddingBR(node->AsContent())) {
|
||||
nodePosition.Set(node, 0);
|
||||
break;
|
||||
}
|
||||
|
@ -1623,7 +1621,7 @@ ContentEventHandler::GetLineBreakerRectBefore(nsIFrame* aFrame) {
|
|||
// 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;
|
||||
|
||||
|
@ -1883,7 +1881,7 @@ nsresult ContentEventHandler::OnQueryTextRectArray(
|
|||
// 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.
|
||||
|
|
|
@ -246,9 +246,12 @@ 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)
|
||||
|
|
|
@ -186,7 +186,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(IMEContentObserver)
|
|||
IMEContentObserver::IMEContentObserver()
|
||||
: mESM(nullptr),
|
||||
mIMENotificationRequests(nullptr),
|
||||
mPreAttrChangeLength(0),
|
||||
mSuppressNotifications(0),
|
||||
mPreCharacterDataChangeLength(-1),
|
||||
mSendingNotification(NOTIFY_IME_OF_NOTHING),
|
||||
|
@ -1085,51 +1084,6 @@ void IMEContentObserver::ContentRemoved(nsIContent* aChild,
|
|||
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;
|
||||
|
|
|
@ -57,8 +57,6 @@ class IMEContentObserver final : public nsStubMutationObserver,
|
|||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
|
||||
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
|
||||
NS_DECL_NSIREFLOWOBSERVER
|
||||
|
||||
// nsIScrollObserver
|
||||
|
@ -484,7 +482,6 @@ class IMEContentObserver final : public nsStubMutationObserver,
|
|||
EventStateManager* mESM;
|
||||
|
||||
const IMENotificationRequests* mIMENotificationRequests;
|
||||
uint32_t mPreAttrChangeLength;
|
||||
uint32_t mSuppressNotifications;
|
||||
int64_t mPreCharacterDataChangeLength;
|
||||
|
||||
|
|
|
@ -14,10 +14,29 @@
|
|||
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,
|
||||
|
@ -38,6 +57,13 @@ class HTMLBRElement final : public nsGenericHTMLElement {
|
|||
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();
|
||||
|
||||
|
|
|
@ -22,3 +22,16 @@ partial interface HTMLBRElement {
|
|||
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;
|
||||
};
|
||||
|
|
|
@ -348,9 +348,10 @@ enum class EditAction {
|
|||
// 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
|
||||
|
@ -469,8 +470,9 @@ enum class EditSubAction : int32_t {
|
|||
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) {
|
||||
|
|
|
@ -1450,6 +1450,83 @@ nsresult EditorBase::InsertNodeWithTransaction(
|
|||
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)) {
|
||||
|
@ -2520,9 +2597,10 @@ EditorRawDOMPoint EditorBase::FindBetterInsertionPoint(
|
|||
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
|
||||
|
@ -2551,10 +2629,10 @@ EditorRawDOMPoint EditorBase::FindBetterInsertionPoint(
|
|||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
|
@ -2603,9 +2681,9 @@ nsresult EditorBase::InsertTextWithTransaction(
|
|||
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
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#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
|
||||
|
@ -90,6 +91,10 @@ class TextServicesDocument;
|
|||
class TypeInState;
|
||||
class WSRunObject;
|
||||
|
||||
template <typename NodeType>
|
||||
class CreateNodeResultBase;
|
||||
typedef CreateNodeResultBase<dom::Element> CreateElementResult;
|
||||
|
||||
namespace dom {
|
||||
class DataTransfer;
|
||||
class DragEvent;
|
||||
|
@ -101,9 +106,6 @@ namespace widget {
|
|||
struct IMEState;
|
||||
} // namespace widget
|
||||
|
||||
#define kMOZEditorBogusNodeAttrAtom nsGkAtoms::mozeditorbogusnode
|
||||
#define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
|
||||
|
||||
/**
|
||||
* SplitAtEdges is for EditorBase::SplitNodeDeepWithTransaction(),
|
||||
* HTMLEditor::InsertNodeAtPoint()
|
||||
|
@ -707,7 +709,7 @@ class EditorBase : public nsIEditor,
|
|||
case EditSubAction::eUndo:
|
||||
case EditSubAction::eRedo:
|
||||
case EditSubAction::eComputeTextToOutput:
|
||||
case EditSubAction::eCreateBogusNode:
|
||||
case EditSubAction::eCreatePaddingBRElementForEmptyEditor:
|
||||
case EditSubAction::eNone:
|
||||
MOZ_ASSERT(aDirection == eNone);
|
||||
mDirectionOfTopLevelEditSubAction = eNone;
|
||||
|
@ -966,6 +968,18 @@ class EditorBase : public nsIEditor,
|
|||
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,
|
||||
|
@ -1619,8 +1633,8 @@ class EditorBase : public nsIEditor,
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!aNode->IsContent() || IsMozEditorBogusNode(aNode) ||
|
||||
!IsModifiableNode(*aNode)) {
|
||||
if (!aNode->IsContent() || !IsModifiableNode(*aNode) ||
|
||||
EditorBase::IsPaddingBRElementForEmptyEditor(*aNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1638,26 +1652,34 @@ class EditorBase : public nsIEditor,
|
|||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2075,6 +2097,18 @@ class EditorBase : public nsIEditor,
|
|||
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;
|
||||
|
|
|
@ -528,7 +528,7 @@ nsresult HTMLEditor::SetPositionToAbsolute(Element& aElement) {
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -20,17 +20,18 @@
|
|||
#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"
|
||||
|
@ -69,9 +70,6 @@ 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 };
|
||||
|
||||
/********************************************************
|
||||
|
@ -455,7 +453,7 @@ nsresult HTMLEditRules::AfterEditInner(EditSubAction aEditSubAction,
|
|||
}
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to normalize Selection");
|
||||
if (aEditSubAction == EditSubAction::eReplaceHeadWithHTMLSource ||
|
||||
aEditSubAction == EditSubAction::eCreateBogusNode) {
|
||||
aEditSubAction == EditSubAction::eCreatePaddingBRElementForEmptyEditor) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -631,7 +629,10 @@ nsresult HTMLEditRules::AfterEditInner(EditSubAction aEditSubAction,
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -804,7 +805,9 @@ nsresult HTMLEditRules::DidDoAction(EditSubActionInfo& aInfo,
|
|||
}
|
||||
}
|
||||
|
||||
bool HTMLEditRules::DocumentIsEmpty() { return !!mBogusNode; }
|
||||
bool HTMLEditRules::DocumentIsEmpty() const {
|
||||
return !!mPaddingBRElementForEmptyEditor;
|
||||
}
|
||||
|
||||
nsresult HTMLEditRules::GetListState(bool* aMixed, bool* aOL, bool* aUL,
|
||||
bool* aDL) {
|
||||
|
@ -1237,16 +1240,17 @@ nsresult HTMLEditRules::WillInsert(bool* aCancel) {
|
|||
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;
|
||||
|
@ -1261,15 +1265,16 @@ nsresult HTMLEditRules::WillInsert(bool* aCancel) {
|
|||
// 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);
|
||||
|
@ -1474,7 +1479,7 @@ nsresult HTMLEditRules::WillInsertText(EditSubAction aEditSubAction,
|
|||
// 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;
|
||||
|
@ -1641,19 +1646,21 @@ nsresult HTMLEditRules::WillInsertText(EditSubAction aEditSubAction,
|
|||
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;
|
||||
|
@ -1849,7 +1856,7 @@ EditActionResult HTMLEditRules::WillInsertParagraphSeparator() {
|
|||
endOfBlockParent.SetToEndOf(blockParent);
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(endOfBlockParent);
|
||||
.InsertBRElementWithTransaction(endOfBlockParent);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
|
@ -1937,7 +1944,7 @@ nsresult HTMLEditRules::InsertBRElement(const EditorDOMPoint& aPointToBreak) {
|
|||
RefPtr<Element> brElement;
|
||||
if (IsPlaintextEditor()) {
|
||||
brElement = MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(aPointToBreak);
|
||||
.InsertBRElementWithTransaction(aPointToBreak);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -2144,7 +2151,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
|||
endOfPreviousNodeOfSplitPoint.SetToEndOf(previousNodeOfSplitPoint);
|
||||
RefPtr<Element> invisibleBrElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(endOfPreviousNodeOfSplitPoint);
|
||||
.InsertBRElementWithTransaction(endOfPreviousNodeOfSplitPoint);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return EditActionIgnored(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
|
@ -2159,7 +2166,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
|||
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);
|
||||
}
|
||||
|
@ -2210,7 +2217,7 @@ EditActionResult HTMLEditRules::SplitMailCites() {
|
|||
// 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);
|
||||
}
|
||||
|
@ -2283,8 +2290,9 @@ nsresult HTMLEditRules::WillDeleteSelection(
|
|||
// 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;
|
||||
}
|
||||
|
@ -3239,7 +3247,7 @@ nsresult HTMLEditRules::InsertBRIfNeeded() {
|
|||
*nsGkAtoms::br)) {
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(atStartOfSelection,
|
||||
.InsertBRElementWithTransaction(atStartOfSelection,
|
||||
nsIEditor::ePrevious);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
|
@ -3862,7 +3870,7 @@ nsresult HTMLEditRules::DidDeleteSelection() {
|
|||
if (atCiteNode.IsSet() && seenBR) {
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(atCiteNode);
|
||||
.InsertBRElementWithTransaction(atCiteNode);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -4526,7 +4534,7 @@ nsresult HTMLEditRules::MakeBasicBlock(nsAtom& blockType) {
|
|||
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;
|
||||
}
|
||||
|
@ -4645,8 +4653,8 @@ nsresult HTMLEditRules::DidMakeBasicBlock() {
|
|||
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;
|
||||
}
|
||||
|
@ -6138,10 +6146,14 @@ nsresult HTMLEditRules::AlignContentsAtSelection(const nsAString& aAlignType) {
|
|||
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
|
||||
|
@ -6440,7 +6452,7 @@ nsresult HTMLEditRules::MaybeDeleteTopMostEmptyAncestor(
|
|||
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;
|
||||
}
|
||||
|
@ -7820,7 +7832,8 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
|||
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));
|
||||
|
@ -7830,10 +7843,12 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
|||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7877,7 +7892,7 @@ nsresult HTMLEditRules::ReturnInHeader(Element& aHeader, nsINode& aNode,
|
|||
// 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;
|
||||
}
|
||||
|
@ -8024,7 +8039,7 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
|
|||
brContent = HTMLEditorRef().GetPriorHTMLSibling(
|
||||
atStartOfSelection.GetContainer());
|
||||
if (!brContent || !HTMLEditorRef().IsVisibleBRElement(brContent) ||
|
||||
TextEditUtils::HasMozAttr(brContent)) {
|
||||
EditorBase::IsPaddingBRElementForEmptyLastLine(*brContent)) {
|
||||
pointToInsertBR.Set(atStartOfSelection.GetContainer());
|
||||
brContent = nullptr;
|
||||
}
|
||||
|
@ -8034,7 +8049,7 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
|
|||
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,
|
||||
|
@ -8072,11 +8087,11 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
|
|||
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;
|
||||
}
|
||||
|
@ -8092,7 +8107,7 @@ EditActionResult HTMLEditRules::ReturnInParagraph(Element& aParentDivOrP) {
|
|||
}
|
||||
|
||||
brContent = MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(pointToInsertBR);
|
||||
.InsertBRElementWithTransaction(pointToInsertBR);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return EditActionResult(NS_ERROR_EDITOR_DESTROYED);
|
||||
}
|
||||
|
@ -8175,11 +8190,11 @@ nsresult HTMLEditRules::SplitParagraph(
|
|||
}
|
||||
|
||||
// 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;
|
||||
|
@ -8300,7 +8315,7 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
|||
// 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;
|
||||
}
|
||||
|
@ -8361,10 +8376,12 @@ nsresult HTMLEditRules::ReturnInListItem(Element& aListItem, nsINode& aNode,
|
|||
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);
|
||||
|
@ -9243,7 +9260,7 @@ HTMLEditRules::InsertBRElementToEmptyListItemsAndTableCellsInChangedRange() {
|
|||
}
|
||||
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
|
||||
|
@ -9251,11 +9268,11 @@ HTMLEditRules::InsertBRElementToEmptyListItemsAndTableCellsInChangedRange() {
|
|||
// 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;
|
||||
|
@ -9455,14 +9472,16 @@ nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
|
|||
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;
|
||||
}
|
||||
|
@ -9473,7 +9492,8 @@ nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
|
|||
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
|
||||
|
@ -9490,12 +9510,15 @@ nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
|
|||
// 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())) {
|
||||
|
@ -9516,9 +9539,11 @@ nsresult HTMLEditRules::AdjustSelection(nsIEditor::EDirection aAction) {
|
|||
} 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(),
|
||||
|
@ -9767,7 +9792,7 @@ nsresult HTMLEditRules::RemoveEmptyNodesInChangedRange() {
|
|||
// 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;
|
||||
}
|
||||
|
@ -10151,7 +10176,7 @@ nsresult HTMLEditRules::UpdateDocChangeRange(nsRange* aRange) {
|
|||
}
|
||||
|
||||
nsresult HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
|
||||
bool aInsertMozBR) {
|
||||
bool aForPadding) {
|
||||
MOZ_ASSERT(IsEditorDataAvailable());
|
||||
|
||||
if (!IsBlockNode(aNode)) {
|
||||
|
@ -10167,13 +10192,24 @@ nsresult HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
CreateElementResult createBrResult =
|
||||
!aInsertMozBR ? CreateBR(EditorDOMPoint(&aNode, 0))
|
||||
: CreateMozBR(EditorDOMPoint(&aNode, 0));
|
||||
if (NS_WARN_IF(createBrResult.Failed())) {
|
||||
return createBrResult.Rv();
|
||||
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();
|
||||
}
|
||||
return NS_OK;
|
||||
|
||||
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) {
|
||||
|
@ -10493,7 +10529,7 @@ nsresult HTMLEditRules::MakeSureElemStartsOrEndsOnCR(nsINode& aNode,
|
|||
}
|
||||
RefPtr<Element> brElement =
|
||||
MOZ_KnownLive(HTMLEditorRef())
|
||||
.InsertBrElementWithTransaction(pointToInsert);
|
||||
.InsertBRElementWithTransaction(pointToInsert);
|
||||
if (NS_WARN_IF(!CanHandleEditAction())) {
|
||||
return NS_ERROR_EDITOR_DESTROYED;
|
||||
}
|
||||
|
@ -11095,22 +11131,25 @@ void HTMLEditRules::OnModifyDocument() {
|
|||
// 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");
|
||||
// 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 bogus node if needed.
|
||||
DebugOnly<nsresult> rv = CreateBogusNodeIfNeeded();
|
||||
// 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
|
||||
|
|
|
@ -94,7 +94,7 @@ class HTMLEditRules : public TextEditRules {
|
|||
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.
|
||||
|
@ -193,7 +193,7 @@ class HTMLEditRules : public TextEditRules {
|
|||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
|
@ -277,26 +277,28 @@ class HTMLEditRules : public TextEditRules {
|
|||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -536,8 +538,8 @@ class HTMLEditRules : public TextEditRules {
|
|||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
|
|
|
@ -213,7 +213,9 @@ bool HTMLEditUtils::IsNamedAnchor(nsINode* aNode) {
|
|||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#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"
|
||||
|
@ -1186,10 +1187,10 @@ nsresult HTMLEditor::InsertBrElementAtSelectionWithTransaction() {
|
|||
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;
|
||||
}
|
||||
|
@ -1644,7 +1645,7 @@ nsresult HTMLEditor::InsertElementAtSelectionAsAction(
|
|||
"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;
|
||||
}
|
||||
|
@ -3403,7 +3404,7 @@ nsresult HTMLEditor::DeleteNodeWithTransaction(nsINode& aNode) {
|
|||
// 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);
|
||||
|
@ -3565,6 +3566,67 @@ nsresult HTMLEditor::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);
|
||||
}
|
||||
|
@ -3616,8 +3678,8 @@ void HTMLEditor::DoContentInserted(nsIContent* aChild,
|
|||
}
|
||||
// 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();
|
||||
|
@ -3662,8 +3724,8 @@ void HTMLEditor::ContentRemoved(nsIContent* aChild,
|
|||
// 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;
|
||||
}
|
||||
|
||||
|
@ -3751,7 +3813,7 @@ nsresult HTMLEditor::SelectEntireDocument() {
|
|||
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(
|
||||
|
@ -3980,7 +4042,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
|||
!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;
|
||||
}
|
||||
|
@ -4000,7 +4062,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
|||
// 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;
|
||||
}
|
||||
|
@ -4021,7 +4083,7 @@ nsresult HTMLEditor::RemoveBlockContainerWithTransaction(Element& aElement) {
|
|||
!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;
|
||||
}
|
||||
|
@ -4745,7 +4807,7 @@ nsresult HTMLEditor::CopyLastEditableChildStylesWithTransaction(
|
|||
}
|
||||
|
||||
RefPtr<Element> brElement =
|
||||
InsertBrElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
|
||||
InsertBRElementWithTransaction(EditorDOMPoint(firstClonsedElement, 0));
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -5252,7 +5314,8 @@ void HTMLEditor::OnModifyDocument() {
|
|||
return;
|
||||
}
|
||||
|
||||
AutoEditActionDataSetter editActionData(*this, EditAction::eCreateBogusNode);
|
||||
AutoEditActionDataSetter editActionData(
|
||||
*this, EditAction::eCreatePaddingBRElementForEmptyEditor);
|
||||
if (NS_WARN_IF(!editActionData.CanHandle())) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -587,6 +587,24 @@ class HTMLEditor final : public TextEditor,
|
|||
* 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.
|
||||
|
@ -841,11 +859,11 @@ class HTMLEditor final : public TextEditor,
|
|||
* 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) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#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"
|
||||
|
@ -62,7 +63,7 @@ using namespace dom;
|
|||
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;
|
||||
|
@ -74,7 +75,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TextEditRules)
|
|||
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;
|
||||
|
@ -103,7 +104,7 @@ TextEditRules::TextEditRules()
|
|||
|
||||
void TextEditRules::InitFields() {
|
||||
mTextEditor = nullptr;
|
||||
mBogusNode = nullptr;
|
||||
mPaddingBRElementForEmptyEditor = nullptr;
|
||||
mCachedSelectionNode = nullptr;
|
||||
mCachedSelectionOffset = 0;
|
||||
mActionNesting = 0;
|
||||
|
@ -139,7 +140,7 @@ nsresult TextEditRules::Init(TextEditor* aTextEditor) {
|
|||
|
||||
// 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;
|
||||
}
|
||||
|
@ -243,7 +244,7 @@ nsresult TextEditRules::AfterEdit(EditSubAction aEditSubAction,
|
|||
}
|
||||
|
||||
// detect empty doc
|
||||
rv = CreateBogusNodeIfNeeded();
|
||||
rv = CreatePaddingBRElementForEmptyEditorIfNeeded();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -254,8 +255,8 @@ nsresult TextEditRules::AfterEdit(EditSubAction aEditSubAction,
|
|||
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;
|
||||
|
@ -349,7 +350,7 @@ nsresult TextEditRules::DidDoAction(EditSubActionInfo& aInfo,
|
|||
}
|
||||
}
|
||||
|
||||
bool TextEditRules::DocumentIsEmpty() {
|
||||
bool TextEditRules::DocumentIsEmpty() const {
|
||||
bool retVal = false;
|
||||
if (!mTextEditor || NS_FAILED(mTextEditor->IsEmpty(&retVal))) {
|
||||
retVal = true;
|
||||
|
@ -378,19 +379,22 @@ nsresult TextEditRules::WillInsert(bool* aCancel) {
|
|||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -498,8 +502,9 @@ 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;
|
||||
}
|
||||
|
@ -515,7 +520,8 @@ nsresult TextEditRules::CollapseSelectionToTrailingBRIfNeeded() {
|
|||
}
|
||||
|
||||
// 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())) {
|
||||
|
@ -538,7 +544,7 @@ nsresult TextEditRules::CollapseSelectionToTrailingBRIfNeeded() {
|
|||
}
|
||||
|
||||
nsINode* nextNode = selectionStartPoint.GetContainer()->GetNextSibling();
|
||||
if (!nextNode || !TextEditUtils::IsMozBR(nextNode)) {
|
||||
if (!nextNode || !EditorBase::IsPaddingBRElementForEmptyLastLine(*nextNode)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -886,28 +892,29 @@ nsresult TextEditRules::WillSetText(bool* aCancel, bool* aHandled,
|
|||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -1011,8 +1018,9 @@ nsresult TextEditRules::WillDeleteSelection(
|
|||
*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;
|
||||
}
|
||||
|
@ -1151,10 +1159,10 @@ nsresult TextEditRules::DidUndo(nsresult aResult) {
|
|||
// 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;
|
||||
}
|
||||
|
@ -1188,16 +1196,16 @@ nsresult TextEditRules::DidRedo(nsresult aResult) {
|
|||
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;
|
||||
}
|
||||
|
@ -1221,8 +1229,9 @@ nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
|||
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;
|
||||
|
@ -1279,7 +1288,9 @@ nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
|||
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;
|
||||
}
|
||||
|
@ -1302,8 +1313,8 @@ nsresult TextEditRules::WillOutputText(const nsAString* aOutputFormat,
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1317,35 +1328,24 @@ nsresult TextEditRules::RemoveRedundantTrailingBR() {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -1362,73 +1362,76 @@ nsresult TextEditRules::CreateTrailingBRIfNeeded() {
|
|||
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;
|
||||
}
|
||||
|
@ -1443,15 +1446,11 @@ nsresult TextEditRules::CreateBogusNodeIfNeeded() {
|
|||
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())
|
||||
|
@ -1557,44 +1556,6 @@ nsresult TextEditRules::TruncateInsertionIfNeeded(const nsAString* aInString,
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ class EditSubActionInfo;
|
|||
class HTMLEditor;
|
||||
class HTMLEditRules;
|
||||
namespace dom {
|
||||
class HTMLBRElement;
|
||||
class Selection;
|
||||
} // namespace dom
|
||||
|
||||
|
@ -100,7 +101,7 @@ class TextEditRules {
|
|||
* nodes. Otherwise, i.e., there is no meaningful content,
|
||||
* return true.
|
||||
*/
|
||||
virtual bool DocumentIsEmpty();
|
||||
virtual bool DocumentIsEmpty() const;
|
||||
|
||||
bool DontEchoPassword() const;
|
||||
|
||||
|
@ -130,7 +131,9 @@ class TextEditRules {
|
|||
*/
|
||||
void HandleNewLines(nsString& aString);
|
||||
|
||||
bool HasBogusNode() { return !!mBogusNode; }
|
||||
bool HasPaddingBRElementForEmptyEditor() const {
|
||||
return !!mPaddingBRElementForEmptyEditor;
|
||||
}
|
||||
|
||||
protected:
|
||||
void InitFields();
|
||||
|
@ -263,9 +266,11 @@ class TextEditRules {
|
|||
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
|
||||
|
@ -275,46 +280,6 @@ class TextEditRules {
|
|||
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,
|
||||
|
@ -326,7 +291,8 @@ class TextEditRules {
|
|||
* 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();
|
||||
|
||||
|
@ -341,20 +307,6 @@ class TextEditRules {
|
|||
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
|
||||
|
@ -436,8 +388,9 @@ class TextEditRules {
|
|||
*/
|
||||
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.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#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"
|
||||
|
@ -20,6 +21,8 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace dom;
|
||||
|
||||
/******************************************************************************
|
||||
* TextEditUtils
|
||||
******************************************************************************/
|
||||
|
@ -40,28 +43,6 @@ bool TextEditUtils::IsBreak(nsINode* 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
|
||||
******************************************************************************/
|
||||
|
|
|
@ -19,8 +19,6 @@ class TextEditUtils final {
|
|||
// from TextEditRules:
|
||||
static bool IsBody(nsINode* aNode);
|
||||
static bool IsBreak(nsINode* aNode);
|
||||
static bool IsMozBR(nsINode* aNode);
|
||||
static bool HasMozAttr(nsINode* aNode);
|
||||
};
|
||||
|
||||
/***************************************************************************
|
||||
|
|
|
@ -440,105 +440,6 @@ nsresult TextEditor::InsertLineBreakAsAction(nsIPrincipal* aPrincipal) {
|
|||
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());
|
||||
|
||||
|
@ -1441,12 +1342,13 @@ nsresult TextEditor::IsEmpty(bool* aIsEmpty) const {
|
|||
|
||||
*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??
|
||||
|
@ -1481,7 +1383,8 @@ TextEditor::GetTextLength(int32_t* 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))) {
|
||||
|
@ -2163,7 +2066,7 @@ nsresult TextEditor::SelectEntireDocument() {
|
|||
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(
|
||||
|
@ -2183,14 +2086,14 @@ nsresult TextEditor::SelectEntireDocument() {
|
|||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -167,9 +167,9 @@ class TextEditor : public EditorBase,
|
|||
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 {
|
||||
|
@ -466,24 +466,6 @@ class TextEditor : public EditorBase,
|
|||
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
|
||||
|
|
|
@ -237,7 +237,7 @@ already_AddRefed<Element> WSRunObject::InsertBreak(
|
|||
|
||||
RefPtr<Element> newBrElement =
|
||||
MOZ_KnownLive(mHTMLEditor)
|
||||
->InsertBrElementWithTransaction(pointToInsert, aSelect);
|
||||
->InsertBRElementWithTransaction(pointToInsert, aSelect);
|
||||
if (NS_WARN_IF(!newBrElement)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -1845,7 +1845,7 @@ nsresult WSRunObject::CheckTrailingNBSPOfRun(WSFragment* aRun) {
|
|||
// they type 2 spaces.
|
||||
|
||||
RefPtr<Element> brElement =
|
||||
htmlEditor->InsertBrElementWithTransaction(aRun->EndPoint());
|
||||
htmlEditor->InsertBRElementWithTransaction(aRun->EndPoint());
|
||||
if (NS_WARN_IF(!brElement)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -57,7 +57,8 @@ SimpleTest.waitForFocus(() => {
|
|||
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(),
|
||||
|
|
|
@ -18,10 +18,10 @@ https://bugzilla.mozilla.org/show_bug.cgi=id=1385905
|
|||
<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");
|
||||
|
@ -35,15 +35,15 @@ SimpleTest.waitForFocus(() => {
|
|||
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>
|
||||
|
|
|
@ -30,42 +30,42 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=471319
|
|||
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>
|
||||
|
|
|
@ -41,8 +41,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=471722
|
|||
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 = {};
|
||||
|
@ -53,18 +53,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=471722
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -69,7 +69,8 @@ SimpleTest.waitForFocus(function() {
|
|||
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});
|
||||
|
|
|
@ -34,7 +34,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=740784
|
|||
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();
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <map>
|
||||
|
||||
#include "base/file_path.h"
|
||||
#include "base/process.h"
|
||||
#include "base/string_util.h"
|
||||
#include "base/string16.h"
|
||||
#include "base/time.h"
|
||||
|
|
|
@ -22,6 +22,8 @@ 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):
|
||||
|
|
|
@ -3909,6 +3909,19 @@ bool js::IsPromiseForAsync(JSObject* promise) {
|
|||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -242,10 +242,8 @@ bool BytecodeEmitter::emitCheck(JSOp op, ptrdiff_t delta,
|
|||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
|
@ -22,6 +22,7 @@
|
|||
#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"
|
||||
|
@ -1134,7 +1135,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
|
|||
} 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);
|
||||
|
@ -1326,7 +1327,7 @@ static bool InitFromBailout(JSContext* cx, size_t frameNo, HandleFunction fun,
|
|||
|
||||
// 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)) {
|
||||
|
@ -1869,7 +1870,7 @@ bool jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfoArg) {
|
|||
|
||||
// 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());
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#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"
|
||||
|
@ -224,7 +225,7 @@ bool JitScript::initICEntriesAndBytecodeTypeMap(JSContext* cx,
|
|||
|
||||
// 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++;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#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"
|
||||
|
@ -654,7 +655,7 @@ AbortReasonOr<Ok> IonBuilder::analyzeNewLoopTypes(
|
|||
last = earlier;
|
||||
}
|
||||
|
||||
if (CodeSpec[*last].format & JOF_TYPESET) {
|
||||
if (BytecodeOpHasTypeSet(JSOp(*last))) {
|
||||
TemporaryTypeSet* typeSet = bytecodeTypes(last);
|
||||
if (!typeSet->empty()) {
|
||||
MIRType type = typeSet->getKnownMIRType();
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "mozilla/BinarySearch.h"
|
||||
|
||||
#include "vm/BytecodeUtil.h"
|
||||
#include "vm/JSScript.h"
|
||||
#include "vm/TypeInference.h"
|
||||
|
||||
|
@ -42,7 +43,7 @@ template <typename TYPESET>
|
|||
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.
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "jit/BaselineIC.h"
|
||||
#include "jit/BytecodeAnalysis.h"
|
||||
#include "vm/BytecodeUtil.h"
|
||||
#include "vm/JSScript.h"
|
||||
#include "vm/Stack.h"
|
||||
#include "vm/TypeInference.h"
|
||||
|
@ -258,7 +259,7 @@ void JitScript::printTypes(JSContext* cx, HandleScript script) {
|
|||
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();
|
||||
|
|
|
@ -644,6 +644,9 @@ MSG_DEF(JSMSG_PROMISE_ERROR_IN_WRAPPED_REJECTION_REASON,0, JSEXN_INTERNALERR, "P
|
|||
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")
|
||||
|
|
|
@ -11254,8 +11254,8 @@ int main(int argc, char** argv, char** envp) {
|
|||
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;
|
||||
|
|
|
@ -633,6 +633,10 @@ static inline int32_t GetBytecodeInteger(jsbytecode* pc) {
|
|||
|
||||
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
|
||||
|
|
|
@ -71,7 +71,7 @@ bool js::CreateHelperThreadsState() {
|
|||
return false;
|
||||
}
|
||||
gHelperThreadState = helperThreadState.release();
|
||||
if (!gHelperThreadState->initializeHelperContexts()) {
|
||||
if (!gHelperThreadState->ensureContextListForThreadCount()) {
|
||||
js_delete(gHelperThreadState);
|
||||
return false;
|
||||
}
|
||||
|
@ -106,12 +106,17 @@ static size_t ThreadCountForCPUCount(size_t cpuCount) {
|
|||
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(
|
||||
|
@ -1202,9 +1207,16 @@ void GlobalHelperThreadState::finishThreads() {
|
|||
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
|
||||
|
|
|
@ -171,7 +171,7 @@ class GlobalHelperThreadState {
|
|||
void finish();
|
||||
void finishThreads();
|
||||
|
||||
MOZ_MUST_USE bool initializeHelperContexts();
|
||||
MOZ_MUST_USE bool ensureContextListForThreadCount();
|
||||
JSContext* getFirstUnusedContext(AutoLockHelperThreadState& locked);
|
||||
void destroyHelperContexts(AutoLockHelperThreadState& lock);
|
||||
|
||||
|
@ -492,7 +492,7 @@ 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();
|
||||
|
|
|
@ -3444,7 +3444,7 @@ void JitScript::MonitorBytecodeTypeSlow(JSContext* cx, JSScript* script,
|
|||
/* 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;
|
||||
|
|
|
@ -10803,6 +10803,9 @@ void nsCSSFrameConstructor::FinishBuildingColumns(
|
|||
|
||||
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
|
||||
|
|
|
@ -245,7 +245,6 @@ nsresult nsLayoutStatics::Initialize() {
|
|||
MediaManager::StartupInit();
|
||||
CubebUtils::InitLibrary();
|
||||
|
||||
nsContentSink::InitializeStatics();
|
||||
nsHtml5Module::InitializeStatics();
|
||||
mozilla::dom::FallbackEncoding::Initialize();
|
||||
nsLayoutUtils::Initialize();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
/* rendering object for HTML <br> elements */
|
||||
|
||||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/dom/HTMLBRElement.h"
|
||||
#include "gfxContext.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContainerFrame.h"
|
||||
|
@ -247,11 +248,10 @@ nsIFrame::FrameSearchResult BRFrame::PeekOffsetWord(
|
|||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
|
|
@ -3575,6 +3575,8 @@ void nsBlockFrame::ReflowBlockFrame(BlockReflowInput& aState,
|
|||
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 =
|
||||
|
|
|
@ -917,37 +917,71 @@ nsColumnSetFrame::ColumnBalanceData nsColumnSetFrame::ReflowChildren(
|
|||
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();
|
||||
|
|
|
@ -49,7 +49,7 @@ pref(layout.css.column-span.enabled,true) == first-line-first-letter.html first-
|
|||
# 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
|
||||
|
|
|
@ -260,8 +260,7 @@ 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
|
||||
|
|
|
@ -125,7 +125,7 @@ fuzzy-if(skiaContent,0-64,0-2) == css-multicol/multicol-reduce-000.xht css-multi
|
|||
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
|
||||
|
|
|
@ -137,22 +137,14 @@ class InfallibleAllocPolicy {
|
|||
|
||||
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) {
|
||||
|
@ -162,11 +154,6 @@ class StackTrace : public phc::StackTrace {
|
|||
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
|
||||
|
@ -190,7 +177,6 @@ class StackTrace : public phc::StackTrace {
|
|||
//
|
||||
void StackTrace::Fill() {
|
||||
mLength = 0;
|
||||
mSkipped = false;
|
||||
|
||||
#if defined(XP_WIN) && defined(_M_IX86)
|
||||
// This avoids MozStackWalk(), which causes unusably slow startup on Win32
|
||||
|
@ -558,16 +544,16 @@ class GMut {
|
|||
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.
|
||||
|
@ -620,8 +606,8 @@ class GMut {
|
|||
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;
|
||||
}
|
||||
|
||||
|
@ -641,7 +627,7 @@ class GMut {
|
|||
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.
|
||||
};
|
||||
|
@ -667,7 +653,7 @@ class GMut {
|
|||
|
||||
// page.mAllocStack is left unchanged, for reporting on UAF.
|
||||
|
||||
page.mFreeStack = aFreeStack;
|
||||
page.mFreeStack = Some(aFreeStack);
|
||||
page.mReuseTime = GAtomic::Now() + aReuseDelay;
|
||||
}
|
||||
|
||||
|
@ -776,8 +762,8 @@ class GMut {
|
|||
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);
|
||||
}
|
||||
|
||||
|
@ -789,8 +775,8 @@ class GMut {
|
|||
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
|
||||
}
|
||||
|
@ -1023,8 +1009,7 @@ MOZ_ALWAYS_INLINE static void* PageRealloc(const Maybe<arena_id_t>& aArenaId,
|
|||
// 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();
|
||||
|
@ -1097,8 +1082,7 @@ MOZ_ALWAYS_INLINE static void PageFree(const Maybe<arena_id_t>& aArenaId,
|
|||
// 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();
|
||||
|
@ -1153,7 +1137,7 @@ static size_t replace_malloc_usable_size(usable_ptr_t 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);
|
||||
}
|
||||
|
|
|
@ -8,13 +8,16 @@
|
|||
#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:
|
||||
|
@ -70,8 +73,8 @@ class AddrInfo {
|
|||
|
||||
// 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()
|
||||
|
|
|
@ -16,10 +16,10 @@ bool PHCInfoEq(phc::AddrInfo& aInfo, phc::AddrInfo::Kind aKind, void* aBaseAddr,
|
|||
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,
|
||||
|
@ -70,7 +70,7 @@ TEST(PHC, TestPHCBasics)
|
|||
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));
|
||||
|
||||
|
@ -78,7 +78,7 @@ TEST(PHC, TestPHCBasics)
|
|||
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));
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
# 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
|
||||
# -----------
|
||||
|
@ -974,6 +974,99 @@
|
|||
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."
|
||||
#---------------------------------------------------------------------------
|
||||
|
|
|
@ -1117,11 +1117,6 @@ pref("dom.use_components_shim", false);
|
|||
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
|
||||
|
|
|
@ -32,6 +32,7 @@ pref_groups = [
|
|||
'canvas',
|
||||
'channelclassifier',
|
||||
'clipboard',
|
||||
'content',
|
||||
'device',
|
||||
'devtools',
|
||||
'dom',
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#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"
|
||||
|
@ -315,9 +316,10 @@ void nsHtml5TreeOpExecutor::ContinueInterruptedParsingAsync() {
|
|||
&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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[multicol-breaking-nobackground-000.html]
|
||||
prefs: [layout.css.column-span.enabled:true]
|
|
@ -0,0 +1,2 @@
|
|||
[multicol-breaking-nobackground-001.html]
|
||||
prefs: [layout.css.column-span.enabled:true]
|
|
@ -0,0 +1,2 @@
|
|||
[multicol-rule-nested-balancing-001.html]
|
||||
prefs: [layout.css.column-span.enabled:true]
|
|
@ -0,0 +1,2 @@
|
|||
[multicol-rule-nested-balancing-002.html]
|
||||
prefs: [layout.css.column-span.enabled:true]
|
|
@ -0,0 +1,2 @@
|
|||
[multicol-span-all-rule-001.html]
|
||||
prefs: [layout.css.column-span.enabled:true]
|
|
@ -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
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
<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
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<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>
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
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>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<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>
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
</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>
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<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>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</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
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<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>
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
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>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<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>
|
||||
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -929,11 +929,17 @@ LoginManagerPrompter.prototype = {
|
|||
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);
|
||||
|
@ -949,8 +955,19 @@ LoginManagerPrompter.prototype = {
|
|||
* @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;
|
||||
|
@ -975,6 +992,10 @@ LoginManagerPrompter.prototype = {
|
|||
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);
|
||||
|
@ -1293,11 +1314,11 @@ LoginManagerPrompter.prototype = {
|
|||
return false;
|
||||
},
|
||||
},
|
||||
options
|
||||
showOptions
|
||||
)
|
||||
);
|
||||
|
||||
if (options.notifySaved) {
|
||||
if (notifySaved) {
|
||||
let notification = popupNote.getNotification(notificationID);
|
||||
let anchor = notification.anchorElement;
|
||||
anchor.ownerGlobal.ConfirmationHint.show(anchor, "passwordSaved");
|
||||
|
@ -1437,11 +1458,31 @@ LoginManagerPrompter.prototype = {
|
|||
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(
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче