From 55b64360f6b4e1286e8f39bb9c6191e800899f3f Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 9 Sep 2020 17:02:45 +0000 Subject: [PATCH 01/26] Bug 1662961 - Replace MOZ_MUST_USE with [[nodiscard]] in security/sandbox/linux. r=jld The MOZ_MUST_USE macro is defined as clang's and gcc's nonstandard __attribute__((warn_unused_result)). Now that we compile as C++17 by default (bug 1560664), we can replace MOZ_MUST_USE with C++17's standard [[nodiscard]] attribute. The [[nodiscard]] attribute must precede a function declaration's declaration specifiers (like static, extern, inline, or virtual). The __attribute__((warn_unused_result)) attribute does not have this order restriction. Differential Revision: https://phabricator.services.mozilla.com/D89235 --- security/sandbox/linux/Sandbox.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/security/sandbox/linux/Sandbox.cpp b/security/sandbox/linux/Sandbox.cpp index a60435905395..772c6b806a20 100644 --- a/security/sandbox/linux/Sandbox.cpp +++ b/security/sandbox/linux/Sandbox.cpp @@ -212,8 +212,8 @@ static void InstallSigSysHandler(void) { * @see SandboxInfo * @see BroadcastSetThreadSandbox */ -static bool MOZ_MUST_USE InstallSyscallFilter(const sock_fprog* aProg, - bool aUseTSync) { +[[nodiscard]] static bool InstallSyscallFilter(const sock_fprog* aProg, + bool aUseTSync) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { if (!aUseTSync && errno == ETXTBSY) { return false; From 35d27dc9bdc1a225a5163ba8b95a7e8349f7dc68 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 9 Sep 2020 17:23:34 +0000 Subject: [PATCH 02/26] Bug 1663217 - Remove MOZ_MUST_USE comment from NSIS BitsUtils.cpp. r=agashlin The MOZ_MUST_USE macro is defined as clang's and gcc's nonstandard __attribute__((warn_unused_result)). Now that we compile as C++17 by default (bug 1560664), we can replace MOZ_MUST_USE with C++17's standard [[nodiscard]] attribute. BitsUtils.cpp only references MOZ_MUST_USE in a comment about forking a copy of mozilla::ScopeExit to remove some dependencies on other Mozilla header files (including MOZ_MUST_USE from mfbt/Attributes.h). [[nodiscard]] doesn't require a header file, so we can just remove this comment about MOZ_MUST_USE. Differential Revision: https://phabricator.services.mozilla.com/D89296 --- other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp index e9b58e22a2ae..aea1f270eaf9 100644 --- a/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp +++ b/other-licenses/nsis/Contrib/BitsUtils/BitsUtils.cpp @@ -16,7 +16,7 @@ static HINSTANCE gHInst; // ***** Section: ScopeExit // Derived from mfbt mozilla::ScopeExit, I have removed the use of -// GuardObjectNotifier and annotations MOZ_STACK_CLASS and MOZ_MUST_USE. +// GuardObjectNotifier and the MOZ_* annotations. template class ScopeExit { ExitFunction mExitFunction; @@ -314,4 +314,4 @@ BOOL APIENTRY DllMain(HINSTANCE instance, DWORD reason, LPVOID) { InitializeCriticalSection(&gStartBitsThread.cs); } return TRUE; -} \ No newline at end of file +} From 3c3839d4b3ba6062e60136d74922b015f266b03a Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 10 Sep 2020 03:44:30 +0000 Subject: [PATCH 03/26] Bug 1664083 - Remove support for external source directories. r=nalexander It was there for comm-central, and hasn't been used since bug 1479904. Differential Revision: https://phabricator.services.mozilla.com/D89691 --- build/moz.configure/init.configure | 28 +++---------------- old-configure.in | 2 -- .../mozbuild/backend/configenvironment.py | 8 ------ python/mozbuild/mozbuild/frontend/context.py | 5 ---- python/mozbuild/mozbuild/frontend/reader.py | 16 ----------- python/mozbuild/mozbuild/test/common.py | 1 - .../mozbuild/test/frontend/test_context.py | 1 - 7 files changed, 4 insertions(+), 57 deletions(-) diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure index 7f3d2b7472bf..970ef849e5e9 100644 --- a/build/moz.configure/init.configure +++ b/build/moz.configure/init.configure @@ -135,18 +135,6 @@ option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script') option(env='MOZCONFIG', nargs=1, help='Mozconfig location') -option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1, - help='External directory containing additional build files') - - -@depends('--with-external-source-dir') -def external_source_dir(value): - if value: - return value[0] - - -set_config('EXTERNAL_SOURCE_DIR', external_source_dir) -add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir) # Read user mozconfig # ============================================================== @@ -155,13 +143,11 @@ add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir) # be called when --help is passed, and the mozconfig wouldn't be read. -@depends('MOZCONFIG', 'OLD_CONFIGURE', - check_build_environment, '--with-external-source-dir', +@depends('MOZCONFIG', 'OLD_CONFIGURE', check_build_environment, '--help') @imports(_from='mozbuild.mozconfig', _import='MozconfigLoader') @imports(_from='mozboot.mozconfig', _import='find_mozconfig') -def mozconfig(mozconfig, old_configure, build_env, - external_source_dir, help): +def mozconfig(mozconfig, old_configure, build_env, help): if not old_configure and not help: die('The OLD_CONFIGURE environment variable must be set') @@ -185,8 +171,6 @@ def mozconfig(mozconfig, old_configure, build_env, return {'path': None} topsrcdir = build_env.topsrcdir - if external_source_dir: - topsrcdir = external_source_dir[0] loader = MozconfigLoader(topsrcdir) mozconfig = mozconfig[0] if mozconfig else None mozconfig = find_mozconfig(topsrcdir, env={'MOZCONFIG': mozconfig}) @@ -1100,17 +1084,13 @@ def target_is_sparc(target): set_define('SPARC64', target_is_sparc) -@depends('--enable-project', '--with-external-source-dir', - check_build_environment, '--help') +@depends('--enable-project', check_build_environment, '--help') @imports(_from='os.path', _import='exists') -def include_project_configure(project, external_source_dir, build_env, help): +def include_project_configure(project, build_env, help): if not project: die('--enable-project is required.') base_dir = build_env.topsrcdir - if external_source_dir: - base_dir = os.path.join(base_dir, external_source_dir[0]) - path = os.path.join(base_dir, project[0], 'moz.configure') if not exists(path): die('Cannot find project %s', project[0]) diff --git a/old-configure.in b/old-configure.in index 1c3e07992883..d1bb65083857 100644 --- a/old-configure.in +++ b/old-configure.in @@ -1655,8 +1655,6 @@ fi if test -f "${_topsrcdir}/$REAL_BRANDING_DIRECTORY/configure.sh"; then . "${_topsrcdir}/$REAL_BRANDING_DIRECTORY/configure.sh" -elif test -f "${EXTERNAL_SOURCE_DIR}/$REAL_BRANDING_DIRECTORY/configure.sh"; then - . "${EXTERNAL_SOURCE_DIR}/$REAL_BRANDING_DIRECTORY/configure.sh" fi AC_SUBST(MOZ_BRANDING_DIRECTORY) diff --git a/python/mozbuild/mozbuild/backend/configenvironment.py b/python/mozbuild/mozbuild/backend/configenvironment.py index 20d1a9fa69da..c861264d1300 100644 --- a/python/mozbuild/mozbuild/backend/configenvironment.py +++ b/python/mozbuild/mozbuild/backend/configenvironment.py @@ -183,14 +183,6 @@ class ConfigEnvironment(object): self.substs = ReadOnlyDict(self.substs) - self.external_source_dir = None - external = self.substs.get('EXTERNAL_SOURCE_DIR', '') - if external: - external = mozpath.normpath(external) - if not os.path.isabs(external): - external = mozpath.join(self.topsrcdir, external) - self.external_source_dir = mozpath.normpath(external) - @property def is_artifact_build(self): return self.substs.get('MOZ_ARTIFACT_BUILDS', False) diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index d54041f606b3..21c3d3d1faf0 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -769,11 +769,6 @@ class SourcePath(Path): if value.startswith('/'): path = None - # If the path starts with a '/' and is actually relative to an - # external source dir, use that as base instead of topsrcdir. - if context.config.external_source_dir: - path = mozpath.join(context.config.external_source_dir, - value[1:]) if not path or not os.path.exists(path): path = mozpath.join(context.config.topsrcdir, value[1:]) diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 05514065ce0e..c461a715e8b0 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -118,7 +118,6 @@ class EmptyConfig(object): self.substs = self.PopulateOnGetDict(EmptyValue, substs or self.default_substs) self.defines = self.substs - self.external_source_dir = None self.error_is_fatal = False @@ -141,12 +140,6 @@ def is_read_allowed(path, config): if mozpath.basedir(path, [topsrcdir]): return True - if config.external_source_dir: - external_dir = os.path.normcase(config.external_source_dir) - norm_path = os.path.normcase(path) - if mozpath.basedir(norm_path, [external_dir]): - return True - return False @@ -1098,14 +1091,6 @@ class BuildReader(object): topobjdir = config.topobjdir - if not mozpath.basedir(path, [config.topsrcdir]): - external = config.external_source_dir - if external and mozpath.basedir(path, [external]): - config = ConfigEnvironment.from_config_status( - mozpath.join(topobjdir, 'config.status')) - config.topsrcdir = external - config.external_source_dir = None - relpath = mozpath.relpath(path, config.topsrcdir) reldir = mozpath.dirname(relpath) @@ -1114,7 +1099,6 @@ class BuildReader(object): config = ConfigEnvironment.from_config_status( mozpath.join(topobjdir, reldir, 'config.status')) config.topobjdir = topobjdir - config.external_source_dir = None context = Context(VARIABLES, config, self.finder) sandbox = MozbuildSandbox(context, metadata=metadata, diff --git a/python/mozbuild/mozbuild/test/common.py b/python/mozbuild/mozbuild/test/common.py index 93058a64c7e7..caf05a2b781c 100644 --- a/python/mozbuild/mozbuild/test/common.py +++ b/python/mozbuild/mozbuild/test/common.py @@ -61,7 +61,6 @@ class MockConfig(object): self.defines = self.substs - self.external_source_dir = None self.lib_prefix = 'lib' self.rust_lib_prefix = 'lib' self.lib_suffix = '.a' diff --git a/python/mozbuild/mozbuild/test/frontend/test_context.py b/python/mozbuild/mozbuild/test/frontend/test_context.py index 58700f1f9003..741bdc875e9b 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_context.py +++ b/python/mozbuild/mozbuild/test/frontend/test_context.py @@ -286,7 +286,6 @@ class TestPaths(unittest.TestCase): cls.config = config = Config() config.topsrcdir = mozpath.abspath(os.curdir) config.topobjdir = mozpath.abspath('obj') - config.external_source_dir = None def test_path(self): config = self.config From a9f1a51e99b0d7cf2d9abc97535222212e7c9e80 Mon Sep 17 00:00:00 2001 From: Tom Ritter Date: Thu, 10 Sep 2020 03:36:36 +0000 Subject: [PATCH 04/26] Bug 1659182 - Do not run the civet toolchain job unless explicitly requested r=aki Differential Revision: https://phabricator.services.mozilla.com/D89315 --- taskcluster/ci/toolchain/clang-tidy.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/taskcluster/ci/toolchain/clang-tidy.yml b/taskcluster/ci/toolchain/clang-tidy.yml index cbc7c58c6b7c..df3a0303df6c 100644 --- a/taskcluster/ci/toolchain/clang-tidy.yml +++ b/taskcluster/ci/toolchain/clang-tidy.yml @@ -21,8 +21,6 @@ job-defaults: - 'build/clang-plugin/moz.build' - 'build/clang-plugin/Makefile.in' - 'build/build-clang/build-clang.py' - run-on-projects: - - trunk fetches: fetch: - clang-10 @@ -38,6 +36,8 @@ linux64-clang-tidy: - 'build/build-clang/clang-tidy-linux64.json' resources: - 'build/build-clang/clang-tidy-linux64.json' + run-on-projects: + - trunk fetches: toolchain: - linux64-binutils @@ -61,6 +61,8 @@ macosx64-clang-tidy: resources: - 'build/build-clang/clang-tidy-macosx64.json' - 'taskcluster/scripts/misc/tooltool-download.sh' + run-on-projects: + - trunk fetches: toolchain: - linux64-binutils @@ -88,12 +90,16 @@ win64-clang-tidy: resources: - 'build/build-clang/clang-tidy-win64.json' tooltool-downloads: internal + run-on-projects: + - trunk fetches: fetch: - cmake - ninja linux64-civet-tidy: + attributes: + local-toolchain: false index: job-name: linux64-civet-tidy treeherder: @@ -110,6 +116,7 @@ linux64-civet-tidy: - 'build/build-clang/civet-tidy-linux64.json' resources: - 'build/build-clang/civet-tidy-linux64.json' + run-on-projects: [] fetches: fetch: - civet-source From 009decd190f2985d67138b76bea69c9fb3b22ce5 Mon Sep 17 00:00:00 2001 From: James Teh Date: Thu, 10 Sep 2020 04:47:49 +0000 Subject: [PATCH 05/26] Bug 1662879 part 1: Expose the credit card type via aria-label in the credit card list in Preferences. r=zbraniecki Differential Revision: https://phabricator.services.mozilla.com/D89548 --- .../formautofill/content/manageDialog.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/browser/extensions/formautofill/content/manageDialog.js b/browser/extensions/formautofill/content/manageDialog.js index df29cf23c472..1a9de8160ec3 100644 --- a/browser/extensions/formautofill/content/manageDialog.js +++ b/browser/extensions/formautofill/content/manageDialog.js @@ -449,6 +449,21 @@ class ManageCreditCards extends ManageRecords { let record = option.record; if (record && record["cc-type"]) { option.setAttribute("cc-type", record["cc-type"]); + // The card type is displayed visually using an image. For a11y, we need + // to expose it as text. We do this using aria-label. However, + // aria-label overrides the text content, so we must include that also. + // XXX Bug 1664086: We should set aria-label using Fluent. The code + // below does not react to locale changes, so aria-label will be wrong + // if the locale is changed at runtime. This hack was necessary to get + // this uplifted to 81, but should be fixed sooner rather than later. + const ccTypeName = FormAutofillUtils.stringBundle.GetStringFromName( + `cardNetwork.${record["cc-type"]}` + ); + await document.l10n.translateElements([option]); + option.setAttribute( + "aria-label", + `${ccTypeName} ${option.textContent}` + ); } else { option.removeAttribute("cc-type"); } From 7a1dc60a540f3ddcd3e8dc7b1b15517d91f0a9fc Mon Sep 17 00:00:00 2001 From: James Teh Date: Thu, 10 Sep 2020 02:41:27 +0000 Subject: [PATCH 06/26] Bug 1662879 part 2: Expose the credit card type via aria-label for credit card autocomplete results. r=zbraniecki Differential Revision: https://phabricator.services.mozilla.com/D89549 --- .../ProfileAutoCompleteResult.jsm | 21 ++++++++++++++----- .../formautofill/content/customElements.js | 5 ++++- .../browser_creditCard_dropdown_layout.js | 4 ++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm index 676621fc3295..ee95015caf60 100644 --- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm +++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm @@ -421,14 +421,25 @@ class CreditCardResult extends ProfileAutoCompleteResult { primaryAffix = affix; primary = label; } + const secondary = this._getSecondaryLabel( + focusedFieldName, + allFieldNames, + profile + ); + // The card type is displayed visually using an image. For a11y, we need + // to expose it as text. We do this using aria-label. However, + // aria-label overrides the text content, so we must include that also. + const ccTypeName = FormAutofillUtils.stringBundle.GetStringFromName( + `cardNetwork.${profile["cc-type"]}` + ); + const ariaLabel = [ccTypeName, primaryAffix, primary, secondary] + .filter(chunk => !!chunk) // Exclude empty chunks. + .join(" "); return { primaryAffix, primary, - secondary: this._getSecondaryLabel( - focusedFieldName, - allFieldNames, - profile - ), + secondary, + ariaLabel, }; }); // Add an empty result entry for footer. diff --git a/browser/extensions/formautofill/content/customElements.js b/browser/extensions/formautofill/content/customElements.js index de2543923674..a6add2317019 100644 --- a/browser/extensions/formautofill/content/customElements.js +++ b/browser/extensions/formautofill/content/customElements.js @@ -143,13 +143,16 @@ `url(${this.getAttribute("ac-image")})` ); - let { primaryAffix, primary, secondary } = JSON.parse( + let { primaryAffix, primary, secondary, ariaLabel } = JSON.parse( this.getAttribute("ac-value") ); this._labelAffix.textContent = primaryAffix; this._label.textContent = primary; this._comment.textContent = secondary; + if (ariaLabel) { + this.setAttribute("aria-label", ariaLabel); + } } }; diff --git a/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js b/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js index ced45e81106c..b6115a950890 100644 --- a/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js +++ b/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js @@ -33,6 +33,10 @@ add_task(async function test_credit_card_dropdown() { const firstItem = getDisplayedPopupItems(browser)[0]; isnot(firstItem.getAttribute("ac-image"), "", "Should show icon"); + ok( + firstItem.getAttribute("aria-label").startsWith("Visa "), + "aria-label should start with Visa" + ); // The breakpoint of two-lines layout is 185px await reopenPopupWithResizedInput(browser, focusInput, 175); From 8b9afb0283ba70a4130552d1c7f13aafd110e3a3 Mon Sep 17 00:00:00 2001 From: Dmitry Bezhetskov Date: Thu, 10 Sep 2020 05:18:04 +0000 Subject: [PATCH 07/26] Bug 1639153 - Part 6.2: Establish dependency from tls for x86 callWithABI div/mod i64. r=lth x86 has few register so to do div/mod for i64 it call the runtime and clobber almost all gp registers including WasmTlsReg. To be able to call c++ runtime via Builtin thunk we need to set up WasmTlsReg. In this patch I create dependencies from MIR level to Codegen to be sure that WasmTlsReg is alive when we call runtime. Differential Revision: https://phabricator.services.mozilla.com/D88524 --- js/src/jit/Lowering.cpp | 8 ++ js/src/jit/MIR.h | 138 ++++++++++++++++++++++++++ js/src/jit/arm/Lowering-arm.cpp | 8 ++ js/src/jit/arm/Lowering-arm.h | 2 + js/src/jit/arm64/Lowering-arm64.cpp | 8 ++ js/src/jit/arm64/Lowering-arm64.h | 2 + js/src/jit/mips32/Lowering-mips32.cpp | 8 ++ js/src/jit/mips32/Lowering-mips32.h | 2 + js/src/jit/mips64/Lowering-mips64.cpp | 8 ++ js/src/jit/mips64/Lowering-mips64.h | 2 + js/src/jit/x64/Lowering-x64.cpp | 8 ++ js/src/jit/x64/Lowering-x64.h | 2 + js/src/jit/x86/CodeGenerator-x86.cpp | 41 +++++--- js/src/jit/x86/LIR-x86.h | 68 ++++++------- js/src/jit/x86/Lowering-x86.cpp | 60 ++++++++--- js/src/jit/x86/Lowering-x86.h | 2 + js/src/wasm/WasmIonCompile.cpp | 26 +++++ 17 files changed, 330 insertions(+), 63 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 5a0af4ced648..b9fee872230a 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1845,6 +1845,14 @@ void LIRGenerator::visitDiv(MDiv* ins) { MOZ_CRASH("Unhandled number specialization"); } +void LIRGenerator::visitWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + lowerWasmBuiltinDivI64(div); +} + +void LIRGenerator::visitWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + lowerWasmBuiltinModI64(mod); +} + void LIRGenerator::visitMod(MMod* ins) { MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type()); MOZ_ASSERT(IsNumberType(ins->type())); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index f9e0f55e0aff..adcc0ef2d3f7 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5736,6 +5736,85 @@ class MDiv : public MBinaryArithInstruction { ALLOW_CLONE(MDiv) }; +class MWasmBuiltinDivI64 : public MAryInstruction<3>, public ArithPolicy::Data { + bool canBeNegativeZero_; + bool canBeNegativeOverflow_; + bool canBeDivideByZero_; + bool canBeNegativeDividend_; + bool unsigned_; // If false, signedness will be derived from operands + bool trapOnError_; + wasm::BytecodeOffset bytecodeOffset_; + + MWasmBuiltinDivI64(MDefinition* left, MDefinition* right, MDefinition* tls) + : MAryInstruction(classOpcode), + canBeNegativeZero_(true), + canBeNegativeOverflow_(true), + canBeDivideByZero_(true), + canBeNegativeDividend_(true), + unsigned_(false), + trapOnError_(false) { + initOperand(0, left); + initOperand(1, right); + initOperand(2, tls); + + setResultType(MIRType::Int64); + setMovable(); + } + + public: + INSTRUCTION_HEADER(WasmBuiltinDivI64) + + NAMED_OPERANDS((0, lhs), (1, rhs), (2, tls)) + + static MWasmBuiltinDivI64* New( + TempAllocator& alloc, MDefinition* left, MDefinition* right, + MDefinition* tls, bool unsignd, bool trapOnError = false, + wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) { + auto* wasm64Div = new (alloc) MWasmBuiltinDivI64(left, right, tls); + wasm64Div->unsigned_ = unsignd; + wasm64Div->trapOnError_ = trapOnError; + wasm64Div->bytecodeOffset_ = bytecodeOffset; + if (trapOnError) { + wasm64Div->setGuard(); // not removable because of possible side-effects. + wasm64Div->setNotMovable(); + } + return wasm64Div; + } + + bool canBeNegativeZero() const { return canBeNegativeZero_; } + void setCanBeNegativeZero(bool negativeZero) { + canBeNegativeZero_ = negativeZero; + } + + bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; } + + bool canBeDivideByZero() const { return canBeDivideByZero_; } + + bool canBeNegativeDividend() const { + // "Dividend" is an ambiguous concept for unsigned truncated + // division, because of the truncation procedure: + // ((x>>>0)/2)|0, for example, gets transformed in + // MWasmDiv::truncate into a node with lhs representing x (not + // x>>>0) and rhs representing the constant 2; in other words, + // the MIR node corresponds to "cast operands to unsigned and + // divide" operation. In this case, is the dividend x or is it + // x>>>0? In order to resolve such ambiguities, we disallow + // the usage of this method for unsigned division. + MOZ_ASSERT(!unsigned_); + return canBeNegativeDividend_; + } + + bool isUnsigned() const { return unsigned_; } + + bool trapOnError() const { return trapOnError_; } + wasm::BytecodeOffset bytecodeOffset() const { + MOZ_ASSERT(bytecodeOffset_.isValid()); + return bytecodeOffset_; + } + + ALLOW_CLONE(MWasmBuiltinDivI64) +}; + class MMod : public MBinaryArithInstruction { bool unsigned_; // If false, signedness will be derived from operands bool canBeNegativeDividend_; @@ -5829,6 +5908,65 @@ class MMod : public MBinaryArithInstruction { ALLOW_CLONE(MMod) }; +class MWasmBuiltinModI64 : public MAryInstruction<3>, public ArithPolicy::Data { + bool unsigned_; // If false, signedness will be derived from operands + bool canBeNegativeDividend_; + bool canBeDivideByZero_; + bool trapOnError_; + wasm::BytecodeOffset bytecodeOffset_; + + MWasmBuiltinModI64(MDefinition* left, MDefinition* right, MDefinition* tls) + : MAryInstruction(classOpcode), + unsigned_(false), + canBeNegativeDividend_(true), + canBeDivideByZero_(true), + trapOnError_(false) { + initOperand(0, left); + initOperand(1, right); + initOperand(2, tls); + + setResultType(MIRType::Int64); + setMovable(); + } + + public: + INSTRUCTION_HEADER(WasmBuiltinModI64) + + NAMED_OPERANDS((0, lhs), (1, rhs), (2, tls)) + + static MWasmBuiltinModI64* New( + TempAllocator& alloc, MDefinition* left, MDefinition* right, + MDefinition* tls, bool unsignd, bool trapOnError = false, + wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) { + auto* mod = new (alloc) MWasmBuiltinModI64(left, right, tls); + mod->unsigned_ = unsignd; + mod->trapOnError_ = trapOnError; + mod->bytecodeOffset_ = bytecodeOffset; + if (trapOnError) { + mod->setGuard(); // not removable because of possible side-effects. + mod->setNotMovable(); + } + return mod; + } + + bool canBeNegativeDividend() const { + MOZ_ASSERT(!unsigned_); + return canBeNegativeDividend_; + } + + bool canBeDivideByZero() const { return canBeDivideByZero_; } + + bool isUnsigned() const { return unsigned_; } + + bool trapOnError() const { return trapOnError_; } + wasm::BytecodeOffset bytecodeOffset() const { + MOZ_ASSERT(bytecodeOffset_.isValid()); + return bytecodeOffset_; + } + + ALLOW_CLONE(MWasmBuiltinModI64) +}; + class MConcat : public MBinaryInstruction, public MixPolicy, ConvertToStringPolicy<1>>::Data { diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index 642a14f42ff0..c8b4d2c3d6ed 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -419,6 +419,10 @@ void LIRGeneratorARM::lowerDivI64(MDiv* div) { defineReturn(lir, div); } +void LIRGeneratorARM::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MOZ_CRASH("We don't use runtime div for this architecture"); +} + void LIRGeneratorARM::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -430,6 +434,10 @@ void LIRGeneratorARM::lowerModI64(MMod* mod) { defineReturn(lir, mod); } +void LIRGeneratorARM::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MOZ_CRASH("We don't use runtime mod for this architecture"); +} + void LIRGeneratorARM::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); diff --git a/js/src/jit/arm/Lowering-arm.h b/js/src/jit/arm/Lowering-arm.h index 24093db648dc..1af76c44dd8a 100644 --- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -72,7 +72,9 @@ class LIRGeneratorARM : public LIRGeneratorShared { void lowerDivI(MDiv* div); void lowerModI(MMod* mod); void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index 02e7f68e0349..d37272d86903 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -282,8 +282,16 @@ void LIRGeneratorARM64::lowerModI(MMod* mod) { void LIRGeneratorARM64::lowerDivI64(MDiv* div) { MOZ_CRASH("NYI"); } +void LIRGeneratorARM64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MOZ_CRASH("We don't use runtime div for this architecture"); +} + void LIRGeneratorARM64::lowerModI64(MMod* mod) { MOZ_CRASH("NYI"); } +void LIRGeneratorARM64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MOZ_CRASH("We don't use runtime mod for this architecture"); +} + void LIRGenerator::visitPowHalf(MPowHalf* ins) { MDefinition* input = ins->input(); MOZ_ASSERT(input->type() == MIRType::Double); diff --git a/js/src/jit/arm64/Lowering-arm64.h b/js/src/jit/arm64/Lowering-arm64.h index f61ae9b0de1b..d329560f07a5 100644 --- a/js/src/jit/arm64/Lowering-arm64.h +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -75,7 +75,9 @@ class LIRGeneratorARM64 : public LIRGeneratorShared { void lowerDivI(MDiv* div); void lowerModI(MMod* mod); void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); void lowerUDiv(MDiv* div); void lowerUMod(MMod* mod); diff --git a/js/src/jit/mips32/Lowering-mips32.cpp b/js/src/jit/mips32/Lowering-mips32.cpp index e61405e07994..0598c9320374 100644 --- a/js/src/jit/mips32/Lowering-mips32.cpp +++ b/js/src/jit/mips32/Lowering-mips32.cpp @@ -194,6 +194,10 @@ void LIRGeneratorMIPS::lowerDivI64(MDiv* div) { defineReturn(lir, div); } +void LIRGeneratorMIPS::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MOZ_CRASH("We don't use runtime div for this architecture"); +} + void LIRGeneratorMIPS::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -206,6 +210,10 @@ void LIRGeneratorMIPS::lowerModI64(MMod* mod) { defineReturn(lir, mod); } +void LIRGeneratorMIPS::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MOZ_CRASH("We don't use runtime mod for this architecture"); +} + void LIRGeneratorMIPS::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); diff --git a/js/src/jit/mips32/Lowering-mips32.h b/js/src/jit/mips32/Lowering-mips32.h index ec94d7de5e2e..665cb403f50e 100644 --- a/js/src/jit/mips32/Lowering-mips32.h +++ b/js/src/jit/mips32/Lowering-mips32.h @@ -34,7 +34,9 @@ class LIRGeneratorMIPS : public LIRGeneratorMIPSShared { void lowerTruncateFToInt32(MTruncateToInt32* ins); void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/mips64/Lowering-mips64.cpp b/js/src/jit/mips64/Lowering-mips64.cpp index f4559859b2e8..0290758f1f21 100644 --- a/js/src/jit/mips64/Lowering-mips64.cpp +++ b/js/src/jit/mips64/Lowering-mips64.cpp @@ -43,6 +43,10 @@ void LIRGeneratorMIPS64::lowerDivI64(MDiv* div) { defineInt64(lir, div); } +void LIRGeneratorMIPS64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MOZ_CRASH("We don't use runtime div for this architecture"); +} + void LIRGeneratorMIPS64::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -54,6 +58,10 @@ void LIRGeneratorMIPS64::lowerModI64(MMod* mod) { defineInt64(lir, mod); } +void LIRGeneratorMIPS64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MOZ_CRASH("We don't use runtime mod for this architecture"); +} + void LIRGeneratorMIPS64::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()), temp()); diff --git a/js/src/jit/mips64/Lowering-mips64.h b/js/src/jit/mips64/Lowering-mips64.h index 4875ef45614c..2a2664393efd 100644 --- a/js/src/jit/mips64/Lowering-mips64.h +++ b/js/src/jit/mips64/Lowering-mips64.h @@ -33,7 +33,9 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared { void lowerTruncateFToInt32(MTruncateToInt32* ins); void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index f97ae77afa2f..d1a607517194 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -352,6 +352,10 @@ void LIRGeneratorX64::lowerDivI64(MDiv* div) { defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax)))); } +void LIRGeneratorX64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MOZ_CRASH("We don't use runtime div for this architecture"); +} + void LIRGeneratorX64::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -363,6 +367,10 @@ void LIRGeneratorX64::lowerModI64(MMod* mod) { defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx)))); } +void LIRGeneratorX64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MOZ_CRASH("We don't use runtime mod for this architecture"); +} + void LIRGeneratorX64::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(rdx)); diff --git a/js/src/jit/x64/Lowering-x64.h b/js/src/jit/x64/Lowering-x64.h index 4093f92459c3..f20c617283e6 100644 --- a/js/src/jit/x64/Lowering-x64.h +++ b/js/src/jit/x64/Lowering-x64.h @@ -45,7 +45,9 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared { bool needTempForPostBarrier() { return true; } void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index e381ef775af9..2689f0c14029 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -803,9 +803,14 @@ void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { } void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { + MOZ_ASSERT(gen->compilingWasm()); + MOZ_ASSERT(ToRegister(lir->getOperand(LDivOrModI64::Tls)) == WasmTlsReg); + + masm.Push(WasmTlsReg); + int32_t framePushedAfterTls = masm.framePushed(); + Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs)); Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs)); - Register temp = ToRegister(lir->temp()); Register64 output = ToOutRegister64(lir); MOZ_ASSERT(output == ReturnReg64); @@ -815,7 +820,8 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { // Handle divide by zero. if (lir->canBeDivideByZero()) { Label nonZero; - masm.branchTest64(Assembler::NonZero, rhs, rhs, temp, &nonZero); + // We can use WasmTlsReg as temp register because we preserved it before. + masm.branchTest64(Assembler::NonZero, rhs, rhs, WasmTlsReg, &nonZero); masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); masm.bind(&nonZero); } @@ -827,7 +833,7 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { Label notOverflow; masm.branch64(Assembler::NotEqual, lhs, Imm64(INT64_MIN), ¬Overflow); masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬Overflow); - if (mir->isMod()) { + if (mir->isWasmBuiltinModI64()) { masm.xor64(output, output); } else { masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->bytecodeOffset()); @@ -842,13 +848,13 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { masm.passABIArg(rhs.high); masm.passABIArg(rhs.low); - MOZ_ASSERT(gen->compilingWasm()); - if (mir->isMod()) { + int32_t tlsOffset = masm.framePushed() - framePushedAfterTls; + if (mir->isWasmBuiltinModI64()) { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::ModI64, - mozilla::Nothing()); + mozilla::Some(tlsOffset)); } else { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::DivI64, - mozilla::Nothing()); + mozilla::Some(tlsOffset)); } // output in edx:eax, move to output register. @@ -856,12 +862,18 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { MOZ_ASSERT(eax == output.low); masm.bind(&done); + masm.Pop(WasmTlsReg); } void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { + MOZ_ASSERT(gen->compilingWasm()); + MOZ_ASSERT(ToRegister(lir->getOperand(LDivOrModI64::Tls)) == WasmTlsReg); + + masm.Push(WasmTlsReg); + int32_t framePushedAfterTls = masm.framePushed(); + Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs)); Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs)); - Register temp = ToRegister(lir->temp()); Register64 output = ToOutRegister64(lir); MOZ_ASSERT(output == ReturnReg64); @@ -869,7 +881,8 @@ void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { // Prevent divide by zero. if (lir->canBeDivideByZero()) { Label nonZero; - masm.branchTest64(Assembler::NonZero, rhs, rhs, temp, &nonZero); + // We can use WasmTlsReg as temp register because we preserved it before. + masm.branchTest64(Assembler::NonZero, rhs, rhs, WasmTlsReg, &nonZero); masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); masm.bind(&nonZero); } @@ -880,19 +893,21 @@ void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { masm.passABIArg(rhs.high); masm.passABIArg(rhs.low); - MOZ_ASSERT(gen->compilingWasm()); MDefinition* mir = lir->mir(); - if (mir->isMod()) { + int32_t tlsOffset = masm.framePushed() - framePushedAfterTls; + if (mir->isWasmBuiltinModI64()) { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UModI64, - mozilla::Nothing()); + mozilla::Some(tlsOffset)); } else { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UDivI64, - mozilla::Nothing()); + mozilla::Some(tlsOffset)); } // output in edx:eax, move to output register. masm.movl(edx, output.high); MOZ_ASSERT(eax == output.low); + + masm.Pop(WasmTlsReg); } void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { diff --git a/js/src/jit/x86/LIR-x86.h b/js/src/jit/x86/LIR-x86.h index 5c3075fc8f73..25cdccae65db 100644 --- a/js/src/jit/x86/LIR-x86.h +++ b/js/src/jit/x86/LIR-x86.h @@ -86,87 +86,87 @@ class LWasmUint32ToFloat32 : public LInstructionHelper<1, 1, 1> { }; class LDivOrModI64 - : public LCallInstructionHelper { + : public LCallInstructionHelper { public: LIR_HEADER(DivOrModI64) static const size_t Lhs = 0; static const size_t Rhs = INT64_PIECES; + static const size_t Tls = 2 * INT64_PIECES; LDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs, - const LDefinition& temp) + const LAllocation& tls) : LCallInstructionHelper(classOpcode) { setInt64Operand(Lhs, lhs); setInt64Operand(Rhs, rhs); - setTemp(0, temp); + setOperand(Tls, tls); } - MBinaryArithInstruction* mir() const { - MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); - return static_cast(mir_); + MDefinition* mir() const { + MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); + return mir_; } bool canBeDivideByZero() const { - if (mir_->isMod()) { - return mir_->toMod()->canBeDivideByZero(); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->canBeDivideByZero(); } - return mir_->toDiv()->canBeDivideByZero(); + return mir_->toWasmBuiltinDivI64()->canBeDivideByZero(); } bool canBeNegativeOverflow() const { - if (mir_->isMod()) { - return mir_->toMod()->canBeNegativeDividend(); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->canBeNegativeDividend(); } - return mir_->toDiv()->canBeNegativeOverflow(); + return mir_->toWasmBuiltinDivI64()->canBeNegativeOverflow(); } wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); - if (mir_->isMod()) { - return mir_->toMod()->bytecodeOffset(); + MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->bytecodeOffset(); } - return mir_->toDiv()->bytecodeOffset(); + return mir_->toWasmBuiltinDivI64()->bytecodeOffset(); } - const LDefinition* temp() { return getTemp(0); } }; class LUDivOrModI64 - : public LCallInstructionHelper { + : public LCallInstructionHelper { public: LIR_HEADER(UDivOrModI64) static const size_t Lhs = 0; static const size_t Rhs = INT64_PIECES; + static const size_t Tls = 2 * INT64_PIECES; LUDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs, - const LDefinition& temp) + const LAllocation& tls) : LCallInstructionHelper(classOpcode) { setInt64Operand(Lhs, lhs); setInt64Operand(Rhs, rhs); - setTemp(0, temp); + setOperand(Tls, tls); } - MBinaryArithInstruction* mir() const { - MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); - return static_cast(mir_); + MDefinition* mir() const { + MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); + return mir_; } bool canBeDivideByZero() const { - if (mir_->isMod()) { - return mir_->toMod()->canBeDivideByZero(); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->canBeDivideByZero(); } - return mir_->toDiv()->canBeDivideByZero(); + return mir_->toWasmBuiltinDivI64()->canBeDivideByZero(); } bool canBeNegativeOverflow() const { - if (mir_->isMod()) { - return mir_->toMod()->canBeNegativeDividend(); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->canBeNegativeDividend(); } - return mir_->toDiv()->canBeNegativeOverflow(); + return mir_->toWasmBuiltinDivI64()->canBeNegativeOverflow(); } wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); - if (mir_->isMod()) { - return mir_->toMod()->bytecodeOffset(); + MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); + if (mir_->isWasmBuiltinModI64()) { + return mir_->toWasmBuiltinModI64()->bytecodeOffset(); } - return mir_->toDiv()->bytecodeOffset(); + return mir_->toWasmBuiltinDivI64()->bytecodeOffset(); } - const LDefinition* temp() { return getTemp(0); } }; class LWasmTruncateToInt64 : public LInstructionHelper { diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 60e4ae4baed0..23379731836d 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -586,41 +586,69 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { } void LIRGeneratorX86::lowerDivI64(MDiv* div) { + MOZ_CRASH("We use MWasmBuiltinModI64 instead."); +} + +void LIRGeneratorX86::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { + MDefinition* lhs = div->lhs(); + MDefinition* rhs = div->rhs(); + MOZ_ASSERT(lhs->type() == rhs->type()); + MOZ_ASSERT(IsNumberType(div->type())); + + MOZ_ASSERT(div->type() == MIRType::Int64); + MOZ_ASSERT(div->type() == MIRType::Int64); + if (div->isUnsigned()) { - lowerUDivI64(div); + LUDivOrModI64* lir = new (alloc()) + LUDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), + useFixedAtStart(div->tls(), WasmTlsReg)); + defineReturn(lir, div); return; } - LDivOrModI64* lir = new (alloc()) LDivOrModI64( - useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); + LDivOrModI64* lir = new (alloc()) + LDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), + useFixedAtStart(div->tls(), WasmTlsReg)); defineReturn(lir, div); } void LIRGeneratorX86::lowerModI64(MMod* mod) { + MOZ_CRASH("We use MWasmBuiltinModI64 instead."); +} + +void LIRGeneratorX86::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { + MDefinition* lhs = mod->lhs(); + MDefinition* rhs = mod->rhs(); + MOZ_ASSERT(lhs->type() == rhs->type()); + MOZ_ASSERT(IsNumberType(mod->type())); + + MOZ_ASSERT(mod->type() == MIRType::Int64); + MOZ_ASSERT(mod->type() == MIRType::Int64); + if (mod->isUnsigned()) { - lowerUModI64(mod); + LUDivOrModI64* lir = new (alloc()) + LUDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), + useFixedAtStart(mod->tls(), WasmTlsReg)); + defineReturn(lir, mod); return; } - LDivOrModI64* lir = new (alloc()) LDivOrModI64( - useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); + LDivOrModI64* lir = new (alloc()) + LDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), + useFixedAtStart(mod->tls(), WasmTlsReg)); defineReturn(lir, mod); } void LIRGeneratorX86::lowerUDivI64(MDiv* div) { - LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( - useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); - defineReturn(lir, div); + MOZ_CRASH("We use MWasmBuiltinDivI64 instead."); } void LIRGeneratorX86::lowerUModI64(MMod* mod) { - LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( - useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); - defineReturn(lir, mod); + MOZ_CRASH("We use MWasmBuiltinModI64 instead."); } void LIRGenerator::visitSubstr(MSubstr* ins) { diff --git a/js/src/jit/x86/Lowering-x86.h b/js/src/jit/x86/Lowering-x86.h index 2e00dd6fd4aa..6f18ad9d7f5c 100644 --- a/js/src/jit/x86/Lowering-x86.h +++ b/js/src/jit/x86/Lowering-x86.h @@ -49,7 +49,9 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared { MDefinition* rhs); void lowerDivI64(MDiv* div); + void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); + void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index d4dc40832bd5..db55615a67ad 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -460,6 +460,19 @@ class FunctionCompiler { curBlock_->add(rhs2); rhs = rhs2; } + + // For x86 we implement i64 div via c++ runtime. + // A call to c++ runtime requires tls pointer. +#ifdef JS_CODEGEN_X86 + if (type == MIRType::Int64) { + auto* ins = + MWasmBuiltinDivI64::New(alloc(), lhs, rhs, tlsPointer_, unsignd, + trapOnError, bytecodeOffset()); + curBlock_->add(ins); + return ins; + } +#endif + auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError, bytecodeOffset(), mustPreserveNaN(type)); curBlock_->add(ins); @@ -481,6 +494,19 @@ class FunctionCompiler { curBlock_->add(rhs2); rhs = rhs2; } + + // This is because x86 codegen calls runtime via BuiltinThunk and so it + // needs WasmTlsReg to be live. +#ifdef JS_CODEGEN_X86 + if (type == MIRType::Int64) { + auto* ins = + MWasmBuiltinModI64::New(alloc(), lhs, rhs, tlsPointer_, unsignd, + trapOnError, bytecodeOffset()); + curBlock_->add(ins); + return ins; + } +#endif + auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError, bytecodeOffset()); curBlock_->add(ins); From b7de916a2f3f3a9a061f059a8fb8453016a7b949 Mon Sep 17 00:00:00 2001 From: Butkovits Atila Date: Thu, 10 Sep 2020 09:14:01 +0300 Subject: [PATCH 08/26] Backed out changeset eb35fdc50799 (bug 1639153) for bustages at Lowering.cpp. CLOSED TREE --- js/src/jit/Lowering.cpp | 8 -- js/src/jit/MIR.h | 138 -------------------------- js/src/jit/arm/Lowering-arm.cpp | 8 -- js/src/jit/arm/Lowering-arm.h | 2 - js/src/jit/arm64/Lowering-arm64.cpp | 8 -- js/src/jit/arm64/Lowering-arm64.h | 2 - js/src/jit/mips32/Lowering-mips32.cpp | 8 -- js/src/jit/mips32/Lowering-mips32.h | 2 - js/src/jit/mips64/Lowering-mips64.cpp | 8 -- js/src/jit/mips64/Lowering-mips64.h | 2 - js/src/jit/x64/Lowering-x64.cpp | 8 -- js/src/jit/x64/Lowering-x64.h | 2 - js/src/jit/x86/CodeGenerator-x86.cpp | 41 +++----- js/src/jit/x86/LIR-x86.h | 68 ++++++------- js/src/jit/x86/Lowering-x86.cpp | 60 +++-------- js/src/jit/x86/Lowering-x86.h | 2 - js/src/wasm/WasmIonCompile.cpp | 26 ----- 17 files changed, 63 insertions(+), 330 deletions(-) diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index b9fee872230a..5a0af4ced648 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -1845,14 +1845,6 @@ void LIRGenerator::visitDiv(MDiv* ins) { MOZ_CRASH("Unhandled number specialization"); } -void LIRGenerator::visitWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - lowerWasmBuiltinDivI64(div); -} - -void LIRGenerator::visitWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - lowerWasmBuiltinModI64(mod); -} - void LIRGenerator::visitMod(MMod* ins) { MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type()); MOZ_ASSERT(IsNumberType(ins->type())); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index adcc0ef2d3f7..f9e0f55e0aff 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -5736,85 +5736,6 @@ class MDiv : public MBinaryArithInstruction { ALLOW_CLONE(MDiv) }; -class MWasmBuiltinDivI64 : public MAryInstruction<3>, public ArithPolicy::Data { - bool canBeNegativeZero_; - bool canBeNegativeOverflow_; - bool canBeDivideByZero_; - bool canBeNegativeDividend_; - bool unsigned_; // If false, signedness will be derived from operands - bool trapOnError_; - wasm::BytecodeOffset bytecodeOffset_; - - MWasmBuiltinDivI64(MDefinition* left, MDefinition* right, MDefinition* tls) - : MAryInstruction(classOpcode), - canBeNegativeZero_(true), - canBeNegativeOverflow_(true), - canBeDivideByZero_(true), - canBeNegativeDividend_(true), - unsigned_(false), - trapOnError_(false) { - initOperand(0, left); - initOperand(1, right); - initOperand(2, tls); - - setResultType(MIRType::Int64); - setMovable(); - } - - public: - INSTRUCTION_HEADER(WasmBuiltinDivI64) - - NAMED_OPERANDS((0, lhs), (1, rhs), (2, tls)) - - static MWasmBuiltinDivI64* New( - TempAllocator& alloc, MDefinition* left, MDefinition* right, - MDefinition* tls, bool unsignd, bool trapOnError = false, - wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) { - auto* wasm64Div = new (alloc) MWasmBuiltinDivI64(left, right, tls); - wasm64Div->unsigned_ = unsignd; - wasm64Div->trapOnError_ = trapOnError; - wasm64Div->bytecodeOffset_ = bytecodeOffset; - if (trapOnError) { - wasm64Div->setGuard(); // not removable because of possible side-effects. - wasm64Div->setNotMovable(); - } - return wasm64Div; - } - - bool canBeNegativeZero() const { return canBeNegativeZero_; } - void setCanBeNegativeZero(bool negativeZero) { - canBeNegativeZero_ = negativeZero; - } - - bool canBeNegativeOverflow() const { return canBeNegativeOverflow_; } - - bool canBeDivideByZero() const { return canBeDivideByZero_; } - - bool canBeNegativeDividend() const { - // "Dividend" is an ambiguous concept for unsigned truncated - // division, because of the truncation procedure: - // ((x>>>0)/2)|0, for example, gets transformed in - // MWasmDiv::truncate into a node with lhs representing x (not - // x>>>0) and rhs representing the constant 2; in other words, - // the MIR node corresponds to "cast operands to unsigned and - // divide" operation. In this case, is the dividend x or is it - // x>>>0? In order to resolve such ambiguities, we disallow - // the usage of this method for unsigned division. - MOZ_ASSERT(!unsigned_); - return canBeNegativeDividend_; - } - - bool isUnsigned() const { return unsigned_; } - - bool trapOnError() const { return trapOnError_; } - wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(bytecodeOffset_.isValid()); - return bytecodeOffset_; - } - - ALLOW_CLONE(MWasmBuiltinDivI64) -}; - class MMod : public MBinaryArithInstruction { bool unsigned_; // If false, signedness will be derived from operands bool canBeNegativeDividend_; @@ -5908,65 +5829,6 @@ class MMod : public MBinaryArithInstruction { ALLOW_CLONE(MMod) }; -class MWasmBuiltinModI64 : public MAryInstruction<3>, public ArithPolicy::Data { - bool unsigned_; // If false, signedness will be derived from operands - bool canBeNegativeDividend_; - bool canBeDivideByZero_; - bool trapOnError_; - wasm::BytecodeOffset bytecodeOffset_; - - MWasmBuiltinModI64(MDefinition* left, MDefinition* right, MDefinition* tls) - : MAryInstruction(classOpcode), - unsigned_(false), - canBeNegativeDividend_(true), - canBeDivideByZero_(true), - trapOnError_(false) { - initOperand(0, left); - initOperand(1, right); - initOperand(2, tls); - - setResultType(MIRType::Int64); - setMovable(); - } - - public: - INSTRUCTION_HEADER(WasmBuiltinModI64) - - NAMED_OPERANDS((0, lhs), (1, rhs), (2, tls)) - - static MWasmBuiltinModI64* New( - TempAllocator& alloc, MDefinition* left, MDefinition* right, - MDefinition* tls, bool unsignd, bool trapOnError = false, - wasm::BytecodeOffset bytecodeOffset = wasm::BytecodeOffset()) { - auto* mod = new (alloc) MWasmBuiltinModI64(left, right, tls); - mod->unsigned_ = unsignd; - mod->trapOnError_ = trapOnError; - mod->bytecodeOffset_ = bytecodeOffset; - if (trapOnError) { - mod->setGuard(); // not removable because of possible side-effects. - mod->setNotMovable(); - } - return mod; - } - - bool canBeNegativeDividend() const { - MOZ_ASSERT(!unsigned_); - return canBeNegativeDividend_; - } - - bool canBeDivideByZero() const { return canBeDivideByZero_; } - - bool isUnsigned() const { return unsigned_; } - - bool trapOnError() const { return trapOnError_; } - wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(bytecodeOffset_.isValid()); - return bytecodeOffset_; - } - - ALLOW_CLONE(MWasmBuiltinModI64) -}; - class MConcat : public MBinaryInstruction, public MixPolicy, ConvertToStringPolicy<1>>::Data { diff --git a/js/src/jit/arm/Lowering-arm.cpp b/js/src/jit/arm/Lowering-arm.cpp index c8b4d2c3d6ed..642a14f42ff0 100644 --- a/js/src/jit/arm/Lowering-arm.cpp +++ b/js/src/jit/arm/Lowering-arm.cpp @@ -419,10 +419,6 @@ void LIRGeneratorARM::lowerDivI64(MDiv* div) { defineReturn(lir, div); } -void LIRGeneratorARM::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MOZ_CRASH("We don't use runtime div for this architecture"); -} - void LIRGeneratorARM::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -434,10 +430,6 @@ void LIRGeneratorARM::lowerModI64(MMod* mod) { defineReturn(lir, mod); } -void LIRGeneratorARM::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MOZ_CRASH("We don't use runtime mod for this architecture"); -} - void LIRGeneratorARM::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); diff --git a/js/src/jit/arm/Lowering-arm.h b/js/src/jit/arm/Lowering-arm.h index 1af76c44dd8a..24093db648dc 100644 --- a/js/src/jit/arm/Lowering-arm.h +++ b/js/src/jit/arm/Lowering-arm.h @@ -72,9 +72,7 @@ class LIRGeneratorARM : public LIRGeneratorShared { void lowerDivI(MDiv* div); void lowerModI(MMod* mod); void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index d37272d86903..02e7f68e0349 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -282,16 +282,8 @@ void LIRGeneratorARM64::lowerModI(MMod* mod) { void LIRGeneratorARM64::lowerDivI64(MDiv* div) { MOZ_CRASH("NYI"); } -void LIRGeneratorARM64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MOZ_CRASH("We don't use runtime div for this architecture"); -} - void LIRGeneratorARM64::lowerModI64(MMod* mod) { MOZ_CRASH("NYI"); } -void LIRGeneratorARM64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MOZ_CRASH("We don't use runtime mod for this architecture"); -} - void LIRGenerator::visitPowHalf(MPowHalf* ins) { MDefinition* input = ins->input(); MOZ_ASSERT(input->type() == MIRType::Double); diff --git a/js/src/jit/arm64/Lowering-arm64.h b/js/src/jit/arm64/Lowering-arm64.h index d329560f07a5..f61ae9b0de1b 100644 --- a/js/src/jit/arm64/Lowering-arm64.h +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -75,9 +75,7 @@ class LIRGeneratorARM64 : public LIRGeneratorShared { void lowerDivI(MDiv* div); void lowerModI(MMod* mod); void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs); void lowerUDiv(MDiv* div); void lowerUMod(MMod* mod); diff --git a/js/src/jit/mips32/Lowering-mips32.cpp b/js/src/jit/mips32/Lowering-mips32.cpp index 0598c9320374..e61405e07994 100644 --- a/js/src/jit/mips32/Lowering-mips32.cpp +++ b/js/src/jit/mips32/Lowering-mips32.cpp @@ -194,10 +194,6 @@ void LIRGeneratorMIPS::lowerDivI64(MDiv* div) { defineReturn(lir, div); } -void LIRGeneratorMIPS::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MOZ_CRASH("We don't use runtime div for this architecture"); -} - void LIRGeneratorMIPS::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -210,10 +206,6 @@ void LIRGeneratorMIPS::lowerModI64(MMod* mod) { defineReturn(lir, mod); } -void LIRGeneratorMIPS::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MOZ_CRASH("We don't use runtime mod for this architecture"); -} - void LIRGeneratorMIPS::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useInt64RegisterAtStart(div->lhs()), useInt64RegisterAtStart(div->rhs())); diff --git a/js/src/jit/mips32/Lowering-mips32.h b/js/src/jit/mips32/Lowering-mips32.h index 665cb403f50e..ec94d7de5e2e 100644 --- a/js/src/jit/mips32/Lowering-mips32.h +++ b/js/src/jit/mips32/Lowering-mips32.h @@ -34,9 +34,7 @@ class LIRGeneratorMIPS : public LIRGeneratorMIPSShared { void lowerTruncateFToInt32(MTruncateToInt32* ins); void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/mips64/Lowering-mips64.cpp b/js/src/jit/mips64/Lowering-mips64.cpp index 0290758f1f21..f4559859b2e8 100644 --- a/js/src/jit/mips64/Lowering-mips64.cpp +++ b/js/src/jit/mips64/Lowering-mips64.cpp @@ -43,10 +43,6 @@ void LIRGeneratorMIPS64::lowerDivI64(MDiv* div) { defineInt64(lir, div); } -void LIRGeneratorMIPS64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MOZ_CRASH("We don't use runtime div for this architecture"); -} - void LIRGeneratorMIPS64::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -58,10 +54,6 @@ void LIRGeneratorMIPS64::lowerModI64(MMod* mod) { defineInt64(lir, mod); } -void LIRGeneratorMIPS64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MOZ_CRASH("We don't use runtime mod for this architecture"); -} - void LIRGeneratorMIPS64::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()), temp()); diff --git a/js/src/jit/mips64/Lowering-mips64.h b/js/src/jit/mips64/Lowering-mips64.h index 2a2664393efd..4875ef45614c 100644 --- a/js/src/jit/mips64/Lowering-mips64.h +++ b/js/src/jit/mips64/Lowering-mips64.h @@ -33,9 +33,7 @@ class LIRGeneratorMIPS64 : public LIRGeneratorMIPSShared { void lowerTruncateFToInt32(MTruncateToInt32* ins); void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/x64/Lowering-x64.cpp b/js/src/jit/x64/Lowering-x64.cpp index d1a607517194..f97ae77afa2f 100644 --- a/js/src/jit/x64/Lowering-x64.cpp +++ b/js/src/jit/x64/Lowering-x64.cpp @@ -352,10 +352,6 @@ void LIRGeneratorX64::lowerDivI64(MDiv* div) { defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax)))); } -void LIRGeneratorX64::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MOZ_CRASH("We don't use runtime div for this architecture"); -} - void LIRGeneratorX64::lowerModI64(MMod* mod) { if (mod->isUnsigned()) { lowerUModI64(mod); @@ -367,10 +363,6 @@ void LIRGeneratorX64::lowerModI64(MMod* mod) { defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx)))); } -void LIRGeneratorX64::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MOZ_CRASH("We don't use runtime mod for this architecture"); -} - void LIRGeneratorX64::lowerUDivI64(MDiv* div) { LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( useRegister(div->lhs()), useRegister(div->rhs()), tempFixed(rdx)); diff --git a/js/src/jit/x64/Lowering-x64.h b/js/src/jit/x64/Lowering-x64.h index f20c617283e6..4093f92459c3 100644 --- a/js/src/jit/x64/Lowering-x64.h +++ b/js/src/jit/x64/Lowering-x64.h @@ -45,9 +45,7 @@ class LIRGeneratorX64 : public LIRGeneratorX86Shared { bool needTempForPostBarrier() { return true; } void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); }; diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 2689f0c14029..e381ef775af9 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -803,14 +803,9 @@ void CodeGenerator::visitCompareI64AndBranch(LCompareI64AndBranch* lir) { } void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { - MOZ_ASSERT(gen->compilingWasm()); - MOZ_ASSERT(ToRegister(lir->getOperand(LDivOrModI64::Tls)) == WasmTlsReg); - - masm.Push(WasmTlsReg); - int32_t framePushedAfterTls = masm.framePushed(); - Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs)); Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs)); + Register temp = ToRegister(lir->temp()); Register64 output = ToOutRegister64(lir); MOZ_ASSERT(output == ReturnReg64); @@ -820,8 +815,7 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { // Handle divide by zero. if (lir->canBeDivideByZero()) { Label nonZero; - // We can use WasmTlsReg as temp register because we preserved it before. - masm.branchTest64(Assembler::NonZero, rhs, rhs, WasmTlsReg, &nonZero); + masm.branchTest64(Assembler::NonZero, rhs, rhs, temp, &nonZero); masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); masm.bind(&nonZero); } @@ -833,7 +827,7 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { Label notOverflow; masm.branch64(Assembler::NotEqual, lhs, Imm64(INT64_MIN), ¬Overflow); masm.branch64(Assembler::NotEqual, rhs, Imm64(-1), ¬Overflow); - if (mir->isWasmBuiltinModI64()) { + if (mir->isMod()) { masm.xor64(output, output); } else { masm.wasmTrap(wasm::Trap::IntegerOverflow, lir->bytecodeOffset()); @@ -848,13 +842,13 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { masm.passABIArg(rhs.high); masm.passABIArg(rhs.low); - int32_t tlsOffset = masm.framePushed() - framePushedAfterTls; - if (mir->isWasmBuiltinModI64()) { + MOZ_ASSERT(gen->compilingWasm()); + if (mir->isMod()) { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::ModI64, - mozilla::Some(tlsOffset)); + mozilla::Nothing()); } else { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::DivI64, - mozilla::Some(tlsOffset)); + mozilla::Nothing()); } // output in edx:eax, move to output register. @@ -862,18 +856,12 @@ void CodeGenerator::visitDivOrModI64(LDivOrModI64* lir) { MOZ_ASSERT(eax == output.low); masm.bind(&done); - masm.Pop(WasmTlsReg); } void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { - MOZ_ASSERT(gen->compilingWasm()); - MOZ_ASSERT(ToRegister(lir->getOperand(LDivOrModI64::Tls)) == WasmTlsReg); - - masm.Push(WasmTlsReg); - int32_t framePushedAfterTls = masm.framePushed(); - Register64 lhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Lhs)); Register64 rhs = ToRegister64(lir->getInt64Operand(LDivOrModI64::Rhs)); + Register temp = ToRegister(lir->temp()); Register64 output = ToOutRegister64(lir); MOZ_ASSERT(output == ReturnReg64); @@ -881,8 +869,7 @@ void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { // Prevent divide by zero. if (lir->canBeDivideByZero()) { Label nonZero; - // We can use WasmTlsReg as temp register because we preserved it before. - masm.branchTest64(Assembler::NonZero, rhs, rhs, WasmTlsReg, &nonZero); + masm.branchTest64(Assembler::NonZero, rhs, rhs, temp, &nonZero); masm.wasmTrap(wasm::Trap::IntegerDivideByZero, lir->bytecodeOffset()); masm.bind(&nonZero); } @@ -893,21 +880,19 @@ void CodeGenerator::visitUDivOrModI64(LUDivOrModI64* lir) { masm.passABIArg(rhs.high); masm.passABIArg(rhs.low); + MOZ_ASSERT(gen->compilingWasm()); MDefinition* mir = lir->mir(); - int32_t tlsOffset = masm.framePushed() - framePushedAfterTls; - if (mir->isWasmBuiltinModI64()) { + if (mir->isMod()) { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UModI64, - mozilla::Some(tlsOffset)); + mozilla::Nothing()); } else { masm.callWithABI(lir->bytecodeOffset(), wasm::SymbolicAddress::UDivI64, - mozilla::Some(tlsOffset)); + mozilla::Nothing()); } // output in edx:eax, move to output register. masm.movl(edx, output.high); MOZ_ASSERT(eax == output.low); - - masm.Pop(WasmTlsReg); } void CodeGenerator::visitWasmSelectI64(LWasmSelectI64* lir) { diff --git a/js/src/jit/x86/LIR-x86.h b/js/src/jit/x86/LIR-x86.h index 25cdccae65db..5c3075fc8f73 100644 --- a/js/src/jit/x86/LIR-x86.h +++ b/js/src/jit/x86/LIR-x86.h @@ -86,87 +86,87 @@ class LWasmUint32ToFloat32 : public LInstructionHelper<1, 1, 1> { }; class LDivOrModI64 - : public LCallInstructionHelper { + : public LCallInstructionHelper { public: LIR_HEADER(DivOrModI64) static const size_t Lhs = 0; static const size_t Rhs = INT64_PIECES; - static const size_t Tls = 2 * INT64_PIECES; LDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs, - const LAllocation& tls) + const LDefinition& temp) : LCallInstructionHelper(classOpcode) { setInt64Operand(Lhs, lhs); setInt64Operand(Rhs, rhs); - setOperand(Tls, tls); + setTemp(0, temp); } - MDefinition* mir() const { - MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); - return mir_; + MBinaryArithInstruction* mir() const { + MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); + return static_cast(mir_); } bool canBeDivideByZero() const { - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->canBeDivideByZero(); + if (mir_->isMod()) { + return mir_->toMod()->canBeDivideByZero(); } - return mir_->toWasmBuiltinDivI64()->canBeDivideByZero(); + return mir_->toDiv()->canBeDivideByZero(); } bool canBeNegativeOverflow() const { - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->canBeNegativeDividend(); + if (mir_->isMod()) { + return mir_->toMod()->canBeNegativeDividend(); } - return mir_->toWasmBuiltinDivI64()->canBeNegativeOverflow(); + return mir_->toDiv()->canBeNegativeOverflow(); } wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->bytecodeOffset(); + MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); + if (mir_->isMod()) { + return mir_->toMod()->bytecodeOffset(); } - return mir_->toWasmBuiltinDivI64()->bytecodeOffset(); + return mir_->toDiv()->bytecodeOffset(); } + const LDefinition* temp() { return getTemp(0); } }; class LUDivOrModI64 - : public LCallInstructionHelper { + : public LCallInstructionHelper { public: LIR_HEADER(UDivOrModI64) static const size_t Lhs = 0; static const size_t Rhs = INT64_PIECES; - static const size_t Tls = 2 * INT64_PIECES; LUDivOrModI64(const LInt64Allocation& lhs, const LInt64Allocation& rhs, - const LAllocation& tls) + const LDefinition& temp) : LCallInstructionHelper(classOpcode) { setInt64Operand(Lhs, lhs); setInt64Operand(Rhs, rhs); - setOperand(Tls, tls); + setTemp(0, temp); } - MDefinition* mir() const { - MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); - return mir_; + MBinaryArithInstruction* mir() const { + MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); + return static_cast(mir_); } bool canBeDivideByZero() const { - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->canBeDivideByZero(); + if (mir_->isMod()) { + return mir_->toMod()->canBeDivideByZero(); } - return mir_->toWasmBuiltinDivI64()->canBeDivideByZero(); + return mir_->toDiv()->canBeDivideByZero(); } bool canBeNegativeOverflow() const { - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->canBeNegativeDividend(); + if (mir_->isMod()) { + return mir_->toMod()->canBeNegativeDividend(); } - return mir_->toWasmBuiltinDivI64()->canBeNegativeOverflow(); + return mir_->toDiv()->canBeNegativeOverflow(); } wasm::BytecodeOffset bytecodeOffset() const { - MOZ_ASSERT(mir_->isWasmBuiltinDivI64() || mir_->isWasmBuiltinModI64()); - if (mir_->isWasmBuiltinModI64()) { - return mir_->toWasmBuiltinModI64()->bytecodeOffset(); + MOZ_ASSERT(mir_->isDiv() || mir_->isMod()); + if (mir_->isMod()) { + return mir_->toMod()->bytecodeOffset(); } - return mir_->toWasmBuiltinDivI64()->bytecodeOffset(); + return mir_->toDiv()->bytecodeOffset(); } + const LDefinition* temp() { return getTemp(0); } }; class LWasmTruncateToInt64 : public LInstructionHelper { diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index 23379731836d..60e4ae4baed0 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -586,69 +586,41 @@ void LIRGenerator::visitWasmAtomicBinopHeap(MWasmAtomicBinopHeap* ins) { } void LIRGeneratorX86::lowerDivI64(MDiv* div) { - MOZ_CRASH("We use MWasmBuiltinModI64 instead."); -} - -void LIRGeneratorX86::lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div) { - MDefinition* lhs = div->lhs(); - MDefinition* rhs = div->rhs(); - MOZ_ASSERT(lhs->type() == rhs->type()); - MOZ_ASSERT(IsNumberType(div->type())); - - MOZ_ASSERT(div->type() == MIRType::Int64); - MOZ_ASSERT(div->type() == MIRType::Int64); - if (div->isUnsigned()) { - LUDivOrModI64* lir = new (alloc()) - LUDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), - useFixedAtStart(div->tls(), WasmTlsReg)); - defineReturn(lir, div); + lowerUDivI64(div); return; } - LDivOrModI64* lir = new (alloc()) - LDivOrModI64(useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), - useFixedAtStart(div->tls(), WasmTlsReg)); + LDivOrModI64* lir = new (alloc()) LDivOrModI64( + useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); defineReturn(lir, div); } void LIRGeneratorX86::lowerModI64(MMod* mod) { - MOZ_CRASH("We use MWasmBuiltinModI64 instead."); -} - -void LIRGeneratorX86::lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod) { - MDefinition* lhs = mod->lhs(); - MDefinition* rhs = mod->rhs(); - MOZ_ASSERT(lhs->type() == rhs->type()); - MOZ_ASSERT(IsNumberType(mod->type())); - - MOZ_ASSERT(mod->type() == MIRType::Int64); - MOZ_ASSERT(mod->type() == MIRType::Int64); - if (mod->isUnsigned()) { - LUDivOrModI64* lir = new (alloc()) - LUDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), - useFixedAtStart(mod->tls(), WasmTlsReg)); - defineReturn(lir, mod); + lowerUModI64(mod); return; } - LDivOrModI64* lir = new (alloc()) - LDivOrModI64(useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), - useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), - useFixedAtStart(mod->tls(), WasmTlsReg)); + LDivOrModI64* lir = new (alloc()) LDivOrModI64( + useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); defineReturn(lir, mod); } void LIRGeneratorX86::lowerUDivI64(MDiv* div) { - MOZ_CRASH("We use MWasmBuiltinDivI64 instead."); + LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( + useInt64FixedAtStart(div->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(div->rhs(), Register64(ecx, edx)), tempFixed(esi)); + defineReturn(lir, div); } void LIRGeneratorX86::lowerUModI64(MMod* mod) { - MOZ_CRASH("We use MWasmBuiltinModI64 instead."); + LUDivOrModI64* lir = new (alloc()) LUDivOrModI64( + useInt64FixedAtStart(mod->lhs(), Register64(eax, ebx)), + useInt64FixedAtStart(mod->rhs(), Register64(ecx, edx)), tempFixed(esi)); + defineReturn(lir, mod); } void LIRGenerator::visitSubstr(MSubstr* ins) { diff --git a/js/src/jit/x86/Lowering-x86.h b/js/src/jit/x86/Lowering-x86.h index 6f18ad9d7f5c..2e00dd6fd4aa 100644 --- a/js/src/jit/x86/Lowering-x86.h +++ b/js/src/jit/x86/Lowering-x86.h @@ -49,9 +49,7 @@ class LIRGeneratorX86 : public LIRGeneratorX86Shared { MDefinition* rhs); void lowerDivI64(MDiv* div); - void lowerWasmBuiltinDivI64(MWasmBuiltinDivI64* div); void lowerModI64(MMod* mod); - void lowerWasmBuiltinModI64(MWasmBuiltinModI64* mod); void lowerUDivI64(MDiv* div); void lowerUModI64(MMod* mod); diff --git a/js/src/wasm/WasmIonCompile.cpp b/js/src/wasm/WasmIonCompile.cpp index db55615a67ad..d4dc40832bd5 100644 --- a/js/src/wasm/WasmIonCompile.cpp +++ b/js/src/wasm/WasmIonCompile.cpp @@ -460,19 +460,6 @@ class FunctionCompiler { curBlock_->add(rhs2); rhs = rhs2; } - - // For x86 we implement i64 div via c++ runtime. - // A call to c++ runtime requires tls pointer. -#ifdef JS_CODEGEN_X86 - if (type == MIRType::Int64) { - auto* ins = - MWasmBuiltinDivI64::New(alloc(), lhs, rhs, tlsPointer_, unsignd, - trapOnError, bytecodeOffset()); - curBlock_->add(ins); - return ins; - } -#endif - auto* ins = MDiv::New(alloc(), lhs, rhs, type, unsignd, trapOnError, bytecodeOffset(), mustPreserveNaN(type)); curBlock_->add(ins); @@ -494,19 +481,6 @@ class FunctionCompiler { curBlock_->add(rhs2); rhs = rhs2; } - - // This is because x86 codegen calls runtime via BuiltinThunk and so it - // needs WasmTlsReg to be live. -#ifdef JS_CODEGEN_X86 - if (type == MIRType::Int64) { - auto* ins = - MWasmBuiltinModI64::New(alloc(), lhs, rhs, tlsPointer_, unsignd, - trapOnError, bytecodeOffset()); - curBlock_->add(ins); - return ins; - } -#endif - auto* ins = MMod::New(alloc(), lhs, rhs, type, unsignd, trapOnError, bytecodeOffset()); curBlock_->add(ins); From 2b308938cedcd649d0bccbbf6b75cae4a2e79563 Mon Sep 17 00:00:00 2001 From: Ryan Hunt Date: Thu, 10 Sep 2020 06:08:45 +0000 Subject: [PATCH 09/26] Bug 1663862 - Check OOM from wasm::ToString(). r=lth wasm::ToCString() couldn't OOM, but wasm::ToString() can, so the all the use cases need to check for OOM and propagate it. Differential Revision: https://phabricator.services.mozilla.com/D89671 --- js/src/wasm/WasmJS.cpp | 8 ++++++++ js/src/wasm/WasmOpIter.h | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/js/src/wasm/WasmJS.cpp b/js/src/wasm/WasmJS.cpp index 830204ed11e0..252f8215a4d2 100644 --- a/js/src/wasm/WasmJS.cpp +++ b/js/src/wasm/WasmJS.cpp @@ -1151,6 +1151,10 @@ static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) { } UniqueChars argStr = ToString(arg); + if (!argStr) { + return nullptr; + } + if (!buf.append(argStr.get(), strlen(argStr.get()))) { return nullptr; } @@ -1169,6 +1173,10 @@ static JSString* FuncTypeToString(JSContext* cx, const FuncType& funcType) { } UniqueChars resultStr = ToString(result); + if (!resultStr) { + return nullptr; + } + if (!buf.append(resultStr.get(), strlen(resultStr.get()))) { return nullptr; } diff --git a/js/src/wasm/WasmOpIter.h b/js/src/wasm/WasmOpIter.h index 7253e157a969..aa5551f29bb9 100644 --- a/js/src/wasm/WasmOpIter.h +++ b/js/src/wasm/WasmOpIter.h @@ -570,7 +570,15 @@ inline bool OpIter::checkIsSubtypeOf(ValType actual, ValType expected) { } UniqueChars actualText = ToString(actual); + if (!actualText) { + return false; + } + UniqueChars expectedText = ToString(expected); + if (!expectedText) { + return false; + } + UniqueChars error( JS_smprintf("type mismatch: expression has type %s but expected %s", actualText.get(), expectedText.get())); @@ -691,6 +699,10 @@ inline bool OpIter::popWithRefType(Value* value) { } UniqueChars actualText = ToString(stackType.valType()); + if (!actualText) { + return false; + } + UniqueChars error(JS_smprintf( "type mismatch: expression has type %s but expected a reference type", actualText.get())); From 62f7f8390fd4af9648b7e9abc5b055379bd772f4 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 9 Sep 2020 15:37:20 +0000 Subject: [PATCH 10/26] Bug 1663916 - mdn to in-tree: Import some Windows & Debugging info r=firefox-build-system-reviewers,rstewart Differential Revision: https://phabricator.services.mozilla.com/D89592 --- .../debugging/capturing_minidump.rst | 95 +++++++ docs/contributing/debugging/img/crashlist.jpg | Bin 0 -> 77200 bytes docs/contributing/debugging/img/reporter.jpg | Bin 0 -> 35259 bytes .../debugging/process_dump_task_manager.rst | 69 ++++++ .../debugging/stacktrace_report.rst | 152 ++++++++++++ .../debugging/stacktrace_windbg.rst | 232 ++++++++++++++++++ 6 files changed, 548 insertions(+) create mode 100644 docs/contributing/debugging/capturing_minidump.rst create mode 100644 docs/contributing/debugging/img/crashlist.jpg create mode 100644 docs/contributing/debugging/img/reporter.jpg create mode 100644 docs/contributing/debugging/process_dump_task_manager.rst create mode 100644 docs/contributing/debugging/stacktrace_report.rst create mode 100644 docs/contributing/debugging/stacktrace_windbg.rst diff --git a/docs/contributing/debugging/capturing_minidump.rst b/docs/contributing/debugging/capturing_minidump.rst new file mode 100644 index 000000000000..bd3d089e9f66 --- /dev/null +++ b/docs/contributing/debugging/capturing_minidump.rst @@ -0,0 +1,95 @@ +Capturing a minidump +==================== + +*Minidumps* are files created by various Windows tools which record the +complete state of a program as it's running, or as it was at the moment +of a crash. Small minidumps are created by the Breakpad :ref:`crash +reporting ` tool, but sometimes that's not +sufficient to diagnose a problem. For example, if the application is +hanging (not responding to input, but hasn't crashed) then Breakpad is +not triggered, and it can be difficult to determine where the problem +lies. Sometimes a more complete form of minidump is needed to see +additional details about a crash, in which case manual capture of a +minidump is desired. + +This page describes how to capture these minidumps on Windows, to permit +better debugging. + + +Privacy and minidumps +--------------------- + +.. warning:: + + **Warning!** Unlike the minidumps submitted by Breakpad, these + minidumps contain the **complete** contents of program memory. They + are therefore much more likely to contain private information, if + there is any in the browser. For this reason, you may prefer to + generate minidumps against a `clean + profile `__ + where possible. + + +Capturing a minidump: application crash +--------------------------------------- + +To capture a full minidump for an application crash, you can use a tool +called windbg. + + +Install debugging tools for Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Microsoft distributes the Debugging Tools for Windows for free, those +include WinDbg which you will need here. Download it from `Install +Debugging Tools for +Windows `__. +(*You'll want the 32-bit version of WinDbg only if you are using a +32*-bit version of Firefox) Then install it, the standard settings in +the installation process are fine. + + +Capture a minidump +~~~~~~~~~~~~~~~~~~ + +#. Connect Firefox to the debugger. + + a. If Firefox is not already running, then open WinDbg from the Start + menu (Start->All Programs->Debugging Tools for Windows->WinDbg). + Next, open the **"File"** menu and choose **"Open + Executable..."**. In the file chooser window that appears, open + the firefox.exe executable in your Firefox program folder + (C:\Program Files\Mozilla Firefox). + + b. If Firefox is already running, open WinDbg from the Start menu + (Start->All Programs->Debugging Tools for Windows->WinDbg). Next, + open the **"File"** menu and choose **"Attach to a Process..."**. + In the file chooser window that appears, find the firefox.exe + executable process with the lowest PID. + +#. You should now see a "Command" text window with debug output at the + top and an input box at the bottom. From the menu, select + ``Debug → Go``, and Firefox should start. If the debugger spits out + some text right away and Firefox doesn't come up, select + ``Debug → Go`` again. + +#. When the program is about to crash, WinDbg will spit out more data, + and the prompt at the bottom will change from saying "``*BUSY*``" to + having a number in it. At this point, you should type + "``.dump /ma c:\temp\firefoxcrash.dmp``" -- without the quotes, but + don't forget the dot at the beginning. Once it completes, which can + take a fair while, you will have a very large file at + ``c:\temp\firefoxcrash.dmp`` that can be used to help debug your + problem. File size will depend on this size of Firefox running in + your environment, which could several GB. + +#. Ask in the relevant bug or thread how best to share this very large + file! + + +Capturing a minidump: application hang +-------------------------------------- + +On Windows Vista and Windows 7, you can follow `these +instructions `__ to capture a +dump file and locate it after it's been saved. diff --git a/docs/contributing/debugging/img/crashlist.jpg b/docs/contributing/debugging/img/crashlist.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4cb376d0f25e3c239bdfa165ab7f65348992e711 GIT binary patch literal 77200 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

QoLu<=Lp2Apr2kqyYMh_XWH#Kjs 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 literal 0 HcmV?d00001 diff --git a/docs/contributing/debugging/img/reporter.jpg b/docs/contributing/debugging/img/reporter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0a62cda4e1a1ab8d3644203f19046105b19b02df GIT binary patch literal 35259 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. You + can use any `official nightly + build `__ or released + version of Firefox from Mozilla. You can find the latest trunk + nightly builds under + `http://ftp.mozilla.org/pub/mozilla.o.../latest-trunk/ `__. + + +Creating the Dump File +---------------------- + +Ensure that Firefox is not already running. + + +Run Firefox, reproduce the hang +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Start Firefox and perform whatever steps are necessary to cause Firefox +to hang. Once the browser hangs, continue with the steps below. + + +After the hang +~~~~~~~~~~~~~~ + +#. Open Windows Task Manager (CTRL+SHIFT+ESC). +#. Find Firefox.exe among the list of processes. +#. Right-click Firefox.exe and select "Create dump file". Task manager + should indicate where the dump file was written to. + + +See also +-------- + +- :ref:`How to get a stacktrace for a bug report ` +- `How to create a user-mode process dump file in Windows Vista and in + Windows 7 + (MSDN) `__ diff --git a/docs/contributing/debugging/stacktrace_report.rst b/docs/contributing/debugging/stacktrace_report.rst new file mode 100644 index 000000000000..f2f6f5219496 --- /dev/null +++ b/docs/contributing/debugging/stacktrace_report.rst @@ -0,0 +1,152 @@ +How to get a stacktrace for a bug report +======================================== + +If you file a bug report in Bugzilla about a crash you should include a +stacktrace (call stack) in your report. A stacktrace will tell Mozilla +developers what crashed and provide a starting point for investigating +its cause. This article describes how to use the Mozilla Crash Reporter +(Breakpad) to get a crash ID, which our engineers can use to get a +stacktrace, and alternative ways to get a stacktrace if you can't get a +crash ID. + +Requirements +------------ + +You need a binary build of Firefox from +`Mozilla.org `__. SeaMonkey and +Thunderbird also support crash reporting. + +Mozilla's crash report server currently only has debug information for +Mozilla builds and thus the crash reporter cannot work if you use a +build from a Linux distribution or if you compile from source code. In +these cases you will need to use one of the :ref:`alternative +methods ` listed below. + +.. note:: + + **Note:** When filing a crash report, it is important to know whether + the crash occurs with `Firefox safe + mode `__. This helps + engineers determine whether a particular + `extension `__ + or + `plugin `__ + is the cause of the crash. + + +How to get a crash ID with the Mozilla Crash Reporter +----------------------------------------------------- + +1 - Crash and submit a report to the system. + +.. image:: img/reporter.jpg + +The Mozilla Crash Reporter window should automatically come up after Firefox crashes. +If you have any additional information about the crash, such as additional detail on +what you were doing at the time that may have triggered the crash, please enter it +into the comments box. Be sure that you **check the "Tell Mozilla about this crash"** +checkbox and click the restart button. The crash reporter should now submit the +crash report and Firefox should open again. + +.. note:: + + The "Details" button gives additional data about the incident, + however this is not useful in a bug report. + + +2 - Tell us the ID of the report you submitted. + +.. image:: img/crashlist.jpg + +To access all of your submitted reports type "about:crashes" into the Firefox address bar +and press enter. Firefox should open a list of IDs for your submitted crash reports. +Copy two or three of the IDs for the appropriate crashes and paste them into your +Bugzilla report. Please check the listed times to avoid copying the ID of an unrelated +crash report. + +.. note:: + + You can prefix a "bp-" to the beginning of an ID to make Bugzilla turn it + into a link: bp-a70759c6-1295-4160-aa30-bc4772090918 + + +How to get the crash ID if Firefox crashes on startup +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If Firefox crashes on startup you can still access your submitted crash +reports. Crash reports are accessible from all Firefox profiles, so if a +`new +profile `__ +does not crash you can use it to access them through "about:crashes" as above. + + +Accessing crash report IDs outside of Firefox +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If you cannot load Firefox at all you can find the crash report files at +this location depending on your operating system: + +* Windows : ``%APPDATA%\Mozilla\Firefox\Crash Reports\submitted\`` +* OS X : ``~/Library/Application Support/Firefox/Crash Reports/submitted/`` +* Linux : ``~/.mozilla/firefox/Crash Reports/submitted/`` + +Each file in this folder contains one submitted crash report ID. You can +check the modified or creation time for each file to discern which crash +reports are relevant to your bug report. + + +Alternative ways to get a stacktrace +------------------------------------ + +If the Mozilla crash reporter doesn't come up or isn't available you +will need to obtain a stacktrace manually: + + +Windows +~~~~~~~ + +See the article :ref:`Create a stacktrace with Windbg` for information +on how to do this. + +For a full process dump, see :ref:`How to get a process dump with Windows +Task Manager`. + + +OS X +~~~~ + +Run /Applications/Utilities/Console.app. Expand "~/Library/Logs" and +"CrashReporter", then look for logs for "firefox-bin". + + +Linux +~~~~~ + +Note that for most distros, the package you need to get symbols for will +be something like "xulrunner", not "firefox". + + +Crash reports files on your computer +------------------------------------ + +When Breakpad initially catches a crash it first writes crash report +files (e.g. .dump and .extra files) into the 'pending' subdirectory of +its 'Crash Reports' directory. + +If Breakpad successfully sends the crash report to the reporting server +then, by default, the files added to the 'pending' subdirectory for the +crash are removed, and a .txt file is placed in the 'submitted' +directory containing the crash ID created by the reporting server. + +If you want Breakpad to leave the .dump and .extra files on your +computer so that you can examine them locally, then set the +MOZ_CRASHREPORTER_NO_DELETE_DUMP environment variable to 1. + +- Ubuntu: `Instructions from the Ubuntu + Team `__ +- openSUSE: `General instructions from + openSUSE `__ +- Fedora: `Capturing Stack + Traces `__ +- Gentoo: `Debugging using + GDB `__ diff --git a/docs/contributing/debugging/stacktrace_windbg.rst b/docs/contributing/debugging/stacktrace_windbg.rst new file mode 100644 index 000000000000..a0d2abfc2557 --- /dev/null +++ b/docs/contributing/debugging/stacktrace_windbg.rst @@ -0,0 +1,232 @@ +How to get a stacktrace with WinDbg +=================================== + ++--------------------------------------------------------------------+ +| This page is an import from MDN and the contents might be outdated | ++--------------------------------------------------------------------+ + +Introduction +------------ + +Sometimes you need to get a stacktrace (call stack) for a crash or hang +but `Breakpad `__ fails because it's +a special crash or a hang. This article describes how to get a +stacktrace in those cases with WinDbg on Windows. (To get a stacktrace +for Thunderbird or some other product, substitute the product name where +ever you see Firefox in this instructions.) + +Requirements +------------ + +To get such a stacktrace you need to install the following software: + +Debugging Tools for Windows +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Microsoft distributes the Debugging Tools for Windows for free, those +include WinDbg which you will need here. Download it from `Install +Debugging Tools for +Windows `__. +(*You'll want the 32-bit version*, even if you are using a 64-bit +version of Windows) Then install it, the standard settings in the +installation process are fine. + +A Firefox nightly or release +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You need a Firefox version for which symbols are availables from the +:ref:`symbol server ` to use +with WinDbg. You can use any `official nightly +build `__ or released +version of Firefox from Mozilla. You can find the latest trunk nightly +builds under +`http://ftp.mozilla.org/pub/mozilla.o.../latest-trunk/ `__. + + +Debugging +--------- + +To begin debugging, ensure that Firefox is not already running and open +WinDbg from the Start menu. (Start->All Programs->Debugging Tools for +Windows->WinDbg) Next, open the **"File"** menu and choose **"Open +Executable..."**. In the file chooser window that appears, open the +firefox.exe executable in your Firefox program folder (C:\Program +Files\Mozilla Firefox). + +You should now see a "Command" text window with debug output at the top +and an input box at the bottom. Before debugging can start, several +commands must be entered into the one-line input box at the bottom of +the Command window. + +.. note:: + + Tip: All commands must be entered exactly as written, one line at a + time, into the bottom of the Command box. + + - Copying and pasting each line is the easiest method to avoid + mistakes + - Some commands start with a period (.) or a pipe character (|), + which is required. (The keystroke for a pipe character on US + keyboards is SHIFT+\) + - Submit the log file on a bug or via the support site, even if + nothing seems to happen during the debug process + + +Start debugging +~~~~~~~~~~~~~~~ + +Now that Firefox is opened in the debugger, you need to configure your +WinDbg to download symbols from the Mozilla symbol server. To load the +symbols, enter the three commands below, pressing enter after each one. +(More details are available at :ref:`symbol server `.) + +:: + + .sympath SRV*c:\symbols*http://symbols.mozilla.org/firefox;SRV*c:\symbols*http://msdl.microsoft.com/download/symbols + .symfix+ c:\symbols + .reload /f + +Now wait for the symbols to download. This may take some time depending +on your connection speed; the total size of the Mozilla and Microsoft +symbols download is around 1.4GB. WinDbg will show "Busy" at the bottom +of the application window until the download is complete. + +Once the download is complete, you need to configure WinDbg to examine +child processes, ignore a specific event caused by Flash Player, and +record a log of loaded modules. You will also want to open a log file to +save data you collect. To do this, enter these four commands, pressing +enter after each one. + +:: + + .logopen /t c:\temp\firefox-debug.log + .childdbg 1 + .tlist + sxn gp + lm + +If you see firefox.exe listed in the output from .tlist more than once, +then you are already running the application and need to close the +running instance first before you start debugging, otherwise you won't +get useful results. + +Now run Firefox by opening the **Debug** menu and clicking **Go**. +**While Firefox is running, you will not be able to type any commands +into the debugger.** After it starts, try to reproduce the crash or +hanging issue that you are seeing. + +.. note:: + + If Firefox fails to start, and you see lines of text followed by a + command prompt in the debugger, a "breakpoint" may have been + triggered. If you are prompted for a command but don't see an error + about a crash, go back to the **Debug** menu and press **Go**. + +Once the browser crashes, you will see an error (such as "Access +violation") in the WinDbg Command window. If Firefox hangs and there is +no command prompt available in the debugger, open the **Debug** menu and +choose **Break.** Once the browser has crashed or been stopped, continue +with the steps below. + + +After the crash or hang +~~~~~~~~~~~~~~~~~~~~~~~ + +You need to capture the debug information to include in a bug comment or +support request. Enter these three commands, one at a time, to get the +stacktrace, crash/hang analysis and log of loaded modules. (Again, press +Enter after each command.) + +:: + + ~* kp + !analyze -v -f + lm + +After these steps are completed, find the file +**c:\temp\firefox-debug-(Today's Date).txt** on your hard drive. To +provide the information to the development community, submit this file +with a `support request `__ or attach it +to a related bug on `Bugzilla `__. + + +Producing a minidump +~~~~~~~~~~~~~~~~~~~~ + +Sometimes the stacktrace alone is not enough information for a developer +to figure out what went wrong. A developer may ask you for a "minidump" +or a "full memory dump", which are files containing more information +about the process. :ref:`You can easily produce minidumps from WinDBG and +provide them to developers `. + +FAQ + +Q: I am running Windows 7 (32-bit or 64-bit) and I see an exception in +the WinDbg command window that says 'ntdll32!LdrpDoDebuggerBreak+0x2c' +or 'ntdll32!LdrpDoDebuggerBreak+0x30'. What do I do now? + +A: If you see 'int 3' after either of those exceptions, you will need to +execute the following commands in WinDbg. + +:: + + bp ntdll!LdrpDoDebuggerBreak+0x30 + bp ntdll!LdrpDoDebuggerBreak+0x2c + eb ntdll!LdrpDoDebuggerBreak+0x30 0x90 + eb ntdll!LdrpDoDebuggerBreak+0x2c 0x90 + +| Make sure you enter them one at a time and press enter after each one. + If you use the 64-bit version of Windows, you need to replace "ntdll" + in these commands with "ntdll32". +| Q: The first four frames of my stack trace look like this: + +:: + + 0012fe20 7c90e89a ntdll!KiFastSystemCallRet + 0012fe24 7c81cd96 ntdll!ZwTerminateProcess+0xc + 0012ff20 7c81cdee kernel32!_ExitProcess+0x62 + + 0012ff34 6000179e kernel32!ExitProcess+0x14 + +This looks wrong to me?! + +A: You ran the application without the "Debug child processes also" +check box being checked. You need to detach the debugger and open the +application again, this time with the check box being checked. + +Q: WinDbg tells me that it is unable to verify checksum for firefox.exe. +Is this normal? + +A: Yes, this is normal and can be ignored. + +Q: Should I click yes or no when WinDbg asks me to "Save information for +workspace?" + +A: Click yes and WinDbg will save you from having to enter in the symbol +location for Firefox.exe in the future. Click no if you'd rather not +having WinDbg save this information. + +Q: I'm seeing "wow64" on top of each thread, is that ok ? + +A: No, you are running a 64 bit version of Windbg and trying to debug a +32 bit version of the mozilla software. Redownload and install the 32 +bit version of windbg. + + +Troubleshooting: Symbols will not download +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If symbols will not download no matter what you do, the problem may be +that Internet Explorer has been set to the **Work Offline** mode. You +will not receive any warnings of this in Windbg, Visual C++ or Visual +Studio. Even using the command line with symchk.exe to download symbols +will fail. This is because Microsoft uses Internet Explorer's internet & +proxy settings to download the symbol files. Check the File menu of +Internet Explorer to ensure "Work Offline" is unchecked. + + +See also +-------- + +- :ref:`symbol server ` Maps addresses to human readable strings. +- :ref:`source server ` Maps addresses to source code lines From fb75df0e5cc1c3278997bcc69e976e236cb7cf19 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 9 Sep 2020 15:13:07 +0000 Subject: [PATCH 11/26] Bug 1663916 - mdn to in-tree: Update of the mdn import doc r=dmajor Differential Revision: https://phabricator.services.mozilla.com/D89594 --- tools/moztreedocs/docs/mdn-import.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/moztreedocs/docs/mdn-import.rst b/tools/moztreedocs/docs/mdn-import.rst index 05cda98796f3..9de78b6213fa 100644 --- a/tools/moztreedocs/docs/mdn-import.rst +++ b/tools/moztreedocs/docs/mdn-import.rst @@ -20,3 +20,9 @@ to the firefox source docs. 4. Verify the rst syntax using `./mach lint -l rst`_ .. _./mach lint -l rst: /tools/lint/linters/rstlinter.html + +5. If relevant, remove unbreakable spaces (rendered with a "!" on Phabricator) + +.. code-block:: shell + + $ sed -i -e 's/\xc2\xa0/ /g' doc.rst From 4f871039e0e468bf6d710862e8986ee64bee2a58 Mon Sep 17 00:00:00 2001 From: Frederik Braun Date: Tue, 8 Sep 2020 13:54:01 +0000 Subject: [PATCH 12/26] Bug 1652481: expose experimental sanitizer api r=ckerschb,hsivonen Differential Revision: https://phabricator.services.mozilla.com/D82347 --- .../en-US/chrome/security/security.properties | 6 + dom/security/moz.build | 2 +- dom/security/sanitizer/Sanitizer.cpp | 203 ++++++++++++++++++ dom/security/sanitizer/Sanitizer.h | 101 +++++++++ dom/security/sanitizer/moz.build | 37 ++++ .../sanitizer/tests/mochitest/mochitest.ini | 5 + .../tests/mochitest/test_sanitizer_api.html | 75 +++++++ .../mochitest/general/test_interfaces.js | 2 + dom/webidl/Sanitizer.webidl | 30 +++ dom/webidl/moz.build | 4 + modules/libpref/init/StaticPrefList.yaml | 6 + 11 files changed, 470 insertions(+), 1 deletion(-) create mode 100644 dom/security/sanitizer/Sanitizer.cpp create mode 100644 dom/security/sanitizer/Sanitizer.h create mode 100644 dom/security/sanitizer/moz.build create mode 100644 dom/security/sanitizer/tests/mochitest/mochitest.ini create mode 100644 dom/security/sanitizer/tests/mochitest/test_sanitizer_api.html create mode 100644 dom/webidl/Sanitizer.webidl diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties index 552a1ee44db7..b9c35c5dd49b 100644 --- a/dom/locales/en-US/chrome/security/security.properties +++ b/dom/locales/en-US/chrome/security/security.properties @@ -141,3 +141,9 @@ HTTPSOnlyFailedRequest = Upgrading insecure request “%1$S” failed. (%2$S) # LOCALIZATION NOTE: %S is the URL of the blocked request; IframeSandboxBlockedDownload = Download of “%S” was blocked because the triggering iframe has the sandbox flag set. + +# Sanitizer API +# LOCALIZATION NOTE: Please do not localize "DocumentFragment". It's the name of an API. +SanitizerRcvdNoInput = Received empty or no input. Returning an empty DocumentFragment. +# LOCALIZATION NOTE: "Sanitizer" is the name of the API. Please do not localize. +SanitizerOptionsDiscarded = Options for the Sanitizer constructor are not yet supported. Please note this is experimental behavior. diff --git a/dom/security/moz.build b/dom/security/moz.build index 624ddd7f0c85..fc73cbf48591 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -9,7 +9,7 @@ with Files('*'): TEST_DIRS += ['test'] -DIRS += [ 'featurepolicy' ] +DIRS += [ 'featurepolicy', 'sanitizer' ] EXPORTS.mozilla.dom += [ 'CSPEvalChecker.h', diff --git a/dom/security/sanitizer/Sanitizer.cpp b/dom/security/sanitizer/Sanitizer.cpp new file mode 100644 index 000000000000..192ee8711118 --- /dev/null +++ b/dom/security/sanitizer/Sanitizer.cpp @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BindingDeclarations.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/DocumentFragment.h" +#include "mozilla/dom/SanitizerBinding.h" +#include "nsContentUtils.h" +#include "nsGenericHTMLElement.h" +#include "nsTreeSanitizer.h" +#include "Sanitizer.h" + +namespace mozilla { +namespace dom { + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Sanitizer, mGlobal) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(Sanitizer) +NS_IMPL_CYCLE_COLLECTING_RELEASE(Sanitizer) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Sanitizer) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +JSObject* Sanitizer::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return Sanitizer_Binding::Wrap(aCx, this, aGivenProto); +} + +/* static */ +already_AddRefed Sanitizer::Constructor( + const GlobalObject& aGlobal, const SanitizerOptions& aOptions, + ErrorResult& aRv) { + // Note: Later, aOptions will be interpreted and stored as a member. + // We'll just ignore it for now. + nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); + RefPtr sanitizer = new Sanitizer(global); + AutoTArray params = {}; + sanitizer->LogLocalizedString("SanitizerOptionsDiscarded", params, + nsIScriptError::infoFlag); + return sanitizer.forget(); +} + +/* static */ +already_AddRefed Sanitizer::InputToNewFragment( + const Optional& aInput, + ErrorResult& aRv) { + // turns an StringOrDocumentFragmentOrDocument into a DocumentFragment for + // internal use with nsTreeSanitizer + + nsCOMPtr window = do_QueryInterface(mGlobal); + if (!window || !window->GetDoc()) { + // FIXME: Should we throw another exception? + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (!aInput.WasPassed()) { + AutoTArray params = {}; + LogLocalizedString("SanitizerRcvdNoInput", params, + nsIScriptError::warningFlag); + + RefPtr emptyFragment = + window->GetDoc()->CreateDocumentFragment(); + return emptyFragment.forget(); + } + // We need to create a new docfragment based on the input + // and can't use a live document (possibly with mutation observershandlers) + nsAutoString innerHTML; + if (aInput.Value().IsDocumentFragment()) { + RefPtr inFragment = + &aInput.Value().GetAsDocumentFragment(); + inFragment->GetInnerHTML(innerHTML); + } else if (aInput.Value().IsString()) { + innerHTML.Assign(aInput.Value().GetAsString()); + } else if (aInput.Value().IsDocument()) { + RefPtr doc = &aInput.Value().GetAsDocument(); + nsCOMPtr docElement = doc->GetDocumentElement(); + + docElement->GetInnerHTML(innerHTML, IgnoreErrors()); + } + if (innerHTML.IsEmpty()) { + AutoTArray params = {}; + LogLocalizedString("SanitizerRcvdNoInput", params, + nsIScriptError::warningFlag); + + RefPtr emptyFragment = + window->GetDoc()->CreateDocumentFragment(); + return emptyFragment.forget(); + } + // We don't have a context element yet. let's create a mock HTML body element + RefPtr info = + window->GetDoc()->NodeInfoManager()->GetNodeInfo( + nsGkAtoms::body, nullptr, kNameSpaceID_XHTML, nsINode::ELEMENT_NODE); + + nsCOMPtr context = NS_NewHTMLBodyElement( + info.forget(), mozilla::dom::FromParser::FROM_PARSER_FRAGMENT); + RefPtr fragment = nsContentUtils::CreateContextualFragment( + context, innerHTML, true /* aPreventScriptExecution */, aRv); + if (aRv.Failed()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + return fragment.forget(); +} + +already_AddRefed Sanitizer::Sanitize( + const Optional& aInput, + ErrorResult& aRv) { + nsCOMPtr window = do_QueryInterface(mGlobal); + if (!window || !window->GetDoc()) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + if (!aInput.WasPassed()) { + AutoTArray params = {}; + LogLocalizedString("SanitizerRcvdNoInput", params, + nsIScriptError::warningFlag); + RefPtr fragment = + window->GetDoc()->CreateDocumentFragment(); + return fragment.forget(); + } + ErrorResult error; + RefPtr fragment = + Sanitizer::InputToNewFragment(aInput, error); + if (error.Failed()) { + return fragment.forget(); + } + nsTreeSanitizer treeSanitizer(mSanitizationFlags); + + treeSanitizer.Sanitize(fragment); + return fragment.forget(); +} + +void Sanitizer::SanitizeToString( + const Optional& aInput, + nsAString& outSanitized, ErrorResult& aRv) { + outSanitized = EmptyString(); + if (!aInput.WasPassed()) { + AutoTArray params = {}; + LogLocalizedString("SanitizerRcvdNoInput", params, + nsIScriptError::warningFlag); + return; + } + ErrorResult error; + RefPtr fragment = + Sanitizer::InputToNewFragment(aInput, error); + if (error.Failed()) { + return; + } + nsTreeSanitizer treeSanitizer(mSanitizationFlags); + + treeSanitizer.Sanitize(fragment); + fragment->GetInnerHTML(outSanitized); +} + +/* ------ Logging ------ */ + +void Sanitizer::LogLocalizedString(const char* aName, + const nsTArray& aParams, + uint32_t aFlags) { + uint64_t innerWindowID = 0; + bool isPrivateBrowsing = true; + nsCOMPtr window = do_QueryInterface(mGlobal); + if (window && window->GetDoc()) { + auto* doc = window->GetDoc(); + innerWindowID = doc->InnerWindowID(); + isPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(doc); + } + nsAutoString logMsg; + nsContentUtils::FormatLocalizedString(nsContentUtils::eSECURITY_PROPERTIES, + aName, aParams, logMsg); + LogMessage(logMsg, aFlags, innerWindowID, isPrivateBrowsing); +} + +/* static */ +void Sanitizer::LogMessage(const nsAString& aMessage, uint32_t aFlags, + uint64_t aInnerWindowID, bool aFromPrivateWindow) { + // Prepending 'Sanitizer' to the outgoing console message + nsString message; + message.AppendLiteral(u"Sanitizer: "); + message.Append(aMessage); + + // Allow for easy distinction in devtools code. + nsCString category("Sanitizer"); + + if (aInnerWindowID > 0) { + // Send to content console + nsContentUtils::ReportToConsoleByWindowID(message, aFlags, category, + aInnerWindowID); + } else { + // Send to browser console + nsContentUtils::LogSimpleConsoleError( + message, category.get(), aFromPrivateWindow, + true /* from chrome context */, aFlags); + } +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/security/sanitizer/Sanitizer.h b/dom/security/sanitizer/Sanitizer.h new file mode 100644 index 000000000000..b9aa326612a6 --- /dev/null +++ b/dom/security/sanitizer/Sanitizer.h @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_Sanitizer_h +#define mozilla_dom_Sanitizer_h + +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/DocumentFragment.h" +#include "mozilla/dom/SanitizerBinding.h" +#include "nsString.h" +#include "nsIParserUtils.h" +#include "nsTreeSanitizer.h" + +class nsISupports; + +namespace mozilla { + +class ErrorResult; + +namespace dom { + +class GlobalObject; + +class Sanitizer final : public nsISupports, public nsWrapperCache { + public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Sanitizer) + + explicit Sanitizer(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) { + MOZ_ASSERT(aGlobal); + // FIXME(freddyb): Waiting for wicg-draft to evolve. Bug 1658564. + mSanitizationFlags = nsIParserUtils::SanitizerAllowStyle | + nsIParserUtils::SanitizerAllowComments; + } + + nsIGlobalObject* GetParentObject() const { return mGlobal; } + + JSObject* WrapObject(JSContext* aCx, + JS::Handle aGivenProto) override; + + /** + * Sanitizer() WebIDL constructor + * @return a new Sanitizer object, with methods as below + */ + static already_AddRefed Constructor( + const GlobalObject& aGlobal, const SanitizerOptions& aOptions, + ErrorResult& aRv); + + /** + * sanitize WebIDL method. + * @param aInput "bad" HTML that needs to be sanitized + * @return DocumentFragment of the sanitized HTML + */ + already_AddRefed Sanitize( + const Optional& aInput, + ErrorResult& aRv); + + /** + * sanitizeToString WebIDL method. + * @param aInput "bad" HTML that needs to be sanitized + * @param outSanitized out-param for the string of sanitized HTML + */ + void SanitizeToString( + const Optional& aInput, + nsAString& outSanitized, ErrorResult& aRv); + + /** + * Logs localized message to either content console or browser console + * @param aName Localization key + * @param aParams Localization parameters + * @param aFlags Logging Flag (see nsIScriptError) + */ + void LogLocalizedString(const char* aName, const nsTArray& aParams, + uint32_t aFlags); + + private: + ~Sanitizer() = default; + already_AddRefed InputToNewFragment( + const Optional& aInput, + ErrorResult& aRv); + /** + * Logs localized message to either content console or browser console + * @param aMessage Message to log + * @param aFlags Logging Flag (see nsIScriptError) + * @param aInnerWindowID Inner Window ID (Logged on browser console if 0) + * @param aFromPrivateWindow If from private window + */ + static void LogMessage(const nsAString& aMessage, uint32_t aFlags, + uint64_t aInnerWindowID, bool aFromPrivateWindow); + + SanitizerOptions mOptions; + uint32_t mSanitizationFlags; + nsCOMPtr mGlobal; +}; +} // namespace dom +} // namespace mozilla + +#endif // ifndef mozilla_dom_Sanitizer_h diff --git a/dom/security/sanitizer/moz.build b/dom/security/sanitizer/moz.build new file mode 100644 index 000000000000..ba9df4d375a3 --- /dev/null +++ b/dom/security/sanitizer/moz.build @@ -0,0 +1,37 @@ +# -*- 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/. + +with Files("**"): + BUG_COMPONENT = ('Core', 'DOM: Security') + +#TEST_DIRS += [ 'tests' ] + +MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini'] + + +EXPORTS.mozilla.dom += [ + 'Sanitizer.h', +] + +UNIFIED_SOURCES += [ + 'Sanitizer.cpp', +] + +LOCAL_INCLUDES += [ + '/dom/base', + '/dom/bindings', + '/dom/html', +] + +#include('/ipc/chromium/chromium-config.mozbuild') +#include('/tools/fuzzing/libfuzzer-config.mozbuild') + +FINAL_LIBRARY = 'xul' + +#if CONFIG['FUZZING_INTERFACES']: +# TEST_DIRS += [ +# 'fuzztest' +# ] diff --git a/dom/security/sanitizer/tests/mochitest/mochitest.ini b/dom/security/sanitizer/tests/mochitest/mochitest.ini new file mode 100644 index 000000000000..ccc8440d876f --- /dev/null +++ b/dom/security/sanitizer/tests/mochitest/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] +prefs = + dom.security.sanitizer.enabled=true +scheme=https +[test_sanitizer_api.html] diff --git a/dom/security/sanitizer/tests/mochitest/test_sanitizer_api.html b/dom/security/sanitizer/tests/mochitest/test_sanitizer_api.html new file mode 100644 index 000000000000..9c74c256a710 --- /dev/null +++ b/dom/security/sanitizer/tests/mochitest/test_sanitizer_api.html @@ -0,0 +1,75 @@ + + + + + Test sanitizer api + + + + + +

+ + + diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js index a3ec59a4a112..f2296ccd4419 100644 --- a/dom/tests/mochitest/general/test_interfaces.js +++ b/dom/tests/mochitest/general/test_interfaces.js @@ -1003,6 +1003,8 @@ var interfaceNamesInGlobalScope = [ // IMPORTANT: Do not change this list without review from a DOM peer! { name: "RTCTrackEvent", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! + { name: "Sanitizer" }, + // IMPORTANT: Do not change this list without review from a DOM peer! { name: "Screen", insecureContext: true }, // IMPORTANT: Do not change this list without review from a DOM peer! { name: "ScreenOrientation", insecureContext: true }, diff --git a/dom/webidl/Sanitizer.webidl b/dom/webidl/Sanitizer.webidl new file mode 100644 index 000000000000..46362e7d5214 --- /dev/null +++ b/dom/webidl/Sanitizer.webidl @@ -0,0 +1,30 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://wicg.github.io/purification/ + * + * * Copyright © 2020 the Contributors to the HTML Sanitizer API Specification, + * published by the Web Platform Incubator Community Group under the W3C Community Contributor License Agreement (CLA). + */ + + +typedef (DOMString or DocumentFragment or Document) SanitizerInput; + +// unimplemented during prototyping +dictionary SanitizerOptions { + sequence allowed; + sequence removed; +}; + +[Exposed=Window, SecureContext] +interface Sanitizer { + [Pref="dom.security.sanitizer.enabled", Throws] + constructor(optional SanitizerOptions options = {}); // optionality still discussed in spec + [Throws] + DocumentFragment sanitize(optional SanitizerInput input); + [Throws] + DOMString sanitizeToString(optional SanitizerInput input); +}; diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build index 13bda95c57ec..50687548e5ec 100644 --- a/dom/webidl/moz.build +++ b/dom/webidl/moz.build @@ -277,6 +277,9 @@ with Files("RTC*"): with Files("SVG*"): BUG_COMPONENT = ("Core", "SVG") +with Files("Sanitizer.webidl"): + BUG_COMPONENT = ("Core", "DOM: Security") + with Files("ScriptProcessorNode.webidl"): BUG_COMPONENT = ("Core", "Web Audio") @@ -781,6 +784,7 @@ WEBIDL_FILES = [ 'Request.webidl', 'ResizeObserver.webidl', 'Response.webidl', + 'Sanitizer.webidl', 'Screen.webidl', 'ScreenOrientation.webidl', 'ScriptProcessorNode.webidl', diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index c0cf2cacceec..238ffc74ca01 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -2649,6 +2649,12 @@ value: true mirror: always +# pref controls `Sanitizer` API being exposed +- name: dom.security.sanitizer.enabled + type: bool + value: false + mirror: always + # Is support for selection event APIs enabled? - name: dom.select_events.enabled type: bool From dfee6e2032e5c71d7cdb452284e7fed30774dcb2 Mon Sep 17 00:00:00 2001 From: Csoregi Natalia Date: Thu, 10 Sep 2020 10:59:01 +0300 Subject: [PATCH 13/26] Backed out 2 changesets (bug 1662879) for multiple failures e.g. browser_manageCreditCardsDialog.js. CLOSED TREE Backed out changeset 3952f1301e38 (bug 1662879) Backed out changeset 5daec3a7ae6f (bug 1662879) --- .../ProfileAutoCompleteResult.jsm | 21 +++++-------------- .../formautofill/content/customElements.js | 5 +---- .../formautofill/content/manageDialog.js | 15 ------------- .../browser_creditCard_dropdown_layout.js | 4 ---- 4 files changed, 6 insertions(+), 39 deletions(-) diff --git a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm index ee95015caf60..676621fc3295 100644 --- a/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm +++ b/browser/extensions/formautofill/ProfileAutoCompleteResult.jsm @@ -421,25 +421,14 @@ class CreditCardResult extends ProfileAutoCompleteResult { primaryAffix = affix; primary = label; } - const secondary = this._getSecondaryLabel( - focusedFieldName, - allFieldNames, - profile - ); - // The card type is displayed visually using an image. For a11y, we need - // to expose it as text. We do this using aria-label. However, - // aria-label overrides the text content, so we must include that also. - const ccTypeName = FormAutofillUtils.stringBundle.GetStringFromName( - `cardNetwork.${profile["cc-type"]}` - ); - const ariaLabel = [ccTypeName, primaryAffix, primary, secondary] - .filter(chunk => !!chunk) // Exclude empty chunks. - .join(" "); return { primaryAffix, primary, - secondary, - ariaLabel, + secondary: this._getSecondaryLabel( + focusedFieldName, + allFieldNames, + profile + ), }; }); // Add an empty result entry for footer. diff --git a/browser/extensions/formautofill/content/customElements.js b/browser/extensions/formautofill/content/customElements.js index a6add2317019..de2543923674 100644 --- a/browser/extensions/formautofill/content/customElements.js +++ b/browser/extensions/formautofill/content/customElements.js @@ -143,16 +143,13 @@ `url(${this.getAttribute("ac-image")})` ); - let { primaryAffix, primary, secondary, ariaLabel } = JSON.parse( + let { primaryAffix, primary, secondary } = JSON.parse( this.getAttribute("ac-value") ); this._labelAffix.textContent = primaryAffix; this._label.textContent = primary; this._comment.textContent = secondary; - if (ariaLabel) { - this.setAttribute("aria-label", ariaLabel); - } } }; diff --git a/browser/extensions/formautofill/content/manageDialog.js b/browser/extensions/formautofill/content/manageDialog.js index 1a9de8160ec3..df29cf23c472 100644 --- a/browser/extensions/formautofill/content/manageDialog.js +++ b/browser/extensions/formautofill/content/manageDialog.js @@ -449,21 +449,6 @@ class ManageCreditCards extends ManageRecords { let record = option.record; if (record && record["cc-type"]) { option.setAttribute("cc-type", record["cc-type"]); - // The card type is displayed visually using an image. For a11y, we need - // to expose it as text. We do this using aria-label. However, - // aria-label overrides the text content, so we must include that also. - // XXX Bug 1664086: We should set aria-label using Fluent. The code - // below does not react to locale changes, so aria-label will be wrong - // if the locale is changed at runtime. This hack was necessary to get - // this uplifted to 81, but should be fixed sooner rather than later. - const ccTypeName = FormAutofillUtils.stringBundle.GetStringFromName( - `cardNetwork.${record["cc-type"]}` - ); - await document.l10n.translateElements([option]); - option.setAttribute( - "aria-label", - `${ccTypeName} ${option.textContent}` - ); } else { option.removeAttribute("cc-type"); } diff --git a/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js b/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js index b6115a950890..ced45e81106c 100644 --- a/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js +++ b/browser/extensions/formautofill/test/browser/creditCard/browser_creditCard_dropdown_layout.js @@ -33,10 +33,6 @@ add_task(async function test_credit_card_dropdown() { const firstItem = getDisplayedPopupItems(browser)[0]; isnot(firstItem.getAttribute("ac-image"), "", "Should show icon"); - ok( - firstItem.getAttribute("aria-label").startsWith("Visa "), - "aria-label should start with Visa" - ); // The breakpoint of two-lines layout is 185px await reopenPopupWithResizedInput(browser, focusInput, 175); From 193ac19715b09c2f54bf705a50fa3e76a7ba4aa7 Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Wed, 9 Sep 2020 14:52:13 +0000 Subject: [PATCH 14/26] Bug 1663896 - Always wait for _watchAllTargets to resolve in DevTools ResourceWatcher r=ochameau Differential Revision: https://phabricator.services.mozilla.com/D89583 --- ...wser_inspector_fission_frame_navigation.js | 7 +- devtools/client/shared/test/shared-head.js | 24 +++++- devtools/shared/resources/resource-watcher.js | 19 +++-- devtools/shared/resources/tests/browser.ini | 1 + ...browser_resources_target_resources_race.js | 77 +++++++++++++++++++ 5 files changed, 112 insertions(+), 16 deletions(-) create mode 100644 devtools/shared/resources/tests/browser_resources_target_resources_race.js diff --git a/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js b/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js index 747a7cbeb121..d727bed1fc49 100644 --- a/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js +++ b/devtools/client/inspector/test/browser_inspector_fission_frame_navigation.js @@ -116,9 +116,12 @@ async function navigateIframeTo(inspector, url) { const { resourceWatcher, targetList } = inspector.toolbox; const onTargetProcessed = waitForTargetProcessed(targetList, url); - const onNewRoot = waitForResourceOnce( + const onNewRoot = waitForNextResource( resourceWatcher, - resourceWatcher.TYPES.ROOT_NODE + resourceWatcher.TYPES.ROOT_NODE, + { + ignoreExistingResources: true, + } ); info("Update the src attribute of the iframe tag"); diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js index 708d01afc2b0..69a00786a7d4 100644 --- a/devtools/client/shared/test/shared-head.js +++ b/devtools/client/shared/test/shared-head.js @@ -1181,18 +1181,34 @@ function getCurrentTestFilePath() { * The ResourceWatcher instance that should emit the expected resource. * @param {String} resourceType * One of ResourceWatcher.TYPES, type of the expected resource. + * @param {Object} additional options + * - {Boolean} ignoreExistingResources: ignore existing resources or not. + * - {Function} predicate: if provided, will wait until a resource makes + * predicate(resource) return true. * @return {Object} * - resource {Object} the resource itself * - targetFront {TargetFront} the target which owns the resource */ -function waitForResourceOnce(resourceWatcher, resourceType) { +function waitForNextResource( + resourceWatcher, + resourceType, + { ignoreExistingResources = false, predicate } = {} +) { + // If no predicate was provided, convert to boolean to avoid resolving for + // empty `resources` arrays. + predicate = predicate || (resource => !!resource); + return new Promise(resolve => { const onAvailable = resources => { - resolve(resources[0]); - resourceWatcher.unwatchResources([resourceType], { onAvailable }); + const matchingResource = resources.find(resource => predicate(resource)); + if (matchingResource) { + resolve(matchingResource); + resourceWatcher.unwatchResources([resourceType], { onAvailable }); + } }; + resourceWatcher.watchResources([resourceType], { - ignoreExistingResources: true, + ignoreExistingResources, onAvailable, }); }); diff --git a/devtools/shared/resources/resource-watcher.js b/devtools/shared/resources/resource-watcher.js index becd862741b9..986140d818c5 100644 --- a/devtools/shared/resources/resource-watcher.js +++ b/devtools/shared/resources/resource-watcher.js @@ -171,22 +171,21 @@ class ResourceWatcher { * It will only listen for types which are defined by `TargetList.startListening`. */ async _watchAllTargets() { - if (this._isWatchingTargets) { - return; + if (!this._watchTargetsPromise) { + this._watchTargetsPromise = this.targetList.watchTargets( + this.targetList.ALL_TYPES, + this._onTargetAvailable, + this._onTargetDestroyed + ); } - this._isWatchingTargets = true; - await this.targetList.watchTargets( - this.targetList.ALL_TYPES, - this._onTargetAvailable, - this._onTargetDestroyed - ); + return this._watchTargetsPromise; } async _unwatchAllTargets() { - if (!this._isWatchingTargets) { + if (!this._watchTargetsPromise) { return; } - this._isWatchingTargets = false; + this._watchTargetsPromise = null; await this.targetList.unwatchTargets( this.targetList.ALL_TYPES, this._onTargetAvailable, diff --git a/devtools/shared/resources/tests/browser.ini b/devtools/shared/resources/tests/browser.ini index c6a28dbb5af1..116e95ef3b29 100644 --- a/devtools/shared/resources/tests/browser.ini +++ b/devtools/shared/resources/tests/browser.ini @@ -35,6 +35,7 @@ skip-if = os == "linux" #Bug 1655183 [browser_resources_several_resources.js] [browser_resources_stylesheets.js] [browser_resources_target_destroy.js] +[browser_resources_target_resources_race.js] [browser_resources_websocket.js] [browser_target_list_browser_workers.js] [browser_target_list_frames.js] diff --git a/devtools/shared/resources/tests/browser_resources_target_resources_race.js b/devtools/shared/resources/tests/browser_resources_target_resources_race.js new file mode 100644 index 000000000000..c593dd11831c --- /dev/null +++ b/devtools/shared/resources/tests/browser_resources_target_resources_race.js @@ -0,0 +1,77 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { + ResourceWatcher, +} = require("devtools/shared/resources/resource-watcher"); + +/** + * Test initial target resources are correctly retrieved even when several calls + * to watchResources are made simultaneously. + * + * This checks a race condition which occurred when calling watchResources + * simultaneously. This made the "second" call to watchResources miss existing + * resources (in case those are emitted from the target instead of the watcher). + * See Bug 1663896. + */ +add_task(async function() { + // Disable the preloaded process as it creates processes intermittently + // which forces the emission of RDP requests we aren't correctly waiting for. + await pushPref("dom.ipc.processPrelaunch.enabled", false); + + const { + client, + resourceWatcher, + targetList, + } = await initResourceWatcherAndTarget(); + + const expectedPlatformMessage = "expectedMessage"; + + info("Log a message *before* calling ResourceWatcher.watchResources"); + Services.console.logStringMessage(expectedPlatformMessage); + + info("Call watchResources from 2 separate call sites consecutively"); + + // Empty onAvailable callback for CSS MESSAGES, we only want to check that + // the second resource we watch correctly provides existing resources. + const onCssMessageAvailable = resources => {}; + + // First call to watchResources. + // We do not await on `watchPromise1` here, in order to simulate simultaneous + // calls to watchResources (which could come from 2 separate modules in a real + // scenario). + const initialWatchPromise = resourceWatcher.watchResources( + [ResourceWatcher.TYPES.CSS_MESSAGE], + { + onAvailable: onCssMessageAvailable, + } + ); + + // `waitForNextResource` will trigger another call to `watchResources`. + const onMessageReceived = waitForNextResource( + resourceWatcher, + ResourceWatcher.TYPES.PLATFORM_MESSAGE, + { + ignoreExistingResources: false, + predicate: r => r.message === expectedPlatformMessage, + } + ); + + info("Waiting for the expected message to be received"); + await onMessageReceived; + ok(true, "All the expected messages were received"); + + info("Wait for the other watchResources promise to finish"); + await initialWatchPromise; + + // Unwatch all resources. + resourceWatcher.unwatchResources([ResourceWatcher.TYPES.CSS_MESSAGE], { + onAvailable: onCssMessageAvailable, + }); + + Services.console.reset(); + targetList.destroy(); + await client.close(); +}); From d0215ae4231d4b9a13a01f9f6d7a3bc3aecd954f Mon Sep 17 00:00:00 2001 From: Julian Descottes Date: Wed, 9 Sep 2020 14:53:26 +0000 Subject: [PATCH 15/26] Bug 1663896 - Remove async for TargetList::unwatchTargets and update call sites r=ochameau Depends on D89583 Differential Revision: https://phabricator.services.mozilla.com/D89608 --- devtools/client/performance/panel.js | 2 +- devtools/shared/resources/resource-watcher.js | 4 ++-- devtools/shared/resources/target-list.js | 2 +- .../shared/resources/tests/browser_target_list_processes.js | 2 +- .../resources/tests/browser_target_list_watchTargets.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/devtools/client/performance/panel.js b/devtools/client/performance/panel.js index 425d14b57b19..28fef793f027 100644 --- a/devtools/client/performance/panel.js +++ b/devtools/client/performance/panel.js @@ -90,7 +90,7 @@ PerformancePanel.prototype = { this._checkRecordingStatus ); - await this.toolbox.targetList.unwatchTargets( + this.toolbox.targetList.unwatchTargets( [this.toolbox.targetList.TYPES.FRAME], this._onTargetAvailable ); diff --git a/devtools/shared/resources/resource-watcher.js b/devtools/shared/resources/resource-watcher.js index 986140d818c5..dc826482188d 100644 --- a/devtools/shared/resources/resource-watcher.js +++ b/devtools/shared/resources/resource-watcher.js @@ -181,12 +181,12 @@ class ResourceWatcher { return this._watchTargetsPromise; } - async _unwatchAllTargets() { + _unwatchAllTargets() { if (!this._watchTargetsPromise) { return; } this._watchTargetsPromise = null; - await this.targetList.unwatchTargets( + this.targetList.unwatchTargets( this.targetList.ALL_TYPES, this._onTargetAvailable, this._onTargetDestroyed diff --git a/devtools/shared/resources/target-list.js b/devtools/shared/resources/target-list.js index 88936878e487..17cb826ceb53 100644 --- a/devtools/shared/resources/target-list.js +++ b/devtools/shared/resources/target-list.js @@ -466,7 +466,7 @@ class TargetList extends EventEmitter { * Stop listening for the creation and/or destruction of a given type of target fronts. * See `watchTargets()` for documentation of the arguments. */ - async unwatchTargets(types, onAvailable, onDestroy) { + unwatchTargets(types, onAvailable, onDestroy) { if (typeof onAvailable != "function") { throw new Error( "TargetList.unwatchTargets expects a function as second argument" diff --git a/devtools/shared/resources/tests/browser_target_list_processes.js b/devtools/shared/resources/tests/browser_target_list_processes.js index 5ea6addb3ab7..f3614b399d86 100644 --- a/devtools/shared/resources/tests/browser_target_list_processes.js +++ b/devtools/shared/resources/tests/browser_target_list_processes.js @@ -179,7 +179,7 @@ async function testProcesses(targetList, target) { "The destroyed target is the one that has been reported as created" ); - await targetList.unwatchTargets( + targetList.unwatchTargets( [TargetList.TYPES.PROCESS], onAvailable, onDestroyed diff --git a/devtools/shared/resources/tests/browser_target_list_watchTargets.js b/devtools/shared/resources/tests/browser_target_list_watchTargets.js index d24c90b8be2b..d32cddf35079 100644 --- a/devtools/shared/resources/tests/browser_target_list_watchTargets.js +++ b/devtools/shared/resources/tests/browser_target_list_watchTargets.js @@ -163,7 +163,7 @@ async function testWatchTargets(mainRoot) { "The destroyed target is the one that has been reported as created" ); - await targetList.unwatchTargets( + targetList.unwatchTargets( [TargetList.TYPES.PROCESS], onAvailable, onDestroyed From a3c20c9800122f1a246761b796f643b0542da5e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Thu, 10 Sep 2020 08:58:09 +0000 Subject: [PATCH 16/26] Bug 1662703 - Make appearance: button behave like auto. r=heycam As per https://github.com/w3c/csswg-drafts/issues/5174. Differential Revision: https://phabricator.services.mozilla.com/D89119 --- .../server/actors/animation-type-longhand.js | 1 - layout/reftests/forms/select/1246836-ref.html | 27 ------------------- layout/reftests/forms/select/1246836.html | 27 ------------------- layout/reftests/forms/select/reftest.list | 1 - layout/style/ServoBindings.toml | 1 - layout/style/nsStyleStruct.cpp | 3 --- layout/style/nsStyleStruct.h | 10 +------ layout/style/res/forms.css | 6 ----- layout/style/test/ListCSSProperties.cpp | 1 - servo/components/style/properties/data.py | 1 - .../style/properties/longhands/box.mako.rs | 19 ------------- servo/components/style/values/computed/box.rs | 2 +- servo/components/style/values/computed/mod.rs | 2 +- .../components/style/values/specified/box.rs | 25 ----------------- .../components/style/values/specified/mod.rs | 2 +- servo/ports/geckolib/cbindgen.toml | 1 - .../appearance-button-002.tentative-ref.html | 4 --- .../appearance-button-002.tentative.html | 16 ----------- ...ebkit-appearance-button-002.tentative.html | 16 ----------- toolkit/themes/osx/reftests/482681.xhtml | 22 --------------- toolkit/themes/osx/reftests/reftest.list | 1 - 21 files changed, 4 insertions(+), 184 deletions(-) delete mode 100644 layout/reftests/forms/select/1246836-ref.html delete mode 100644 layout/reftests/forms/select/1246836.html delete mode 100644 testing/web-platform/tests/css/css-ui/appearance-button-002.tentative-ref.html delete mode 100644 testing/web-platform/tests/css/css-ui/appearance-button-002.tentative.html delete mode 100644 testing/web-platform/tests/css/css-ui/webkit-appearance-button-002.tentative.html delete mode 100644 toolkit/themes/osx/reftests/482681.xhtml diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js index 5966179331b8..26d580c126ed 100644 --- a/devtools/server/actors/animation-type-longhand.js +++ b/devtools/server/actors/animation-type-longhand.js @@ -36,7 +36,6 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [ "-moz-box-orient", "-moz-box-pack", "box-sizing", - "-moz-button-appearance", "caption-side", "clear", "clip-rule", diff --git a/layout/reftests/forms/select/1246836-ref.html b/layout/reftests/forms/select/1246836-ref.html deleted file mode 100644 index 0e97b27c13eb..000000000000 --- a/layout/reftests/forms/select/1246836-ref.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Reference for bug 1246836 - - - - - - - - diff --git a/layout/reftests/forms/select/1246836.html b/layout/reftests/forms/select/1246836.html deleted file mode 100644 index d6f034cbf196..000000000000 --- a/layout/reftests/forms/select/1246836.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Testcase for bug 1246836 - - - - - - - - diff --git a/layout/reftests/forms/select/reftest.list b/layout/reftests/forms/select/reftest.list index 575434257959..bb6b47bffdd2 100644 --- a/layout/reftests/forms/select/reftest.list +++ b/layout/reftests/forms/select/reftest.list @@ -12,7 +12,6 @@ needs-focus == focusring-3.html focusring-3-ref.html == dynamic-text-indent-1.html dynamic-text-indent-1-ref.html == dynamic-text-overflow-1.html dynamic-text-overflow-1-ref.html == listbox-zero-row-initial.html listbox-zero-row-initial-ref.html -== 1246836.html 1246836-ref.html skip-if(Android) == select-option-display-none-inline-size.html select-option-display-none-inline-size-ref.html # Android and Windows actually use the anonymous select > button (rather than diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 46202d1b81cd..036de45d5004 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -580,7 +580,6 @@ cbindgen-types = [ { gecko = "StyleSVGLength", servo = "crate::values::computed::svg::SVGLength" }, { gecko = "StyleFontSizeKeyword", servo = "crate::values::specified::font::FontSizeKeyword" }, { gecko = "StyleDefaultFontSizes", servo = "crate::gecko::wrapper::DefaultFontSizes" }, - { gecko = "StyleButtonAppearance", servo = "crate::values::specified::ButtonAppearance" }, ] mapped-generic-types = [ diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index b3f454be4949..7941a4ba2687 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2165,7 +2165,6 @@ nsStyleDisplay::nsStyleDisplay(const Document& aDocument) mContain(StyleContain::NONE), mAppearance(StyleAppearance::None), mDefaultAppearance(StyleAppearance::None), - mButtonAppearance(StyleButtonAppearance::Allow), mPosition(StylePositionProperty::Static), mFloat(StyleFloat::None), mBreakType(StyleClear::None), @@ -2236,7 +2235,6 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) mContain(aSource.mContain), mAppearance(aSource.mAppearance), mDefaultAppearance(aSource.mDefaultAppearance), - mButtonAppearance(aSource.mButtonAppearance), mPosition(aSource.mPosition), mFloat(aSource.mFloat), mBreakType(aSource.mBreakType), @@ -2504,7 +2502,6 @@ nsChangeHint nsStyleDisplay::CalcDifference( mBreakAfter != aNewData.mBreakAfter || mAppearance != aNewData.mAppearance || mDefaultAppearance != aNewData.mDefaultAppearance || - mButtonAppearance != aNewData.mButtonAppearance || mOrient != aNewData.mOrient || mOverflowClipBoxBlock != aNewData.mOverflowClipBoxBlock || mOverflowClipBoxInline != aNewData.mOverflowClipBoxInline) { diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 05f5d93aa45c..909c13fb47e2 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1224,7 +1224,6 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { public: mozilla::StyleAppearance mDefaultAppearance; - mozilla::StyleButtonAppearance mButtonAppearance; mozilla::StylePositionProperty mPosition; mozilla::StyleFloat mFloat; @@ -1334,6 +1333,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { mozilla::StyleAppearance EffectiveAppearance() const { switch (mAppearance) { case mozilla::StyleAppearance::Auto: + case mozilla::StyleAppearance::Button: case mozilla::StyleAppearance::Searchfield: case mozilla::StyleAppearance::Textarea: case mozilla::StyleAppearance::Checkbox: @@ -1366,14 +1366,6 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { // difference between menulist and menulist-button handling, we don't // bother. return mDefaultAppearance; - case mozilla::StyleAppearance::Button: - // `appearance: button` should behave like `auto` for a specific list - // of widget elements, and we encode that using the internal - // -moz-button-appearance property. - if (mButtonAppearance == mozilla::StyleButtonAppearance::Disallow) { - return mDefaultAppearance; - } - return mAppearance; default: return mAppearance; } diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index 234ec47d07e8..1ea6d0a956d8 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -86,7 +86,6 @@ input { display: inline-block; appearance: auto; -moz-default-appearance: textfield; - -moz-button-appearance: disallow; /* The sum of border and padding on block-start and block-end must be the same here, for buttons, and for diff --git a/testing/web-platform/tests/css/css-ui/appearance-button-002.tentative.html b/testing/web-platform/tests/css/css-ui/appearance-button-002.tentative.html deleted file mode 100644 index 5c9e9a280aca..000000000000 --- a/testing/web-platform/tests/css/css-ui/appearance-button-002.tentative.html +++ /dev/null @@ -1,16 +0,0 @@ - - -CSS Basic User Interface Test: appearance: button - - - - - - diff --git a/testing/web-platform/tests/css/css-ui/webkit-appearance-button-002.tentative.html b/testing/web-platform/tests/css/css-ui/webkit-appearance-button-002.tentative.html deleted file mode 100644 index 4c322fa20e4a..000000000000 --- a/testing/web-platform/tests/css/css-ui/webkit-appearance-button-002.tentative.html +++ /dev/null @@ -1,16 +0,0 @@ - - -CSS Basic User Interface Test: -webkit-appearance: button - - - - - - diff --git a/toolkit/themes/osx/reftests/482681.xhtml b/toolkit/themes/osx/reftests/482681.xhtml deleted file mode 100644 index 6cb9aaeae418..000000000000 --- a/toolkit/themes/osx/reftests/482681.xhtml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - -